|
|
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"
! 14: #include "sjis.h"
! 15:
1.1 root 16: // イベントテーブル
17: wxBEGIN_EVENT_TABLE(WXLogMonitor, inherited)
1.1.1.4 root 18: EVT_SIZE(WXLogMonitor::OnSize)
1.1 root 19: wxEND_EVENT_TABLE()
20:
21: // コンストラクタ
1.1.1.7 root 22: WXLogMonitor::WXLogMonitor(wxWindow *parent)
23: : inherited(parent, wxID_ANY, _("Log"))
1.1 root 24: {
25: col = 80;
26: row = 40;
27:
1.1.1.7 root 28: monitor.obj = &log;
1.1.1.9 ! root 29: monitor.func = ToMonitorCallback(&LogMonitor::MonitorUpdate);
1.1.1.7 root 30: monitor.SetSize(col, row);
31: // プライベート利用なので Regist() は不要
32:
33: log.Init(this);
1.1.1.4 root 34:
35: // →
1.1.1.9 ! root 36: // +------------+---------+
! 37: // | TextScreen | VScroll |
! 38: // +------------+---------+
1.1.1.4 root 39:
1.1.1.8 root 40: wxBoxSizer *topsizer = new wxBoxSizer(wxHORIZONTAL);
1.1.1.9 ! root 41:
! 42: // テキストスクリーン
1.1.1.7 root 43: screen = new WXTextScreen(this, monitor);
1.1.1.9 ! root 44: topsizer->Add(screen, 1, wxEXPAND);
1.1.1.4 root 45:
46: // スクロールバー
1.1.1.9 ! root 47: vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL);
1.1.1.4 root 48: topsizer->Add(vscroll, 0, wxEXPAND);
1.1.1.3 root 49:
50: SetSizer(topsizer);
1.1.1.4 root 51: // ウィンドウサイズを確定させる
52: DoSize();
53:
54: // スクロールのために (パネル領域での) MouseWheel イベントもここで
1.1.1.9 ! root 55: // 受け取りたい。スクロールバー上のマウスホイール操作はスクロールバーが
! 56: // 処理しているので問題ないが、パネル領域でのマウスホイール操作はここ
! 57: // (wxFrame)ではなくパネルに対して飛んでくる。
1.1.1.4 root 58: // ここで一緒に処理したほうが楽なので、こちらに回す。
59: screen->Connect(wxEVT_MOUSEWHEEL,
60: wxMouseEventHandler(WXLogMonitor::OnMouseWheel), NULL, this);
1.1.1.9 ! root 61:
! 62: // スクロールバーからの位置変更通知
! 63: vscroll->Connect(NONO_EVT_SCROLL,
! 64: wxScrollEventHandler(WXLogMonitor::OnScroll), NULL, this);
! 65:
! 66: // XXX ログウィンドウのコピーはまだ動いてない
! 67: screen->DisconnectContextMenu();
1.1 root 68: }
69:
70: // デストラクタ
71: WXLogMonitor::~WXLogMonitor()
72: {
73: }
74:
1.1.1.4 root 75: // サイズ変更イベント
76: void
77: WXLogMonitor::OnSize(wxSizeEvent& event)
78: {
79: inherited::OnSize(event);
80:
81: // スクロールバーを再設定
82: SetScroll();
83: }
84:
85: // スクロールバーを再設定
86: void
87: WXLogMonitor::SetScroll()
88: {
89: int pos;
90: int thumbsize;
91: int range;
92: int pagesize;
93:
1.1.1.7 root 94: row = screen->GetRow();
1.1.1.4 root 95:
96: if (lines <= row) {
97: pos = 0;
98: range = row;
99: } else {
100: pos = (lines - row) - vpos;
101: range = lines;
102: }
103: thumbsize = row;
104: pagesize = thumbsize - 1;
1.1.1.9 ! root 105: vscroll->SetScrollParam(pos, thumbsize, range, pagesize);
1.1.1.4 root 106: }
107:
108: // マウスホイールイベント (WXTextScreen 上で起きたやつをこっちに回してある)
1.1 root 109: void
1.1.1.4 root 110: WXLogMonitor::OnMouseWheel(wxMouseEvent& event)
111: {
112: // GetWheelRotation() は上(奥)向きに回すと +120、下(手前)に回すと -120。
113: // どこの決まりか知らんけど、スクロールバーのほうはホイール1段階で3行
114: // スクロールするのでそれに合わせる。
115: int pos = vscroll->GetThumbPosition();
116: pos = pos - event.GetWheelRotation() / 40;
117:
118: int maxpos = vscroll->GetRange() - vscroll->GetThumbSize();
119: if (pos < 0)
120: pos = 0;
121: if (pos > maxpos)
122: pos = maxpos;
123:
124: DoScroll(pos);
1.1.1.9 ! root 125: // スクロールバーの位置を追従
! 126: vscroll->SetThumbPosition(pos);
1.1.1.4 root 127: }
128:
1.1.1.9 ! root 129: // スクロールバーからの通知イベント
1.1.1.4 root 130: void
131: WXLogMonitor::OnScroll(wxScrollEvent& event)
132: {
133: DoScroll(event.GetPosition());
134: }
135:
136: // スクロール処理 (OnScroll() と OnMouseWheel() から呼ばれる)
137: void
138: WXLogMonitor::DoScroll(int pos)
139: {
140: // pos 即ちスクロールバーの Thumb の位置は上端の位置。
141: // 対してログウィンドウでは下端を基準にしたほうが処理しやすい。
142: // vpos = 0 だと Thumb が下に接していてパネルの最下行が最新行、
143: // vpos = 1 だと Thumb が下から1段階分上にありパネルの最下行は最新行の
144: // 一つ前の行、となる。
145: //
146: // Range=6 Range=6
147: // Pos=4 Pos=0
148: // ThumbSz=2 ThumbSz=2
149: // vpos=0 vpos=4
150: // △ △
151: // □ ■
152: // □ ■
153: // □ □
154: // □ □
155: // ■ □
156: // ■ □
157: // ▽ ▽
158:
159: // パネル最下行に表示する行の最新行からのオフセット
160: // (0 なら Thumb が下端に接している)
161: vpos = vscroll->GetRange() - vscroll->GetThumbSize() - pos;
162: }
163:
1.1.1.9 ! root 164:
1.1.1.7 root 165: //
166: // モニターオブジェクト
167: //
168:
169: // コンストラクタ
170: LogMonitor::LogMonitor()
171: : inherited("LogMonitor")
1.1.1.4 root 172: {
1.1.1.7 root 173: }
174:
175: // デストラクタ
176: LogMonitor::~LogMonitor()
177: {
178: }
179:
180: void
181: LogMonitor::Init(WXLogMonitor *parent_)
182: {
183: parent = parent_;
184:
1.1.1.9 ! root 185: logger = gMainApp.GetLogger();
! 186:
1.1.1.7 root 187: // バックログは今の所固定長
188: logbuf.Init(parent->col, backlog);
189: logbuf.Mode = TextScreen::Ring;
1.1.1.4 root 190: }
191:
192: // モニターテキストを更新
193: void
1.1.1.7 root 194: LogMonitor::MonitorUpdate(Monitor *, TextScreen& screen)
1.1 root 195: {
196: char buf[1024];
197:
1.1.1.4 root 198: // どこでどうするかはあるけど、
199: // とりあえず row は常に現在の TextScreen の高さということにしておく
1.1.1.7 root 200: parent->row = screen.GetRow();
1.1.1.4 root 201:
1.1.1.6 root 202: // XXX: 生成時にやるべき
1.1.1.7 root 203: screen.Mode = TextScreen::Ring;
1.1.1.6 root 204:
1.1.1.4 root 205: // キューから読めるだけバックログに書き込む。
206: // 読めるだけと言っても while ループで読めるだけ読んでしまうと、
207: // 流速が早すぎた場合にこのループから抜けるタイミングがなくなる可能性が
208: // (可能性としては) あるので、適当に1画面分くらいで打ち切る。
209: // 取り込み残した分は次回取り込まれるし、その流速も上回るようだと
1.1.1.9 ! root 210: // どのみち Logger の固定長キューで落ちる。
1.1.1.7 root 211: for (int i = 0; i < parent->row; i++) {
1.1.1.9 ! root 212: if (logger->Read(buf, sizeof(buf)) == false)
1.1.1.4 root 213: break;
1.1 root 214: Append(buf);
215: }
216:
1.1.1.7 root 217: screen.Clear();
1.1.1.4 root 218:
219: // スクロールバー(vpos)による表示位置計算
1.1.1.6 root 220: // vpos は行数、v は logbuf の Y 座標
1.1.1.7 root 221: int v = cursor - parent->vpos;
1.1.1.4 root 222: if (v < 0) {
1.1.1.6 root 223: v += backlog;
1.1 root 224: }
225:
1.1.1.4 root 226: // 表示位置(v)の Row 行分手前から順に表示
1.1.1.7 root 227: int srcY = v - parent->row;
1.1.1.6 root 228: if (srcY < 0) {
229: srcY += backlog;
230: }
231:
232: // 画面コピー
233: logbuf.Locate(0, srcY);
1.1.1.7 root 234: for (int i = 0; i < parent->col * parent->row; i++) {
235: screen.Putc(logbuf.Getc());
1.1.1.4 root 236: }
237: }
1.1 root 238:
1.1.1.4 root 239: // 1行追加された時の共通処理
240: void
1.1.1.7 root 241: LogMonitor::IncLines()
1.1.1.4 root 242: {
243: // スクロールバーが下端にない時(バックログを見ている時)は
244: // ログが追加されても現在位置をキープする。
1.1.1.7 root 245: if (parent->vpos > 0) {
1.1.1.4 root 246: // 上端に達したら増加させない
1.1.1.7 root 247: if (parent->vpos < backlog - parent->vscroll->GetThumbSize()) {
248: parent->vpos++;
1.1 root 249: }
250: }
251:
1.1.1.4 root 252: // 有効行数を増やす
1.1.1.7 root 253: if (parent->lines < backlog) {
254: parent->lines++;
1.1 root 255:
1.1.1.4 root 256: // 行数が変わったのでスクロールバーを更新
1.1.1.7 root 257: parent->SetScroll();
1.1.1.4 root 258: }
1.1 root 259: }
260:
1.1.1.4 root 261: // バックログに行を追加。
1.1 root 262: //
1.1.1.4 root 263: // 引数の utfbuf は UTF-8 で改行コードのない1行ずつ。
264: // これをバックログの最新位置に書き込む。古い行は順次上書きされる。
1.1 root 265: //
1.1.1.4 root 266: // バックログは col * backlog バイトの Shift_JIS バッファ。
267: // 属性は持たず文字コードのみ。漢字は2バイト幅をとる。
268: // 横幅 col が固定なので書き込む時点で行数は表示用のそれと一致する。
1.1 root 269: //
1.1.1.4 root 270: // cursor はこれから書き込む位置の行頭。cursor は必ず行頭だけをポイント
271: // しながら移動する。
272: //
273: // logbuf +------------------------
274: // | :
275: // | 前回書き込んだ行
276: // cursor -> | 今から書きこむ行 (保存されていた一番古い行)
277: // :
278: // |
279: // +------------------------
1.1 root 280: void
1.1.1.7 root 281: LogMonitor::Append(const char *utfbuf)
1.1 root 282: {
1.1.1.4 root 283: const char *s;
284: int x;
1.1 root 285:
286: // 文字コードの変換
287: wxString utfstr(utfbuf, wxConvUTF8);
1.1.1.9 ! root 288: // mb_str() をキャストせずに一度保持しておく必要がある (scan-build 対策)
! 289: auto sjis = utfstr.mb_str(conv);
1.1 root 290:
1.1.1.6 root 291: logbuf.Locate(0, cursor);
292:
1.1.1.9 ! root 293: for (s = (const char *)sjis; *s; ) {
1.1.1.4 root 294: // 最終桁で2バイト文字の1バイト目が来たら
295: // 代わりに1文字空白を入れる (それによってこの後改行が起きる)
1.1.1.6 root 296: if (SJIS::IsZenkaku(*s)) {
297: // 漢字ならば 2 バイトセットで扱う
1.1.1.7 root 298: if (logbuf.GetX() >= parent->col - 1) {
1.1.1.6 root 299: // 最終桁には出せないので空白で次の行に送る
300: logbuf.Putc(' ');
301: IncLines();
302: }
303: logbuf.Putc(*s++);
304: // 万が一に備える
305: if (__predict_true(*s)) {
306: logbuf.Putc(*s++);
307: }
1.1.1.4 root 308: } else {
309: // そうでなければそのまま出力
1.1.1.6 root 310: logbuf.Putc(*s++);
1.1 root 311: }
1.1.1.4 root 312: }
1.1 root 313:
1.1.1.6 root 314: // 行の終わりまでは空白でクリアする
315: x = logbuf.GetX();
1.1.1.4 root 316: if (x > 0) {
1.1.1.6 root 317: for (; x < logbuf.GetCol(); x++) {
318: logbuf.Putc(' ');
319: }
1.1.1.4 root 320: IncLines();
1.1 root 321: }
1.1.1.6 root 322: // 次回書き込みの Y 座標
323: cursor = logbuf.GetY();
1.1 root 324: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.