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

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: }

unix.superglobalmegacorp.com

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