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