Annotation of nono/wx/wxlogmonitor.cpp, revision 1.1.1.7

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: 
                     46:        topsizer = new wxBoxSizer(wxHORIZONTAL);
                     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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.