File:  [Isaki's NoNo m68k/m88k emulator] / nono / wx / wxmonitor.cpp
Revision 1.1.1.13 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:57 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//

//
// モニター
//

#include "wxmonitor.h"
#include "wxmainframe.h"
#include "monitor.h"

//#define WINDOW_DEBUG 1

#if defined(WINDOW_DEBUG)
#define DPRINTF(fmt...) printf(fmt)
#else
#define DPRINTF(fmt...) /**/
#endif

//
// モニターパネル
//

// イベントテーブル
wxBEGIN_EVENT_TABLE(WXMonitorPanel, inherited)
	EVT_TIMER(wxID_ANY, WXMonitorPanel::OnTimer)
wxEND_EVENT_TABLE()

// コンストラクタ
WXMonitorPanel::WXMonitorPanel(wxWindow *parent, Monitor *monitor_)
	: inherited(parent, monitor_->GetSize())
	, monitor(monitor_)
{
	timer.SetOwner(this);

	// 現在の設定値を適用
	auto mainframe = dynamic_cast<WXMainFrame*>(GetParent()->GetParent());
	SetRate(mainframe->GetMonitorRate());

	// 今の所パディングは変わらない。
	monitor->SetPadding(WXTextScreen::DefaultPadding);
	FontChanged();

	// 最初に一回描画を起こす
	wxTimerEvent dummy(timer);
	AddPendingEvent(dummy);
}

// デストラクタ
WXMonitorPanel::~WXMonitorPanel()
{
}

// フォントサイズ変更
void
WXMonitorPanel::FontChanged()
{
	inherited::FontChanged();

	monitor->SetFontSize(font_width, font_height);
}

// タイマーイベント
void
WXMonitorPanel::OnTimer(wxTimerEvent& event)
{
	DoRefresh();
}

// 画面を更新して再描画する。
//
// 通常はタイマーイベントにより更新間隔ごとに呼ばれるが、
// ユーザ操作により画面の更新が必要ならその都度直接これを呼ぶ。
void
WXMonitorPanel::DoRefresh()
{
	bool updated = false;

	// モニタースクリーンを更新して
	monitor->UpdateScreen(screen);
	if (screen.GetBuf() != prevbuf) {
		updated = true;
	}

	// あればビットマップ描画を足して
	if (monitor->UpdateBitmap(bitmap)) {
		updated = true;
	}

	// 必要なら再描画
	if (updated) {
		Refresh();
	}
}

// 画面更新頻度を Hz で設定する。
void
WXMonitorPanel::SetRate(int hz)
{
	timer.Start(1000 / hz);
}


//
// モニターウィンドウ
//

// コンストラクタ
WXMonitorWindow::WXMonitorWindow(wxWindow *parent, const wxString& name,
	Monitor *monitor_)
	: inherited(parent, wxID_ANY, name)
{
	// モニタパネル
	monpanel = new WXMonitorPanel(this, monitor_);
	Fit();
}

// デストラクタ
WXMonitorWindow::~WXMonitorWindow()
{
}


//
// 縦スクロールバー付きモニタウィンドウ
//

// コンストラクタ
WXScrollMonitorWindow::WXScrollMonitorWindow(wxWindow *parent,
	const wxString& name, Monitor *monitor_)
	: inherited(parent, wxID_ANY, name, DEFAULT_STYLE | wxRESIZE_BORDER)
{
	DPRINTF("%s begin\n", __method__);

	// +--------------+---------+
	// | MonitorPanel | VScroll |
	// +--------------+---------+

	// モニタパネル
	monpanel = new WXMonitorPanel(this, monitor_);

	// スクロールバー
	vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL);

	// 自前レイアウト。
	SetMyLayout();

	// ウィンドウサイズを確定させる
	FontChanged();

	// スクロールのために (パネル領域での) MouseWheel イベントもここで
	// 受け取りたい。スクロールバー上のマウスホイール操作はスクロールバーが
	// 処理しているので問題ないが、パネル領域でのマウスホイール操作はここ
	// (wxFrame)ではなくパネルに対して飛んでくる。
	// ここで一緒に処理したほうが楽なので、こちらに回す。
	monpanel->Bind(wxEVT_MOUSEWHEEL,
		&WXScrollMonitorWindow::OnMouseWheel, this);

	// スクロールバーからの位置変更通知
	vscroll->Bind(NONO_EVT_SCROLL, &WXScrollMonitorWindow::OnScroll, this);

	DPRINTF("%s done\n", __method__);
}

// デストラクタ
WXScrollMonitorWindow::~WXScrollMonitorWindow()
{
}

void
WXScrollMonitorWindow::FontChanged()
{
	monpanel->FontChanged();
	vscroll->FontChanged();
	DPRINTF("%s %d\n", __method__, monpanel->GetFontHeight());

	Fit();
}

// 自前レイアウト方式のサイズに関する情報を返す。
bool
WXScrollMonitorWindow::GetMySizeHints(wxSize *newp, wxSize *minp, wxSize *maxp,
	wxSize *incp)
{
	auto psize = monpanel->GetSize();
	auto vsize = vscroll->GetSize();

	// 幅は monpanel 幅 + スクロールバーの幅、高さは monpanel の高さで決まる。
	// monpanel には四辺に Padding があることに注意。
	int x = psize.x + vsize.x;
	int min_y = std::max(vscroll->GetMinSize().y, monpanel->GetScreenHeight(1));
	int new_y = std::max(psize.y, min_y);
	wxSize newsize(x, new_y);
	wxSize minsize(x, min_y);
	wxSize maxsize(x, GetMaxClientSize().y);
	wxSize incsize(0, monpanel->GetFontHeight());

	if (newp) *newp = newsize;
	if (minp) *minp = minsize;
	if (maxp) *maxp = maxsize;
	if (incp) *incp = incsize;
	return true;
}

bool
WXScrollMonitorWindow::Layout()
{
	if (MyLayout() == false) {
		// コントロールを配置。
		wxSize client = GetClientSize();
		const wxSize vsize = vscroll->GetSize();

		int mon_width  = client.x - vsize.x;
		int mon_height = client.y;
		// 最低でも 1px ないと GTK とかに怒られる。今は起きないはず?
		if (__predict_false(mon_width < 1)) {
			mon_width = 1;
		}
		if (__predict_false(mon_height < 1)) {
			mon_height = 1;
		}
#if defined(WINDOW_DEBUG)
		const wxSize win = GetSize();
		const wxSize minwin = GetMinSize();
		printf("%s  WinSize   =(%d,%d) MinWin   =(%d,%d) Shown=%u\n",
			__method__, win.x, win.y, minwin.x, minwin.y, IsShown());
		const wxSize min = GetMinClientSize();
		printf("%s  ClientSize=(%d,%d) MinClient=(%d,%d)\n",
			__method__, client.x, client.y, min.x, min.y);
#endif

		monpanel->SetSize(0, 0, mon_width, mon_height);
		vscroll->SetSize(mon_width, 0, vsize.x, mon_height);

#if defined(WINDOW_DEBUG)
		const wxSize monsize = monpanel->GetSize();
		const wxSize vsize1 = vscroll->GetSize();
		int py = monsize.y - monpanel->GetPaddingY();
		int height = monpanel->GetFontHeight();
		int row = (height != 0) ? (py / height) : 0;
		int res = (height != 0) ? (py % height) : 0;
		printf("%s  mon=(%d,%d) vscroll=(%d,%d), row=%d%s\n", __method__,
			monsize.x, monsize.y, vsize1.x, vsize1.y,
			row, (res == 0 ? "" : "+"));
#endif

		// スクロールバーを再設定。
		// 今の所ヘッダが常に1行ある。
		static int HEADLINES = 1;
		int pos = (int)monpanel->GetUserData();
		int thumbsize = monpanel->GetRow() - HEADLINES;
		int range = monpanel->GetMonitor()->GetMaxHeight() - HEADLINES;
		int pagesize = thumbsize - 1;
		if (pos > range - thumbsize) {
			pos = range - thumbsize;
			DoScroll(pos);
		}
		vscroll->SetScrollParam(pos, thumbsize, range, pagesize);
	}
	return true;
}

// マウスホイールイベント (WXTextScreen 上で起きたやつをこっちに回してある)
void
WXScrollMonitorWindow::OnMouseWheel(wxMouseEvent& event)
{
	// GetWheelRotation() は上(奥)向きに回すと +120、下(手前)に回すと -120。
	// どこの決まりか知らんけど、スクロールバーのほうはホイール1段階で3行
	// スクロールするのでそれに合わせる。
	int pos = (int)monpanel->GetUserData();
	pos = pos - event.GetWheelRotation() / 40;

	int maxpos = vscroll->GetRange() - vscroll->GetThumbSize();
	if (pos < 0)
		pos = 0;
	if (pos > maxpos)
		pos = maxpos;

	DoScroll(pos);
	// スクロールバーの位置を追従
	vscroll->SetThumbPosition(pos);
}

// スクロールバーからの通知イベント
void
WXScrollMonitorWindow::OnScroll(wxScrollEvent& event)
{
	DoScroll(event.GetPosition());
}

// スクロール処理 (OnScroll() と OnMouseWheel() から呼ばれる)
void
WXScrollMonitorWindow::DoScroll(int pos)
{
	// userdata が表示開始位置になっている
	monpanel->SetUserData(pos);
	monpanel->DoRefresh();
}

unix.superglobalmegacorp.com

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