|
|
1.1 root 1: //
2: // nono
1.1.1.3 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: //
8: // ログモニター
9: //
10:
1.1.1.9 root 11: #include "wxlogmonitor.h"
12: #include "logger.h"
13: #include "mainapp.h"
1.1.1.12 root 14: #include "monitor.h"
1.1.1.9 root 15: #include "sjis.h"
16:
1.1.1.13! root 17: //#define WINDOW_DEBUG 1
! 18:
! 19: #if defined(WINDOW_DEBUG)
! 20: #define DPRINTF(fmt...) printf(fmt)
! 21: #else
! 22: #define DPRINTF(fmt...) /**/
! 23: #endif
1.1 root 24:
25: // コンストラクタ
1.1.1.7 root 26: WXLogMonitor::WXLogMonitor(wxWindow *parent)
1.1.1.10 root 27: : inherited(parent, wxID_ANY, _("Log"), DEFAULT_STYLE | wxRESIZE_BORDER)
1.1 root 28: {
1.1.1.13! root 29: DPRINTF("%s begin\n", __method__);
1.1 root 30: col = 80;
31: row = 40;
32:
1.1.1.12 root 33: // プライベート利用なので Regist() しない。
34: monitor.reset(new Monitor(-1, &log));
35: monitor->func = ToMonitorCallback(&LogMonitor::MonitorUpdate);
36: monitor->SetSize(col, row);
1.1.1.7 root 37:
38: log.Init(this);
1.1.1.4 root 39:
1.1.1.9 root 40: // +------------+---------+
41: // | TextScreen | VScroll |
42: // +------------+---------+
1.1.1.4 root 43:
1.1.1.9 root 44: // テキストスクリーン
1.1.1.12 root 45: screen = new WXMonitorPanel(this, monitor.get());
1.1.1.4 root 46:
47: // スクロールバー
1.1.1.9 root 48: vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL);
1.1.1.3 root 49:
1.1.1.13! root 50: // Sizer 使わず自前でレイアウトする。
! 51: SetAutoLayout(true);
! 52:
1.1.1.4 root 53: // ウィンドウサイズを確定させる
1.1.1.13! root 54: FontChanged();
1.1.1.4 root 55:
56: // スクロールのために (パネル領域での) MouseWheel イベントもここで
1.1.1.9 root 57: // 受け取りたい。スクロールバー上のマウスホイール操作はスクロールバーが
58: // 処理しているので問題ないが、パネル領域でのマウスホイール操作はここ
59: // (wxFrame)ではなくパネルに対して飛んでくる。
1.1.1.4 root 60: // ここで一緒に処理したほうが楽なので、こちらに回す。
61: screen->Connect(wxEVT_MOUSEWHEEL,
62: wxMouseEventHandler(WXLogMonitor::OnMouseWheel), NULL, this);
1.1.1.9 root 63:
1.1.1.13! root 64: // スクロールバーからの位置変更通知イベントは表示パネルへ回す。
1.1.1.9 root 65: vscroll->Connect(NONO_EVT_SCROLL,
66: wxScrollEventHandler(WXLogMonitor::OnScroll), NULL, this);
67:
68: // XXX ログウィンドウのコピーはまだ動いてない
69: screen->DisconnectContextMenu();
1.1.1.13! root 70: DPRINTF("%s done\n", __method__);
1.1 root 71: }
72:
73: // デストラクタ
74: WXLogMonitor::~WXLogMonitor()
75: {
76: }
77:
1.1.1.13! root 78: void
! 79: WXLogMonitor::FontChanged()
1.1.1.10 root 80: {
1.1.1.13! root 81: screen->FontChanged();
! 82: vscroll->FontChanged();
! 83: DPRINTF("%s %d\n", __method__, screen->GetFontHeight());
! 84:
! 85: Fit();
1.1.1.10 root 86: }
87:
1.1.1.4 root 88: void
1.1.1.13! root 89: WXLogMonitor::Fit()
! 90: {
! 91: // inherited::Fit() は呼ばずに自力で行う。
! 92:
! 93: wxSize newsize = DoSizeHints();
! 94: #if defined(WINDOW_DEBUG)
! 95: wxSize newwin = ClientToWindowSize(newsize);
! 96: printf("%s (%d,%d) client=(%d,%d)\n", __method__,
! 97: newwin.x, newwin.y, newsize.x, newsize.y);
! 98: #endif
! 99: SetClientSize(newsize);
! 100: }
! 101:
! 102: bool
! 103: WXLogMonitor::Layout()
1.1.1.4 root 104: {
1.1.1.13! root 105: // inherited::Layout() は(実質何もしないので)、呼ばずに自力で行う。
! 106:
! 107: wxSize client = GetClientSize();
! 108: const wxSize win = GetSize();
! 109: const wxSize margin = win - client;
! 110: const wxSize vsize = vscroll->GetSize();
! 111: const wxSize monsize = screen->GetSize();
! 112:
! 113: // wxmonitor.cpp の Layout() 内のコメント参照。
! 114: if (oldmargin != margin) {
! 115: layout_pass2 = true;
! 116: oldmargin = margin;
! 117: CallAfter([this]() {
! 118: DoSizeHints();
! 119: });
! 120: return true;
! 121: } else if (layout_pass2) {
! 122: layout_pass2 = false;
! 123: client.y = monsize.y;
! 124: SetClientSize(client);
! 125: Hide();
! 126: Show();
! 127: return true;
! 128: }
! 129:
! 130: // コントロールを配置。
! 131: int panel_width = client.x - vsize.x;
! 132: int panel_height = client.y;
! 133: // 最低でも 1px ないと GTK とかに怒られる。
! 134: if (panel_width < 1) {
! 135: panel_width = 1;
! 136: }
! 137: if (panel_height < 1) {
! 138: panel_height = 1;
! 139: }
! 140:
! 141: screen->SetSize(0, 0, panel_width, panel_height);
! 142: vscroll->SetSize(panel_width, 0, vsize.x, panel_height);
1.1.1.4 root 143:
144: // スクロールバーを再設定
145: SetScroll();
1.1.1.13! root 146:
! 147: DPRINTF("%s done\n", __method__);
! 148:
! 149: return true;
! 150: }
! 151:
! 152: // クライアントサイズと最大/最小サイズを計算する。
! 153: // SetSizeHints() を実行し、設定すべきクライアントサイズを返す。
! 154: wxSize
! 155: WXLogMonitor::DoSizeHints()
! 156: {
! 157: auto ssize = screen->GetSize();
! 158: auto vsize = vscroll->GetSize();
! 159: DPRINTF("%s screen=(%d,%d) vscroll=(%d,%d)\n", __method__,
! 160: ssize.x, ssize.y, vsize.x, vsize.y);
! 161:
! 162: int x = ssize.x + vsize.x;
! 163: int min_y = std::max(vscroll->GetMinSize().y, screen->GetScreenHeight(1));
! 164: int new_y = std::max(ssize.y, min_y);
! 165: wxSize minsize(x, min_y);
! 166: wxSize maxsize(x, GetMaxClientSize().y);
! 167: wxSize incsize(0, screen->GetFontHeight());
! 168: SetSizeHints(ClientToWindowSize(minsize), ClientToWindowSize(maxsize),
! 169: incsize);
! 170:
! 171: return wxSize(x, new_y);
1.1.1.4 root 172: }
173:
174: // スクロールバーを再設定
175: void
176: WXLogMonitor::SetScroll()
177: {
178: int pos;
179: int thumbsize;
180: int range;
181: int pagesize;
182:
1.1.1.7 root 183: row = screen->GetRow();
1.1.1.4 root 184:
185: if (lines <= row) {
186: pos = 0;
187: range = row;
188: } else {
189: pos = (lines - row) - vpos;
190: range = lines;
191: }
192: thumbsize = row;
193: pagesize = thumbsize - 1;
1.1.1.9 root 194: vscroll->SetScrollParam(pos, thumbsize, range, pagesize);
1.1.1.4 root 195: }
196:
197: // マウスホイールイベント (WXTextScreen 上で起きたやつをこっちに回してある)
1.1 root 198: void
1.1.1.4 root 199: WXLogMonitor::OnMouseWheel(wxMouseEvent& event)
200: {
201: // GetWheelRotation() は上(奥)向きに回すと +120、下(手前)に回すと -120。
202: // どこの決まりか知らんけど、スクロールバーのほうはホイール1段階で3行
203: // スクロールするのでそれに合わせる。
204: int pos = vscroll->GetThumbPosition();
205: pos = pos - event.GetWheelRotation() / 40;
206:
207: int maxpos = vscroll->GetRange() - vscroll->GetThumbSize();
208: if (pos < 0)
209: pos = 0;
210: if (pos > maxpos)
211: pos = maxpos;
212:
213: DoScroll(pos);
1.1.1.9 root 214: // スクロールバーの位置を追従
215: vscroll->SetThumbPosition(pos);
1.1.1.4 root 216: }
217:
1.1.1.9 root 218: // スクロールバーからの通知イベント
1.1.1.4 root 219: void
220: WXLogMonitor::OnScroll(wxScrollEvent& event)
221: {
222: DoScroll(event.GetPosition());
223: }
224:
225: // スクロール処理 (OnScroll() と OnMouseWheel() から呼ばれる)
226: void
227: WXLogMonitor::DoScroll(int pos)
228: {
229: // pos 即ちスクロールバーの Thumb の位置は上端の位置。
230: // 対してログウィンドウでは下端を基準にしたほうが処理しやすい。
231: // vpos = 0 だと Thumb が下に接していてパネルの最下行が最新行、
232: // vpos = 1 だと Thumb が下から1段階分上にありパネルの最下行は最新行の
233: // 一つ前の行、となる。
234: //
235: // Range=6 Range=6
236: // Pos=4 Pos=0
237: // ThumbSz=2 ThumbSz=2
238: // vpos=0 vpos=4
239: // △ △
240: // □ ■
241: // □ ■
242: // □ □
243: // □ □
244: // ■ □
245: // ■ □
246: // ▽ ▽
247:
248: // パネル最下行に表示する行の最新行からのオフセット
249: // (0 なら Thumb が下端に接している)
250: vpos = vscroll->GetRange() - vscroll->GetThumbSize() - pos;
251: }
252:
1.1.1.9 root 253:
1.1.1.7 root 254: //
255: // モニターオブジェクト
256: //
257:
258: // コンストラクタ
259: LogMonitor::LogMonitor()
1.1.1.11 root 260: : inherited(OBJ_LOGMONITOR)
1.1.1.4 root 261: {
1.1.1.7 root 262: }
263:
264: // デストラクタ
265: LogMonitor::~LogMonitor()
266: {
267: }
268:
269: void
270: LogMonitor::Init(WXLogMonitor *parent_)
271: {
272: parent = parent_;
273:
1.1.1.9 root 274: logger = gMainApp.GetLogger();
275:
1.1.1.7 root 276: // バックログは今の所固定長
277: logbuf.Init(parent->col, backlog);
278: logbuf.Mode = TextScreen::Ring;
1.1.1.4 root 279: }
280:
281: // モニターテキストを更新
282: void
1.1.1.7 root 283: LogMonitor::MonitorUpdate(Monitor *, TextScreen& screen)
1.1 root 284: {
285: char buf[1024];
286:
1.1.1.4 root 287: // どこでどうするかはあるけど、
288: // とりあえず row は常に現在の TextScreen の高さということにしておく
1.1.1.7 root 289: parent->row = screen.GetRow();
1.1.1.4 root 290:
1.1.1.6 root 291: // XXX: 生成時にやるべき
1.1.1.7 root 292: screen.Mode = TextScreen::Ring;
1.1.1.6 root 293:
1.1.1.4 root 294: // キューから読めるだけバックログに書き込む。
295: // 読めるだけと言っても while ループで読めるだけ読んでしまうと、
296: // 流速が早すぎた場合にこのループから抜けるタイミングがなくなる可能性が
297: // (可能性としては) あるので、適当に1画面分くらいで打ち切る。
298: // 取り込み残した分は次回取り込まれるし、その流速も上回るようだと
1.1.1.9 root 299: // どのみち Logger の固定長キューで落ちる。
1.1.1.7 root 300: for (int i = 0; i < parent->row; i++) {
1.1.1.9 root 301: if (logger->Read(buf, sizeof(buf)) == false)
1.1.1.4 root 302: break;
1.1 root 303: Append(buf);
304: }
305:
1.1.1.7 root 306: screen.Clear();
1.1.1.4 root 307:
308: // スクロールバー(vpos)による表示位置計算
1.1.1.6 root 309: // vpos は行数、v は logbuf の Y 座標
1.1.1.7 root 310: int v = cursor - parent->vpos;
1.1.1.4 root 311: if (v < 0) {
1.1.1.6 root 312: v += backlog;
1.1 root 313: }
314:
1.1.1.4 root 315: // 表示位置(v)の Row 行分手前から順に表示
1.1.1.7 root 316: int srcY = v - parent->row;
1.1.1.6 root 317: if (srcY < 0) {
318: srcY += backlog;
319: }
320:
321: // 画面コピー
322: logbuf.Locate(0, srcY);
1.1.1.7 root 323: for (int i = 0; i < parent->col * parent->row; i++) {
1.1.1.12 root 324: uint16 ch = logbuf.Getc();
325: logbuf.Ascend();
326: screen.Putc(ch);
1.1.1.4 root 327: }
328: }
1.1 root 329:
1.1.1.4 root 330: // 1行追加された時の共通処理
331: void
1.1.1.7 root 332: LogMonitor::IncLines()
1.1.1.4 root 333: {
334: // スクロールバーが下端にない時(バックログを見ている時)は
335: // ログが追加されても現在位置をキープする。
1.1.1.7 root 336: if (parent->vpos > 0) {
1.1.1.4 root 337: // 上端に達したら増加させない
1.1.1.7 root 338: if (parent->vpos < backlog - parent->vscroll->GetThumbSize()) {
339: parent->vpos++;
1.1 root 340: }
341: }
342:
1.1.1.4 root 343: // 有効行数を増やす
1.1.1.7 root 344: if (parent->lines < backlog) {
345: parent->lines++;
1.1 root 346:
1.1.1.4 root 347: // 行数が変わったのでスクロールバーを更新
1.1.1.7 root 348: parent->SetScroll();
1.1.1.4 root 349: }
1.1 root 350: }
351:
1.1.1.4 root 352: // バックログに行を追加。
1.1 root 353: //
1.1.1.4 root 354: // 引数の utfbuf は UTF-8 で改行コードのない1行ずつ。
355: // これをバックログの最新位置に書き込む。古い行は順次上書きされる。
1.1 root 356: //
1.1.1.4 root 357: // バックログは col * backlog バイトの Shift_JIS バッファ。
358: // 属性は持たず文字コードのみ。漢字は2バイト幅をとる。
359: // 横幅 col が固定なので書き込む時点で行数は表示用のそれと一致する。
1.1 root 360: //
1.1.1.4 root 361: // cursor はこれから書き込む位置の行頭。cursor は必ず行頭だけをポイント
362: // しながら移動する。
363: //
364: // logbuf +------------------------
365: // | :
366: // | 前回書き込んだ行
367: // cursor -> | 今から書きこむ行 (保存されていた一番古い行)
368: // :
369: // |
370: // +------------------------
1.1 root 371: void
1.1.1.7 root 372: LogMonitor::Append(const char *utfbuf)
1.1 root 373: {
1.1.1.4 root 374: const char *s;
375: int x;
1.1 root 376:
377: // 文字コードの変換
378: wxString utfstr(utfbuf, wxConvUTF8);
1.1.1.9 root 379: // mb_str() をキャストせずに一度保持しておく必要がある (scan-build 対策)
380: auto sjis = utfstr.mb_str(conv);
1.1 root 381:
1.1.1.6 root 382: logbuf.Locate(0, cursor);
383:
1.1.1.9 root 384: for (s = (const char *)sjis; *s; ) {
1.1.1.4 root 385: // 最終桁で2バイト文字の1バイト目が来たら
386: // 代わりに1文字空白を入れる (それによってこの後改行が起きる)
1.1.1.6 root 387: if (SJIS::IsZenkaku(*s)) {
388: // 漢字ならば 2 バイトセットで扱う
1.1.1.7 root 389: if (logbuf.GetX() >= parent->col - 1) {
1.1.1.6 root 390: // 最終桁には出せないので空白で次の行に送る
391: logbuf.Putc(' ');
392: IncLines();
393: }
394: logbuf.Putc(*s++);
395: // 万が一に備える
396: if (__predict_true(*s)) {
397: logbuf.Putc(*s++);
398: }
1.1.1.4 root 399: } else {
400: // そうでなければそのまま出力
1.1.1.6 root 401: logbuf.Putc(*s++);
1.1 root 402: }
1.1.1.4 root 403: }
1.1 root 404:
1.1.1.6 root 405: // 行の終わりまでは空白でクリアする
406: x = logbuf.GetX();
1.1.1.4 root 407: if (x > 0) {
1.1.1.6 root 408: for (; x < logbuf.GetCol(); x++) {
409: logbuf.Putc(' ');
410: }
1.1.1.4 root 411: IncLines();
1.1 root 412: }
1.1.1.6 root 413: // 次回書き込みの Y 座標
414: cursor = logbuf.GetY();
1.1 root 415: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.