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