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