|
|
1.1 root 1: //
2: // nono
1.1.1.2 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: //
1.1.1.7 root 8: // モニター
1.1 root 9: //
10:
1.1.1.6 root 11: #include "wxmonitor.h"
1.1.1.7 root 12: #include "wxmainframe.h"
1.1.1.9 root 13: #include "monitor.h"
1.1.1.7 root 14:
1.1.1.10! root 15: //#define WINDOW_DEBUG 1
! 16:
! 17: #if defined(WINDOW_DEBUG)
! 18: #define DPRINTF(fmt...) printf(fmt)
! 19: #else
! 20: #define DPRINTF(fmt...) /**/
! 21: #endif
! 22:
1.1.1.7 root 23: //
24: // モニターパネル
25: //
26:
27: // イベントテーブル
28: wxBEGIN_EVENT_TABLE(WXMonitorPanel, inherited)
29: EVT_TIMER(wxID_ANY, WXMonitorPanel::OnTimer)
30: wxEND_EVENT_TABLE()
31:
32: // コンストラクタ
1.1.1.9 root 33: WXMonitorPanel::WXMonitorPanel(wxWindow *parent, Monitor *monitor_)
34: : inherited(parent, monitor_->GetSize())
1.1.1.7 root 35: , monitor(monitor_)
36: {
37: timer.SetOwner(this);
38:
39: // 現在の設定値を適用
1.1.1.8 root 40: auto mainframe = dynamic_cast<WXMainFrame*>(GetParent()->GetParent());
41: SetRate(mainframe->GetMonitorRate());
1.1.1.7 root 42:
43: // 最初に一回描画を起こす
44: wxTimerEvent dummy(timer);
45: AddPendingEvent(dummy);
46: }
47:
48: // デストラクタ
49: WXMonitorPanel::~WXMonitorPanel()
50: {
51: }
52:
53: // タイマーイベント
54: void
55: WXMonitorPanel::OnTimer(wxTimerEvent& event)
56: {
57: DoRefresh();
58: }
59:
60: // 画面を更新して再描画する。
61: //
62: // 通常はタイマーイベントにより更新間隔ごとに呼ばれるが、
63: // ユーザ操作により画面の更新が必要ならその都度直接これを呼ぶ。
64: void
65: WXMonitorPanel::DoRefresh()
66: {
67: // モニタースクリーンを更新して
68: MONITOR_UPDATE(monitor, screen);
69:
70: // 必要なら再描画
71: if (screen.GetBuf() != prevbuf) {
72: Refresh();
73: }
74: }
75:
76: // 画面更新頻度を Hz で設定する。
77: void
78: WXMonitorPanel::SetRate(int hz)
79: {
80: timer.Start(1000 / hz);
81: }
82:
83:
84: //
85: // モニターウィンドウ
86: //
1.1.1.6 root 87:
1.1 root 88: // コンストラクタ
1.1.1.5 root 89: WXMonitorWindow::WXMonitorWindow(wxWindow *parent, const wxString& name,
1.1.1.9 root 90: Monitor *monitor_)
1.1.1.5 root 91: : inherited(parent, wxID_ANY, name)
1.1 root 92: {
1.1.1.7 root 93: // モニタパネル
94: monpanel = new WXMonitorPanel(this, monitor_);
1.1.1.10! root 95: Fit();
1.1 root 96: }
97:
98: // デストラクタ
99: WXMonitorWindow::~WXMonitorWindow()
100: {
101: }
1.1.1.7 root 102:
103:
104: //
105: // 縦スクロールバー付きモニタウィンドウ
106: //
107:
108: // コンストラクタ
109: WXScrollMonitorWindow::WXScrollMonitorWindow(wxWindow *parent,
1.1.1.9 root 110: const wxString& name, Monitor *monitor_)
1.1.1.7 root 111: : inherited(parent, wxID_ANY, name, DEFAULT_STYLE | wxRESIZE_BORDER)
112: {
1.1.1.10! root 113: DPRINTF("%s begin\n", __method__);
! 114:
1.1.1.7 root 115: // +--------------+---------+
116: // | MonitorPanel | VScroll |
117: // +--------------+---------+
118:
119: // モニタパネル
120: monpanel = new WXMonitorPanel(this, monitor_);
121:
122: // スクロールバー
123: vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL);
124:
1.1.1.10! root 125: // Sizer 使わず自前でレイアウトする。
! 126: SetAutoLayout(true);
1.1.1.7 root 127:
128: // ウィンドウサイズを確定させる
1.1.1.10! root 129: FontChanged();
1.1.1.7 root 130:
131: // スクロールのために (パネル領域での) MouseWheel イベントもここで
132: // 受け取りたい。スクロールバー上のマウスホイール操作はスクロールバーが
133: // 処理しているので問題ないが、パネル領域でのマウスホイール操作はここ
134: // (wxFrame)ではなくパネルに対して飛んでくる。
135: // ここで一緒に処理したほうが楽なので、こちらに回す。
136: monpanel->Connect(wxEVT_MOUSEWHEEL,
137: wxMouseEventHandler(WXScrollMonitorWindow::OnMouseWheel), NULL, this);
138:
139: // スクロールバーからの位置変更通知
140: vscroll->Connect(NONO_EVT_SCROLL,
141: wxScrollEventHandler(WXScrollMonitorWindow::OnScroll), NULL, this);
1.1.1.10! root 142: DPRINTF("%s done\n", __method__);
1.1.1.7 root 143: }
144:
145: // デストラクタ
146: WXScrollMonitorWindow::~WXScrollMonitorWindow()
147: {
148: }
149:
1.1.1.10! root 150: void
! 151: WXScrollMonitorWindow::FontChanged()
1.1.1.7 root 152: {
1.1.1.10! root 153: monpanel->FontChanged();
! 154: vscroll->FontChanged();
! 155: DPRINTF("%s %d\n", __method__, monpanel->GetFontHeight());
! 156:
! 157: Fit();
1.1.1.7 root 158: }
159:
160: void
1.1.1.10! root 161: WXScrollMonitorWindow::Fit()
1.1.1.7 root 162: {
1.1.1.10! root 163: // inherited::Fit() は呼ばずに自力で行う。
1.1.1.7 root 164:
1.1.1.10! root 165: wxSize newsize = DoSizeHints();
! 166: #if defined(WINDOW_DEBUG)
! 167: wxSize newwin = ClientToWindowSize(newsize);
! 168: printf("%s (%d,%d) client=(%d,%d)\n", __method__,
! 169: newwin.x, newwin.y, newsize.x, newsize.y);
! 170: #endif
! 171: SetClientSize(newsize);
! 172: }
1.1.1.7 root 173:
1.1.1.10! root 174: bool
! 175: WXScrollMonitorWindow::Layout()
! 176: {
! 177: // inherited::Layout() は(実質何もしないので)、呼ばずに自力で行う。
! 178:
! 179: wxSize client = GetClientSize();
! 180: const wxSize win = GetSize();
! 181: const wxSize margin = win - client;
! 182: const wxSize vsize = vscroll->GetSize();
! 183: const wxSize monsize = monpanel->GetSize();
! 184: #if defined(WINDOW_DEBUG)
! 185: const wxSize min = GetMinClientSize();
! 186: int py = client.y
! 187: - monpanel->GetPaddingTop() - monpanel->GetPaddingBottom();
! 188: int height = monpanel->GetFontHeight();
! 189: int row = (height != 0) ? (py / height) : 0;
! 190: int res = (height != 0) ? (py % height) : 0;
! 191: wxSize minwin = GetMinSize();
! 192: printf("%s begin: margin=(%d,%d) shown=%d\n", __method__,
! 193: margin.x, margin.y, IsShown());
! 194: printf("%s WinSize =(%d,%d) MinWin =(%d,%d)\n", __method__,
! 195: win.x, win.y, minwin.x, minwin.y);
! 196: printf("%s ClientSize=(%d,%d) MinClient=(%d,%d)\n", __method__,
! 197: client.x, client.y, min.x, min.y);
! 198: printf("%s mon=(%d,%d) vscroll=(%d,%d), row=%d%s\n", __method__,
! 199: monsize.x, monsize.y, vsize.x, vsize.y,
! 200: row, (res == 0 ? "" : "+"));
! 201: #endif
! 202:
! 203: if (oldmargin != margin) {
! 204: // ウィンドウマネージャ管轄のサイズが変わったので最小サイズを再設定。
! 205: // ここでは出来ないのでこの関数を抜けた後 CallAfter 内で設定する。
! 206: layout_pass2 = true;
! 207: oldmargin = margin;
! 208: CallAfter([this]() {
! 209: DoSizeHints();
! 210: });
! 211: DPRINTF("%s done(pass1)\n", __method__);
! 212: return true;
! 213: } else if (layout_pass2) {
! 214: // SetSizeHints() 後に呼ばれるところ。
! 215: // なぜか設定したサイズと変わっている(ことがある?)ので、その場合は
! 216: // 仕方ないのでここで元の要求サイズに再設定する。
! 217: // ウィンドウマネージャによっては表示サイズが変わらない(?)ので
! 218: // Hide()/Show() で叩き起こすと直るようだ??。
! 219: layout_pass2 = false;
! 220: client.y = monsize.y;
! 221: DPRINTF("%s SetClientSize (%d,%d)\n", __method__, client.x, client.y);
! 222: SetClientSize(client);
! 223: Hide();
! 224: Show();
! 225: DPRINTF("%s done(pass2)\n", __method__);
! 226: return true;
! 227: }
! 228:
! 229: // コントロールを配置。
! 230: int mon_width = client.x - vsize.x;
! 231: int mon_height = client.y;
! 232: // 最低でも 1px ないと GTK とかに怒られる。
! 233: if (mon_width < 1) {
! 234: mon_width = 1;
! 235: }
! 236: if (mon_height < 1) {
! 237: mon_height = 1;
! 238: }
! 239:
! 240: monpanel->SetSize(0, 0, mon_width, mon_height);
! 241: vscroll->SetSize(mon_width, 0, vsize.x, mon_height);
! 242:
! 243: // スクロールバーを再設定。
! 244: // 今の所ヘッダが常に1行ある。
! 245: static int HEADLINES = 1;
1.1.1.7 root 246: int pos = (int)monpanel->GetUserData();
247: int thumbsize = monpanel->GetRow() - HEADLINES;
1.1.1.9 root 248: int range = monpanel->GetMonitor()->GetMaxHeight() - HEADLINES;
1.1.1.7 root 249: int pagesize = thumbsize - 1;
250: if (pos > range - thumbsize) {
251: pos = range - thumbsize;
252: DoScroll(pos);
253: }
254: vscroll->SetScrollParam(pos, thumbsize, range, pagesize);
1.1.1.10! root 255:
! 256: DPRINTF("%s done\n", __method__);
! 257:
! 258: return true;
! 259: }
! 260:
! 261: // クライアントサイズと最大/最小サイズを計算する。
! 262: // SetSizeHints() を実行し、設定すべきクライアントサイズを返す。
! 263: wxSize
! 264: WXScrollMonitorWindow::DoSizeHints()
! 265: {
! 266: auto psize = monpanel->GetSize();
! 267: auto vsize = vscroll->GetSize();
! 268:
! 269: // 幅は monpanel 幅 + スクロールバーの幅、高さは monpanel の高さで決まる。
! 270: // monpanel には四辺に Padding があることに注意。
! 271: int x = psize.x + vsize.x;
! 272: int min_y = std::max(vscroll->GetMinSize().y,
! 273: monpanel->GetScreenHeight(1));
! 274: int new_y = std::max(psize.y, min_y);
! 275: wxSize minsize(x, min_y);
! 276: wxSize maxsize(x, GetMaxClientSize().y);
! 277: wxSize incsize(0, monpanel->GetFontHeight());
! 278: wxSize minwin = ClientToWindowSize(minsize);
! 279: wxSize maxwin = ClientToWindowSize(maxsize);
! 280: wxSize newsize(x, new_y);
! 281: DPRINTF("%s MinWin=(%d,%d) RecommendClient=(%d,%d)\n", __method__,
! 282: minwin.x, minwin.y, newsize.x, newsize.y);
! 283: SetSizeHints(minwin, maxwin, incsize);
! 284:
! 285: return newsize;
1.1.1.7 root 286: }
287:
288: // マウスホイールイベント (WXTextScreen 上で起きたやつをこっちに回してある)
289: void
290: WXScrollMonitorWindow::OnMouseWheel(wxMouseEvent& event)
291: {
292: // GetWheelRotation() は上(奥)向きに回すと +120、下(手前)に回すと -120。
293: // どこの決まりか知らんけど、スクロールバーのほうはホイール1段階で3行
294: // スクロールするのでそれに合わせる。
295: int pos = (int)monpanel->GetUserData();
296: pos = pos - event.GetWheelRotation() / 40;
297:
298: int maxpos = vscroll->GetRange() - vscroll->GetThumbSize();
299: if (pos < 0)
300: pos = 0;
301: if (pos > maxpos)
302: pos = maxpos;
303:
304: DoScroll(pos);
305: // スクロールバーの位置を追従
306: vscroll->SetThumbPosition(pos);
307: }
308:
309: // スクロールバーからの通知イベント
310: void
311: WXScrollMonitorWindow::OnScroll(wxScrollEvent& event)
312: {
313: DoScroll(event.GetPosition());
314: }
315:
316: // スクロール処理 (OnScroll() と OnMouseWheel() から呼ばれる)
317: void
318: WXScrollMonitorWindow::DoScroll(int pos)
319: {
320: // userdata が表示開始位置になっている
321: monpanel->SetUserData(pos);
322: monpanel->DoRefresh();
323: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.