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