--- nono/wx/wxlogmonitor.cpp 2026/04/29 17:04:51 1.1.1.5 +++ nono/wx/wxlogmonitor.cpp 2026/04/29 17:05:12 1.1.1.10 @@ -4,79 +4,67 @@ // Licensed under nono-license.txt // -#include "wxlogmonitor.h" -#include "logger.h" - // // ログモニター // +#include "wxlogmonitor.h" +#include "logger.h" +#include "mainapp.h" +#include "sjis.h" + // イベントテーブル wxBEGIN_EVENT_TABLE(WXLogMonitor, inherited) EVT_SIZE(WXLogMonitor::OnSize) - EVT_SCROLL(WXLogMonitor::OnScroll) wxEND_EVENT_TABLE() // コンストラクタ -WXLogMonitor::WXLogMonitor(wxWindow *parent, wxWindowID id) - : inherited(parent, id, _("Log")) +WXLogMonitor::WXLogMonitor(wxWindow *parent) + : inherited(parent, wxID_ANY, _("Log"), DEFAULT_STYLE | wxRESIZE_BORDER) { col = 80; row = 40; - // バックログは今の所固定長 - logbuf.resize(col * backlog, ' '); + monitor.obj = &log; + monitor.func = ToMonitorCallback(&LogMonitor::MonitorUpdate); + monitor.SetSize(col, row); + // プライベート利用なので Regist() は不要 + + log.Init(this); // → - // +--+-----------------------+--+--+ - // | |↓+------------------+ | | | - // | | | 上余白(padding1) | | | | - // |左| +------------------+ |右| | - // |余| | TextScreen | |余| <- VScroll - // |白| +------------------+ |白| | - // | | | 下余白(padding2) | | | | - // | | +------------------+ | | | - // +--+-----------------------+--+--+ - - topsizer = new wxBoxSizer(wxHORIZONTAL); - // 左余白 - topsizer->AddSpacer(DefaultPadding); - // 中列の縦 sizer - auto *vbox = new wxBoxSizer(wxVERTICAL); - topsizer->Add(vbox, 0, wxEXPAND); - - // パディングパネル(上下) - // ウィンドウを1行ずつ伸縮させるためには、クライアント領域がフォント高さの - // 整数倍になってないといけない。TextScreen はそれ自身が行の整数倍の大きさ - // だが、上下の余白はその時点のフォントサイズによって変更する必要がある。 - // 先にサイズ不定のパネルだけ置いといて、フォントサイズ確定後に大きさを - // 確定させる。DoSize() 参照。 - padding1 = new wxPanel(this, wxID_ANY); - padding2 = new wxPanel(this, wxID_ANY); - vbox->Add(padding1); - screen = new WXTextScreen(this, *this); - vbox->Add(screen, 1, wxEXPAND); - vbox->Add(padding2); + // +------------+---------+ + // | TextScreen | VScroll | + // +------------+---------+ + + wxBoxSizer *topsizer = new wxBoxSizer(wxHORIZONTAL); + + // テキストスクリーン + screen = new WXMonitorPanel(this, monitor); + topsizer->Add(screen, 1, wxEXPAND); - // 右余白 - topsizer->AddSpacer(DefaultPadding); // スクロールバー - vscroll = new wxScrollBar(this, wxID_ANY, wxDefaultPosition, - wxDefaultSize, wxSB_VERTICAL); + vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL); topsizer->Add(vscroll, 0, wxEXPAND); SetSizer(topsizer); - // ウィンドウサイズを確定させる DoSize(); // スクロールのために (パネル領域での) MouseWheel イベントもここで - // 受け取りたい。スクロールバー上のマウスホイール操作は最初から Scroll - // イベントとして処理されているので問題ないが、パネル領域でのマウス - // ホイール操作はここ(wxFrame)ではなくパネルに対して飛んでくる。 + // 受け取りたい。スクロールバー上のマウスホイール操作はスクロールバーが + // 処理しているので問題ないが、パネル領域でのマウスホイール操作はここ + // (wxFrame)ではなくパネルに対して飛んでくる。 // ここで一緒に処理したほうが楽なので、こちらに回す。 screen->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(WXLogMonitor::OnMouseWheel), NULL, this); + + // スクロールバーからの位置変更通知 + vscroll->Connect(NONO_EVT_SCROLL, + wxScrollEventHandler(WXLogMonitor::OnScroll), NULL, this); + + // XXX ログウィンドウのコピーはまだ動いてない + screen->DisconnectContextMenu(); } // デストラクタ @@ -84,11 +72,11 @@ WXLogMonitor::~WXLogMonitor() { } -// フォントサイズが確定したら、それによってウィンドウサイズを再計算する。 -void -WXLogMonitor::DoSize() +bool +WXLogMonitor::Layout() { - CalcVSize(0); + // 縦リサイズ可能レイアウト + return LayoutTextVResize(screen); } // サイズ変更イベント @@ -110,8 +98,7 @@ WXLogMonitor::SetScroll() int range; int pagesize; - const wxSize& size = screen->GetSize(); - row = size.GetHeight(); + row = screen->GetRow(); if (lines <= row) { pos = 0; @@ -122,7 +109,7 @@ WXLogMonitor::SetScroll() } thumbsize = row; pagesize = thumbsize - 1; - vscroll->SetScrollbar(pos, thumbsize, range, pagesize); + vscroll->SetScrollParam(pos, thumbsize, range, pagesize); } // マウスホイールイベント (WXTextScreen 上で起きたやつをこっちに回してある) @@ -142,13 +129,14 @@ WXLogMonitor::OnMouseWheel(wxMouseEvent& pos = maxpos; DoScroll(pos); + // スクロールバーの位置を追従 + vscroll->SetThumbPosition(pos); } -// スクロールイベント (全部入り) +// スクロールバーからの通知イベント void WXLogMonitor::OnScroll(wxScrollEvent& event) { - // Thumb の上端の位置 (0 なら Thumb が上端に接している) DoScroll(event.GetPosition()); } @@ -178,104 +166,102 @@ WXLogMonitor::DoScroll(int pos) // パネル最下行に表示する行の最新行からのオフセット // (0 なら Thumb が下端に接している) vpos = vscroll->GetRange() - vscroll->GetThumbSize() - pos; +} - // Thumb の位置を更新 - vscroll->SetThumbPosition(pos); + +// +// モニターオブジェクト +// + +// コンストラクタ +LogMonitor::LogMonitor() + : inherited("LogMonitor") +{ } -// モニターサイズ取得 -nnSize -WXLogMonitor::GetMonitorSize() -{ - return nnSize(col, row); -} - -// バックログから TextScreen へのコピー -#define CopyText(text, row) do { \ - for (int i = 0; i < (row); i++) { \ - text.Locate(0, y++); \ - for (int x = 0; x < col; x++) { \ - text.Putc(*s++); \ - } \ - } \ -} while (0) +// デストラクタ +LogMonitor::~LogMonitor() +{ +} + +void +LogMonitor::Init(WXLogMonitor *parent_) +{ + parent = parent_; + + logger = gMainApp.GetLogger(); + + // バックログは今の所固定長 + logbuf.Init(parent->col, backlog); + logbuf.Mode = TextScreen::Ring; +} // モニターテキストを更新 void -WXLogMonitor::MonitorUpdate(TextScreen& text) +LogMonitor::MonitorUpdate(Monitor *, TextScreen& screen) { char buf[1024]; // どこでどうするかはあるけど、 // とりあえず row は常に現在の TextScreen の高さということにしておく - row = text.GetRow(); + parent->row = screen.GetRow(); + + // XXX: 生成時にやるべき + screen.Mode = TextScreen::Ring; // キューから読めるだけバックログに書き込む。 // 読めるだけと言っても while ループで読めるだけ読んでしまうと、 // 流速が早すぎた場合にこのループから抜けるタイミングがなくなる可能性が // (可能性としては) あるので、適当に1画面分くらいで打ち切る。 // 取り込み残した分は次回取り込まれるし、その流速も上回るようだと - // どのみち gLogger の固定長キューで落ちる。 - for (int i = 0; i < row; i++) { - if (gLogger.Read(buf, sizeof(buf)) == false) + // どのみち Logger の固定長キューで落ちる。 + for (int i = 0; i < parent->row; i++) { + if (logger->Read(buf, sizeof(buf)) == false) break; Append(buf); } - text.Clear(); + screen.Clear(); // スクロールバー(vpos)による表示位置計算 - // vpos は行数、v は logbuf[] 先頭からのバイトオフセット - int v = cursor - col * vpos; + // vpos は行数、v は logbuf の Y 座標 + int v = cursor - parent->vpos; if (v < 0) { - v += col * backlog; + v += backlog; } // 表示位置(v)の Row 行分手前から順に表示 - int size = col * row; - int y = 0; - uint8 *s; - if (v >= size) { - // 折り返しなし - s = &logbuf[v - size]; - CopyText(text, row); - } else { - // 折り返しあり - int row1 = (size - v) / col; - int row2 = row - row1; - s = &logbuf[col * backlog - (size - v)]; - CopyText(text, row1); - s = &logbuf[0]; - CopyText(text, row2); + int srcY = v - parent->row; + if (srcY < 0) { + srcY += backlog; + } + + // 画面コピー + logbuf.Locate(0, srcY); + for (int i = 0; i < parent->col * parent->row; i++) { + screen.Putc(logbuf.Getc()); } } // 1行追加された時の共通処理 void -WXLogMonitor::IncLines() +LogMonitor::IncLines() { - // カーソルを次行先頭に移動 - cursor += col; - // バッファ末尾まで来たら折り返し - if (cursor >= col * backlog) { - cursor = 0; - } - // スクロールバーが下端にない時(バックログを見ている時)は // ログが追加されても現在位置をキープする。 - if (vpos > 0) { + if (parent->vpos > 0) { // 上端に達したら増加させない - if (vpos < backlog - vscroll->GetThumbSize()) { - vpos++; + if (parent->vpos < backlog - parent->vscroll->GetThumbSize()) { + parent->vpos++; } } // 有効行数を増やす - if (lines < backlog) { - lines++; + if (parent->lines < backlog) { + parent->lines++; // 行数が変わったのでスクロールバーを更新 - SetScroll(); + parent->SetScroll(); } } @@ -299,46 +285,47 @@ WXLogMonitor::IncLines() // | // +------------------------ void -WXLogMonitor::Append(const char *utfbuf) +LogMonitor::Append(const char *utfbuf) { - const char *sjis; const char *s; int x; - bool iskanji; // 文字コードの変換 wxString utfstr(utfbuf, wxConvUTF8); - sjis = (const char *)utfstr.mb_str(conv); + // mb_str() をキャストせずに一度保持しておく必要がある (scan-build 対策) + auto sjis = utfstr.mb_str(conv); + + logbuf.Locate(0, cursor); - iskanji = false; - s = sjis; - x = 0; - for (; *s; ) { - // Shift_JIS の1文字目は上位ニブルが 8,9,E。 + for (s = (const char *)sjis; *s; ) { // 最終桁で2バイト文字の1バイト目が来たら // 代わりに1文字空白を入れる (それによってこの後改行が起きる) - if (iskanji == false && - (int16)(0b00000000'11000010 << (*s >> 4)) < 0 && - x >= col - 1) - { - logbuf[cursor + x] = ' '; + if (SJIS::IsZenkaku(*s)) { + // 漢字ならば 2 バイトセットで扱う + if (logbuf.GetX() >= parent->col - 1) { + // 最終桁には出せないので空白で次の行に送る + logbuf.Putc(' '); + IncLines(); + } + logbuf.Putc(*s++); + // 万が一に備える + if (__predict_true(*s)) { + logbuf.Putc(*s++); + } } else { // そうでなければそのまま出力 - logbuf[cursor + x] = *s++; - iskanji = false; - } - x++; - - // 行端まで来たら改行 - if (x >= col) { - IncLines(); - // 行頭 - x = 0; + logbuf.Putc(*s++); } } - // 1行追加し終わって行頭でなければ、改行 + // 行の終わりまでは空白でクリアする + x = logbuf.GetX(); if (x > 0) { + for (; x < logbuf.GetCol(); x++) { + logbuf.Putc(' '); + } IncLines(); } + // 次回書き込みの Y 座標 + cursor = logbuf.GetY(); }