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

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.1.13! root       17: //#define WINDOW_DEBUG 1
        !            18: 
        !            19: #if defined(WINDOW_DEBUG)
        !            20: #define DPRINTF(fmt...) printf(fmt)
        !            21: #else
        !            22: #define DPRINTF(fmt...) /**/
        !            23: #endif
1.1       root       24: 
                     25: // コンストラクタ
1.1.1.7   root       26: WXLogMonitor::WXLogMonitor(wxWindow *parent)
1.1.1.10  root       27:        : inherited(parent, wxID_ANY, _("Log"), DEFAULT_STYLE | wxRESIZE_BORDER)
1.1       root       28: {
1.1.1.13! root       29:        DPRINTF("%s begin\n", __method__);
1.1       root       30:        col = 80;
                     31:        row = 40;
                     32: 
1.1.1.12  root       33:        // プライベート利用なので Regist() しない。
                     34:        monitor.reset(new Monitor(-1, &log));
                     35:        monitor->func = ToMonitorCallback(&LogMonitor::MonitorUpdate);
                     36:        monitor->SetSize(col, row);
1.1.1.7   root       37: 
                     38:        log.Init(this);
1.1.1.4   root       39: 
1.1.1.9   root       40:        // +------------+---------+
                     41:        // | TextScreen | VScroll |
                     42:        // +------------+---------+
1.1.1.4   root       43: 
1.1.1.9   root       44:        // テキストスクリーン
1.1.1.12  root       45:        screen = new WXMonitorPanel(this, monitor.get());
1.1.1.4   root       46: 
                     47:        // スクロールバー
1.1.1.9   root       48:        vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL);
1.1.1.3   root       49: 
1.1.1.13! root       50:        // Sizer 使わず自前でレイアウトする。
        !            51:        SetAutoLayout(true);
        !            52: 
1.1.1.4   root       53:        // ウィンドウサイズを確定させる
1.1.1.13! root       54:        FontChanged();
1.1.1.4   root       55: 
                     56:        // スクロールのために (パネル領域での) MouseWheel イベントもここで
1.1.1.9   root       57:        // 受け取りたい。スクロールバー上のマウスホイール操作はスクロールバーが
                     58:        // 処理しているので問題ないが、パネル領域でのマウスホイール操作はここ
                     59:        // (wxFrame)ではなくパネルに対して飛んでくる。
1.1.1.4   root       60:        // ここで一緒に処理したほうが楽なので、こちらに回す。
                     61:        screen->Connect(wxEVT_MOUSEWHEEL,
                     62:                wxMouseEventHandler(WXLogMonitor::OnMouseWheel), NULL, this);
1.1.1.9   root       63: 
1.1.1.13! root       64:        // スクロールバーからの位置変更通知イベントは表示パネルへ回す。
1.1.1.9   root       65:        vscroll->Connect(NONO_EVT_SCROLL,
                     66:                wxScrollEventHandler(WXLogMonitor::OnScroll), NULL, this);
                     67: 
                     68:        // XXX ログウィンドウのコピーはまだ動いてない
                     69:        screen->DisconnectContextMenu();
1.1.1.13! root       70:        DPRINTF("%s done\n", __method__);
1.1       root       71: }
                     72: 
                     73: // デストラクタ
                     74: WXLogMonitor::~WXLogMonitor()
                     75: {
                     76: }
                     77: 
1.1.1.13! root       78: void
        !            79: WXLogMonitor::FontChanged()
1.1.1.10  root       80: {
1.1.1.13! root       81:        screen->FontChanged();
        !            82:        vscroll->FontChanged();
        !            83:        DPRINTF("%s %d\n", __method__, screen->GetFontHeight());
        !            84: 
        !            85:        Fit();
1.1.1.10  root       86: }
                     87: 
1.1.1.4   root       88: void
1.1.1.13! root       89: WXLogMonitor::Fit()
        !            90: {
        !            91:        // inherited::Fit() は呼ばずに自力で行う。
        !            92: 
        !            93:        wxSize newsize = DoSizeHints();
        !            94: #if defined(WINDOW_DEBUG)
        !            95:        wxSize newwin = ClientToWindowSize(newsize);
        !            96:        printf("%s (%d,%d) client=(%d,%d)\n", __method__,
        !            97:                newwin.x, newwin.y, newsize.x, newsize.y);
        !            98: #endif
        !            99:        SetClientSize(newsize);
        !           100: }
        !           101: 
        !           102: bool
        !           103: WXLogMonitor::Layout()
1.1.1.4   root      104: {
1.1.1.13! root      105:        // inherited::Layout() は(実質何もしないので)、呼ばずに自力で行う。
        !           106: 
        !           107:        wxSize client = GetClientSize();
        !           108:        const wxSize win = GetSize();
        !           109:        const wxSize margin = win - client;
        !           110:        const wxSize vsize = vscroll->GetSize();
        !           111:        const wxSize monsize = screen->GetSize();
        !           112: 
        !           113:        // wxmonitor.cpp の Layout() 内のコメント参照。
        !           114:        if (oldmargin != margin) {
        !           115:                layout_pass2 = true;
        !           116:                oldmargin = margin;
        !           117:                CallAfter([this]() {
        !           118:                        DoSizeHints();
        !           119:                });
        !           120:                return true;
        !           121:        } else if (layout_pass2) {
        !           122:                layout_pass2 = false;
        !           123:                client.y = monsize.y;
        !           124:                SetClientSize(client);
        !           125:                Hide();
        !           126:                Show();
        !           127:                return true;
        !           128:        }
        !           129: 
        !           130:        // コントロールを配置。
        !           131:        int panel_width  = client.x - vsize.x;
        !           132:        int panel_height = client.y;
        !           133:        // 最低でも 1px ないと GTK とかに怒られる。
        !           134:        if (panel_width < 1) {
        !           135:                panel_width = 1;
        !           136:        }
        !           137:        if (panel_height < 1) {
        !           138:                panel_height = 1;
        !           139:        }
        !           140: 
        !           141:        screen->SetSize(0, 0, panel_width, panel_height);
        !           142:        vscroll->SetSize(panel_width, 0, vsize.x, panel_height);
1.1.1.4   root      143: 
                    144:        // スクロールバーを再設定
                    145:        SetScroll();
1.1.1.13! root      146: 
        !           147:        DPRINTF("%s done\n", __method__);
        !           148: 
        !           149:        return true;
        !           150: }
        !           151: 
        !           152: // クライアントサイズと最大/最小サイズを計算する。
        !           153: // SetSizeHints() を実行し、設定すべきクライアントサイズを返す。
        !           154: wxSize
        !           155: WXLogMonitor::DoSizeHints()
        !           156: {
        !           157:        auto ssize = screen->GetSize();
        !           158:        auto vsize = vscroll->GetSize();
        !           159:        DPRINTF("%s screen=(%d,%d) vscroll=(%d,%d)\n", __method__,
        !           160:                ssize.x, ssize.y, vsize.x, vsize.y);
        !           161: 
        !           162:        int x = ssize.x + vsize.x;
        !           163:        int min_y = std::max(vscroll->GetMinSize().y, screen->GetScreenHeight(1));
        !           164:        int new_y = std::max(ssize.y, min_y);
        !           165:        wxSize minsize(x, min_y);
        !           166:        wxSize maxsize(x, GetMaxClientSize().y);
        !           167:        wxSize incsize(0, screen->GetFontHeight());
        !           168:        SetSizeHints(ClientToWindowSize(minsize), ClientToWindowSize(maxsize),
        !           169:                incsize);
        !           170: 
        !           171:        return wxSize(x, new_y);
1.1.1.4   root      172: }
                    173: 
                    174: // スクロールバーを再設定
                    175: void
                    176: WXLogMonitor::SetScroll()
                    177: {
                    178:        int pos;
                    179:        int thumbsize;
                    180:        int range;
                    181:        int pagesize;
                    182: 
1.1.1.7   root      183:        row = screen->GetRow();
1.1.1.4   root      184: 
                    185:        if (lines <= row) {
                    186:                pos = 0;
                    187:                range = row;
                    188:        } else {
                    189:                pos = (lines - row) - vpos;
                    190:                range = lines;
                    191:        }
                    192:        thumbsize = row;
                    193:        pagesize = thumbsize - 1;
1.1.1.9   root      194:        vscroll->SetScrollParam(pos, thumbsize, range, pagesize);
1.1.1.4   root      195: }
                    196: 
                    197: // マウスホイールイベント (WXTextScreen 上で起きたやつをこっちに回してある)
1.1       root      198: void
1.1.1.4   root      199: WXLogMonitor::OnMouseWheel(wxMouseEvent& event)
                    200: {
                    201:        // GetWheelRotation() は上(奥)向きに回すと +120、下(手前)に回すと -120。
                    202:        // どこの決まりか知らんけど、スクロールバーのほうはホイール1段階で3行
                    203:        // スクロールするのでそれに合わせる。
                    204:        int pos = vscroll->GetThumbPosition();
                    205:        pos = pos - event.GetWheelRotation() / 40;
                    206: 
                    207:        int maxpos = vscroll->GetRange() - vscroll->GetThumbSize();
                    208:        if (pos < 0)
                    209:                pos = 0;
                    210:        if (pos > maxpos)
                    211:                pos = maxpos;
                    212: 
                    213:        DoScroll(pos);
1.1.1.9   root      214:        // スクロールバーの位置を追従
                    215:        vscroll->SetThumbPosition(pos);
1.1.1.4   root      216: }
                    217: 
1.1.1.9   root      218: // スクロールバーからの通知イベント
1.1.1.4   root      219: void
                    220: WXLogMonitor::OnScroll(wxScrollEvent& event)
                    221: {
                    222:        DoScroll(event.GetPosition());
                    223: }
                    224: 
                    225: // スクロール処理 (OnScroll() と OnMouseWheel() から呼ばれる)
                    226: void
                    227: WXLogMonitor::DoScroll(int pos)
                    228: {
                    229:        // pos 即ちスクロールバーの Thumb の位置は上端の位置。
                    230:        // 対してログウィンドウでは下端を基準にしたほうが処理しやすい。
                    231:        // vpos = 0 だと Thumb が下に接していてパネルの最下行が最新行、
                    232:        // vpos = 1 だと Thumb が下から1段階分上にありパネルの最下行は最新行の
                    233:        // 一つ前の行、となる。
                    234:        //
                    235:        // Range=6              Range=6
                    236:        // Pos=4                Pos=0
                    237:        // ThumbSz=2    ThumbSz=2
                    238:        // vpos=0               vpos=4
                    239:        // △                  △
                    240:        // □                  ■
                    241:        // □                  ■
                    242:        // □                  □
                    243:        // □                  □
                    244:        // ■                  □
                    245:        // ■                  □
                    246:        // ▽                  ▽
                    247: 
                    248:        // パネル最下行に表示する行の最新行からのオフセット
                    249:        // (0 なら Thumb が下端に接している)
                    250:        vpos = vscroll->GetRange() - vscroll->GetThumbSize() - pos;
                    251: }
                    252: 
1.1.1.9   root      253: 
1.1.1.7   root      254: //
                    255: // モニターオブジェクト
                    256: //
                    257: 
                    258: // コンストラクタ
                    259: LogMonitor::LogMonitor()
1.1.1.11  root      260:        : inherited(OBJ_LOGMONITOR)
1.1.1.4   root      261: {
1.1.1.7   root      262: }
                    263: 
                    264: // デストラクタ
                    265: LogMonitor::~LogMonitor()
                    266: {
                    267: }
                    268: 
                    269: void
                    270: LogMonitor::Init(WXLogMonitor *parent_)
                    271: {
                    272:        parent = parent_;
                    273: 
1.1.1.9   root      274:        logger = gMainApp.GetLogger();
                    275: 
1.1.1.7   root      276:        // バックログは今の所固定長
                    277:        logbuf.Init(parent->col, backlog);
                    278:        logbuf.Mode = TextScreen::Ring;
1.1.1.4   root      279: }
                    280: 
                    281: // モニターテキストを更新
                    282: void
1.1.1.7   root      283: LogMonitor::MonitorUpdate(Monitor *, TextScreen& screen)
1.1       root      284: {
                    285:        char buf[1024];
                    286: 
1.1.1.4   root      287:        // どこでどうするかはあるけど、
                    288:        // とりあえず row は常に現在の TextScreen の高さということにしておく
1.1.1.7   root      289:        parent->row = screen.GetRow();
1.1.1.4   root      290: 
1.1.1.6   root      291:        // XXX: 生成時にやるべき
1.1.1.7   root      292:        screen.Mode = TextScreen::Ring;
1.1.1.6   root      293: 
1.1.1.4   root      294:        // キューから読めるだけバックログに書き込む。
                    295:        // 読めるだけと言っても while ループで読めるだけ読んでしまうと、
                    296:        // 流速が早すぎた場合にこのループから抜けるタイミングがなくなる可能性が
                    297:        // (可能性としては) あるので、適当に1画面分くらいで打ち切る。
                    298:        // 取り込み残した分は次回取り込まれるし、その流速も上回るようだと
1.1.1.9   root      299:        // どのみち Logger の固定長キューで落ちる。
1.1.1.7   root      300:        for (int i = 0; i < parent->row; i++) {
1.1.1.9   root      301:                if (logger->Read(buf, sizeof(buf)) == false)
1.1.1.4   root      302:                        break;
1.1       root      303:                Append(buf);
                    304:        }
                    305: 
1.1.1.7   root      306:        screen.Clear();
1.1.1.4   root      307: 
                    308:        // スクロールバー(vpos)による表示位置計算
1.1.1.6   root      309:        // vpos は行数、v は logbuf の Y 座標
1.1.1.7   root      310:        int v = cursor - parent->vpos;
1.1.1.4   root      311:        if (v < 0) {
1.1.1.6   root      312:                v += backlog;
1.1       root      313:        }
                    314: 
1.1.1.4   root      315:        // 表示位置(v)の Row 行分手前から順に表示
1.1.1.7   root      316:        int srcY = v - parent->row;
1.1.1.6   root      317:        if (srcY < 0) {
                    318:                srcY += backlog;
                    319:        }
                    320: 
                    321:        // 画面コピー
                    322:        logbuf.Locate(0, srcY);
1.1.1.7   root      323:        for (int i = 0; i < parent->col * parent->row; i++) {
1.1.1.12  root      324:                uint16 ch = logbuf.Getc();
                    325:                logbuf.Ascend();
                    326:                screen.Putc(ch);
1.1.1.4   root      327:        }
                    328: }
1.1       root      329: 
1.1.1.4   root      330: // 1行追加された時の共通処理
                    331: void
1.1.1.7   root      332: LogMonitor::IncLines()
1.1.1.4   root      333: {
                    334:        // スクロールバーが下端にない時(バックログを見ている時)は
                    335:        // ログが追加されても現在位置をキープする。
1.1.1.7   root      336:        if (parent->vpos > 0) {
1.1.1.4   root      337:                // 上端に達したら増加させない
1.1.1.7   root      338:                if (parent->vpos < backlog - parent->vscroll->GetThumbSize()) {
                    339:                        parent->vpos++;
1.1       root      340:                }
                    341:        }
                    342: 
1.1.1.4   root      343:        // 有効行数を増やす
1.1.1.7   root      344:        if (parent->lines < backlog) {
                    345:                parent->lines++;
1.1       root      346: 
1.1.1.4   root      347:                // 行数が変わったのでスクロールバーを更新
1.1.1.7   root      348:                parent->SetScroll();
1.1.1.4   root      349:        }
1.1       root      350: }
                    351: 
1.1.1.4   root      352: // バックログに行を追加。
1.1       root      353: //
1.1.1.4   root      354: // 引数の utfbuf は UTF-8 で改行コードのない1行ずつ。
                    355: // これをバックログの最新位置に書き込む。古い行は順次上書きされる。
1.1       root      356: //
1.1.1.4   root      357: // バックログは col * backlog バイトの Shift_JIS バッファ。
                    358: // 属性は持たず文字コードのみ。漢字は2バイト幅をとる。
                    359: // 横幅 col が固定なので書き込む時点で行数は表示用のそれと一致する。
1.1       root      360: //
1.1.1.4   root      361: // cursor はこれから書き込む位置の行頭。cursor は必ず行頭だけをポイント
                    362: // しながら移動する。
                    363: //
                    364: // logbuf    +------------------------
                    365: //           | :
                    366: //           | 前回書き込んだ行
                    367: // cursor -> | 今から書きこむ行 (保存されていた一番古い行)
                    368: //           :
                    369: //           |
                    370: //           +------------------------
1.1       root      371: void
1.1.1.7   root      372: LogMonitor::Append(const char *utfbuf)
1.1       root      373: {
1.1.1.4   root      374:        const char *s;
                    375:        int x;
1.1       root      376: 
                    377:        // 文字コードの変換
                    378:        wxString utfstr(utfbuf, wxConvUTF8);
1.1.1.9   root      379:        // mb_str() をキャストせずに一度保持しておく必要がある (scan-build 対策)
                    380:        auto sjis = utfstr.mb_str(conv);
1.1       root      381: 
1.1.1.6   root      382:        logbuf.Locate(0, cursor);
                    383: 
1.1.1.9   root      384:        for (s = (const char *)sjis; *s; ) {
1.1.1.4   root      385:                // 最終桁で2バイト文字の1バイト目が来たら
                    386:                // 代わりに1文字空白を入れる (それによってこの後改行が起きる)
1.1.1.6   root      387:                if (SJIS::IsZenkaku(*s)) {
                    388:                        // 漢字ならば 2 バイトセットで扱う
1.1.1.7   root      389:                        if (logbuf.GetX() >= parent->col - 1) {
1.1.1.6   root      390:                                // 最終桁には出せないので空白で次の行に送る
                    391:                                logbuf.Putc(' ');
                    392:                                IncLines();
                    393:                        }
                    394:                        logbuf.Putc(*s++);
                    395:                        // 万が一に備える
                    396:                        if (__predict_true(*s)) {
                    397:                                logbuf.Putc(*s++);
                    398:                        }
1.1.1.4   root      399:                } else {
                    400:                        // そうでなければそのまま出力
1.1.1.6   root      401:                        logbuf.Putc(*s++);
1.1       root      402:                }
1.1.1.4   root      403:        }
1.1       root      404: 
1.1.1.6   root      405:        // 行の終わりまでは空白でクリアする
                    406:        x = logbuf.GetX();
1.1.1.4   root      407:        if (x > 0) {
1.1.1.6   root      408:                for (; x < logbuf.GetCol(); x++) {
                    409:                        logbuf.Putc(' ');
                    410:                }
1.1.1.4   root      411:                IncLines();
1.1       root      412:        }
1.1.1.6   root      413:        // 次回書き込みの Y 座標
                    414:        cursor = logbuf.GetY();
1.1       root      415: }

unix.superglobalmegacorp.com

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