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