|
|
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:
1.1.1.8 ! root 7: //
! 8: // テキストスクリーンを描画するパネル
! 9: //
! 10:
1.1 root 11: #include "wxtextscreen.h"
1.1.1.8 ! root 12: #include "fontmanager.h"
1.1.1.5 root 13: #include "wxcolor.h"
1.1.1.3 root 14: #include "wxmainframe.h"
1.1.1.5 root 15: #include "sjis.h"
1.1.1.8 ! root 16: PRAGMA_PUSH_WARNINGS
! 17: #include <wx/clipbrd.h>
! 18: PRAGMA_POP_WARNINGS
1.1 root 19:
20: // イベントテーブル
21: wxBEGIN_EVENT_TABLE(WXTextScreen, inherited)
22: EVT_SIZE(WXTextScreen::OnSize)
1.1.1.3 root 23: EVT_TIMER(wxID_ANY, WXTextScreen::OnTimer)
1.1.1.8 ! root 24: EVT_MENU(wxID_COPY, WXTextScreen::OnCopy)
1.1 root 25: wxEND_EVENT_TABLE()
26:
27: // コンストラクタ
1.1.1.6 root 28: WXTextScreen::WXTextScreen(wxWindow *parent, Monitor& monitor_)
1.1 root 29: : inherited(parent)
1.1.1.3 root 30: , monitor(monitor_)
1.1 root 31: {
1.1.1.8 ! root 32: pad_left = DefaultPadding;
! 33: pad_top = DefaultPadding;
! 34: pad_right = DefaultPadding;
! 35: pad_bottom = DefaultPadding;
! 36:
1.1.1.3 root 37: // モニタサイズから初期サイズを決定
1.1.1.6 root 38: nnSize ms = monitor.GetSize();
1.1.1.5 root 39: Init(ms.width, ms.height);
40:
1.1.1.8 ! root 41: FontChanged();
! 42:
! 43: // コンテキストメニューイベントを動的に追加
! 44: // (コンテキストメニューを表示したくない人があとから外せるようにするため)
! 45: ConnectContextMenu();
1.1.1.3 root 46:
47: timer.SetOwner(this);
1.1 root 48:
1.1.1.3 root 49: // 現在の設定値を適用
50: SetRate(gMainFrame->GetMonitorRate());
51:
52: // 最初に一回描画を起こす
1.1.1.8 ! root 53: wxTimerEvent dummy(timer);
1.1.1.3 root 54: AddPendingEvent(dummy);
1.1 root 55: }
56:
57: // デストラクタ
58: WXTextScreen::~WXTextScreen()
59: {
1.1.1.5 root 60: }
61:
62: // (再)初期化
63: void
64: WXTextScreen::Init(int col, int row)
65: {
66: ts.Init(col, row);
67: prevbuf.resize(col * row);
1.1.1.8 ! root 68: InvalidateText();
! 69: }
! 70:
! 71: // テキスト画面全体が次回再描画されるようにする
! 72: void
! 73: WXTextScreen::InvalidateText()
! 74: {
1.1.1.5 root 75: std::fill(prevbuf.begin(), prevbuf.end(), 0xffff);
1.1 root 76: }
77:
1.1.1.3 root 78: // 画面更新頻度を Hz で設定する。
1.1 root 79: void
1.1.1.3 root 80: WXTextScreen::SetRate(int hz)
1.1 root 81: {
1.1.1.3 root 82: timer.Start(1000 / hz);
1.1 root 83: }
84:
85: // フォントサイズ変更 (外部インタフェース)
86: void
1.1.1.8 ! root 87: WXTextScreen::FontChanged()
1.1 root 88: {
1.1.1.8 ! root 89: inherited::FontChanged();
1.1 root 90:
91: // 再描画を強制する。
92: // テキストの桁数行数は変わってないので再確保はしなくていい。
1.1.1.8 ! root 93: InvalidateText();
1.1 root 94:
95: // コントロールの大きさを変更
1.1.1.8 ! root 96: DoSize();
! 97: }
! 98:
! 99: // (自発的な)サイズ変更。
! 100: // ts の行数桁数、font_{width,height}、四方のパディングのいずれかが
! 101: // 変わった場合に呼ぶこと。
! 102: void
! 103: WXTextScreen::DoSize()
! 104: {
! 105: wxSize size = wxSize(
! 106: ts.GetCol() * font_width + pad_left + pad_right,
! 107: ts.GetRow() * font_height + pad_top + pad_bottom);
1.1 root 108: SetClientSize(size);
1.1.1.5 root 109: SetMinClientSize(size);
1.1 root 110: }
111:
112: // サイズ変更イベント
113: void
114: WXTextScreen::OnSize(wxSizeEvent& event)
115: {
116: // Mac ではウィンドウ作成時に 0 以下の値が来ることがある
117: // GTK では 1 で来ることがある
1.1.1.3 root 118: const wxSize& size = event.GetSize();
1.1 root 119: if (size.x <= 1 || size.y <= 1) {
120: return;
121: }
122:
1.1.1.8 ! root 123: // 親クラス
! 124: inherited::OnSize(event);
! 125:
1.1.1.3 root 126: // テキストサイズが変わった時だけテキストバッファを再構成。
127: // (フォントサイズを変えると桁数×行数が変わらず画面サイズが変わる
128: // ということは起きることに注意)
1.1.1.8 ! root 129: int sw = size.x - pad_left - pad_right;
! 130: int sh = size.y - pad_top - pad_bottom;
! 131: if (__predict_false(sw < 0)) {
! 132: sw = 0;
! 133: }
! 134: if (__predict_false(sh < 0)) {
! 135: sh = 0;
! 136: }
! 137: int col = sw / font_width;
! 138: int row = sh / font_height;
1.1.1.3 root 139: if (col != ts.GetCol() || row != ts.GetRow()) {
1.1.1.5 root 140: Init(col, row);
1.1.1.8 ! root 141: } else {
! 142: // テキストバッファを再構成しなくても全域を再描画する必要がある
! 143: InvalidateText();
1.1.1.3 root 144: }
145: }
1.1 root 146:
1.1.1.3 root 147: // タイマーイベント
148: void
149: WXTextScreen::OnTimer(wxTimerEvent& event)
150: {
1.1.1.6 root 151: DoRefresh();
1.1.1.3 root 152: }
1.1 root 153:
1.1.1.6 root 154: // 画面を更新して再描画する。
155: //
156: // 通常はタイマーイベントにより更新間隔ごとに呼ばれるが、
157: // ユーザ操作により画面の更新が必要ならその都度直接これを呼ぶ。
1.1.1.3 root 158: void
1.1.1.6 root 159: WXTextScreen::DoRefresh()
1.1.1.3 root 160: {
1.1.1.6 root 161: // モニタースクリーンを更新して
162: MONITOR_UPDATE(monitor, ts);
163: // 必要なら再描画
1.1.1.5 root 164: if (ts.GetBuf() != prevbuf) {
1.1.1.3 root 165: Refresh();
166: }
1.1 root 167: }
168:
169: // テキストバッファの内容をパネルのバックグラウンドバッファに描画する。
170: void
1.1.1.8 ! root 171: WXTextScreen::Draw()
1.1 root 172: {
1.1.1.5 root 173: auto text = ts.GetBuf().begin();
174: auto prev = prevbuf.begin();
175:
1.1.1.3 root 176: int col = ts.GetCol();
177: int row = ts.GetRow();
1.1.1.5 root 178: uint prevattr = (uint)-1;
179:
1.1.1.8 ! root 180: int py = pad_top;
1.1 root 181: for (int y = 0; y < row; y++, py += font_height) {
1.1.1.8 ! root 182: int px = pad_left;
1.1 root 183: for (int x = 0; x < col; ) {
184: uint16 data = *text;
185: uint attr = (data & 0xff00);
186: uint chr = (data & 0xff);
1.1.1.5 root 187:
188: // 属性(色)を変更
189: if (__predict_false(attr != prevattr)) {
190: switch (attr) {
191: case TA::On:
1.1.1.8 ! root 192: SetTextColor(BGPANEL, UD_BLACK);
1.1.1.5 root 193: break;
1.1.1.7 root 194: case TA::Off:
1.1.1.8 ! root 195: SetTextColor(UD_BLACK, UD_LIGHT_GREY);
1.1.1.7 root 196: break;
1.1.1.5 root 197: case TA::Disable:
198: SetTextColor(UD_GREY, BGPANEL);
199: break;
200: default:
201: ResetTextColor();
202: break;
203: }
204: prevattr = attr;
205: }
206:
207: if (__predict_true(SJIS::IsHankaku(chr))) {
208: // 半角文字
1.1 root 209: if (*prev != data) {
1.1.1.8 ! root 210: DrawChar1(px, py, chr, attr);
! 211:
1.1.1.5 root 212: if (SJIS::IsZenkaku(*prev) && x < col - 1) {
213: // 変更前が全角文字だったら 2 バイト目も消す
1.1.1.4 root 214: prev[1] = 0;
215: }
1.1 root 216: *prev = data;
217: }
1.1.1.5 root 218: prev += 1;
219: text += 1;
220: x += 1;
221: px += font_width * 1;
1.1 root 222: } else {
1.1.1.5 root 223: // 全角文字
1.1.1.6 root 224: // XXX: 最右カラムだったら?
1.1.1.5 root 225: if (*prev != data || prev[1] != text[1]) {
226: uint code = (chr << 8) | (text[1] & 0xff);
1.1.1.8 ! root 227: DrawChar2(px, py, code, attr);
! 228:
1.1.1.5 root 229: if (SJIS::IsASCII(*prev) && SJIS::IsZenkaku(prev[1])
230: && x < col - 2) {
1.1.1.6 root 231: // 変更前が 'Aあ' だったら'あ'の2バイト目も消す
1.1.1.5 root 232: prev[2] = 0;
233: }
234: prev[0] = data;
235: prev[1] = text[1];
1.1 root 236: }
1.1.1.5 root 237: prev += 2;
238: text += 2;
1.1 root 239: x += 2;
240: px += font_width * 2;
241: }
242: }
243: }
244: }
1.1.1.8 ! root 245:
! 246: // パディングサイズ変更。
! 247: // 負数なら変更しない。
! 248: void
! 249: WXTextScreen::SetPadding(int l, int t, int r, int b)
! 250: {
! 251: if (l >= 0) {
! 252: pad_left = l;
! 253: }
! 254: if (t >= 0) {
! 255: pad_top = t;
! 256: }
! 257: if (r >= 0) {
! 258: pad_right = r;
! 259: }
! 260: if (b >= 0) {
! 261: pad_bottom = b;
! 262: }
! 263:
! 264: DoSize();
! 265: }
! 266:
! 267: // クライアント座標 pos をテキスト座標(桁、行)に変換して返す
! 268: // パディング(上と左)領域では戻り値が負数になることに注意。
! 269: wxPoint
! 270: WXTextScreen::GetTextPosition(const wxPoint& pos) const
! 271: {
! 272: wxPoint tpos;
! 273:
! 274: tpos.x = (pos.x - pad_left) / GetFontWidth();
! 275: tpos.y = (pos.y - pad_top) / GetFontHeight();
! 276:
! 277: return tpos;
! 278: }
! 279:
! 280: // コンテキストメニューイベントを接続
! 281: void
! 282: WXTextScreen::ConnectContextMenu()
! 283: {
! 284: Connect(wxEVT_CONTEXT_MENU,
! 285: wxContextMenuEventHandler(WXTextScreen::OnContextMenu), NULL, this);
! 286: }
! 287:
! 288: // コンテキストメニューイベントを接続解除
! 289: void
! 290: WXTextScreen::DisconnectContextMenu()
! 291: {
! 292: Disconnect(wxEVT_CONTEXT_MENU,
! 293: wxContextMenuEventHandler(WXTextScreen::OnContextMenu), NULL, this);
! 294: }
! 295:
! 296: // コンテキストメニューイベント
! 297: void
! 298: WXTextScreen::OnContextMenu(wxContextMenuEvent& ev)
! 299: {
! 300: wxMenu *menu = new wxMenu();
! 301:
! 302: menu->Append(wxID_COPY, _T("&Copy"));
! 303:
! 304: PopupMenu(menu);
! 305: }
! 306:
! 307: // コンテキストメニュー「コピー」イベント
! 308: void
! 309: WXTextScreen::OnCopy(wxCommandEvent& ev)
! 310: {
! 311: int col = ts.GetCol();
! 312: int row = ts.GetRow();
! 313:
! 314: const std::vector<uint16>& src = ts.GetBuf();
! 315: auto s = src.begin();
! 316:
! 317: std::vector<uint8> buf;
! 318:
! 319: for (int y = 0; y < row; y++) {
! 320: for (int x = 0; x < col; x++) {
! 321: // 下位バイトだけ取り出すと SJIS 文字列になる
! 322: buf.push_back((*s++) & 0xff);
! 323: }
! 324: // 末尾の空白を取り除く
! 325: while (buf.back() == ' ') {
! 326: buf.pop_back();
! 327: }
! 328: buf.push_back('\n');
! 329: }
! 330: buf.push_back('\0');
! 331:
! 332: #if 0
! 333: // Shift_JIS を UTF-8 に変換
! 334: // XXX 今はまだ Shift_JIS 出してる人がいない
! 335: #else
! 336: wxString text(buf.data());
! 337: #endif
! 338:
! 339: // クリップボードへ転送
! 340: if (wxTheClipboard->Open()) {
! 341: auto obj = new wxTextDataObject(text);
! 342: wxTheClipboard->UsePrimarySelection(false);
! 343: wxTheClipboard->SetData(obj);
! 344: wxTheClipboard->Close();
! 345: }
! 346: #if defined(__WXGTK__)
! 347: // wxGTK なら X11 のクリップボードにも同じものを入れる。
! 348: // UsePrimarySelection(false) だと GTK のクリップボードに入り、
! 349: // UsePrimarySelection(true) だと X11 のクリップボードに入る。
! 350: // X11 用語では「カットバッファ/セレクション」というらしく、
! 351: // 実は8つくらいあってその1つ目なので PrimarySelection らしい。
! 352: if (wxTheClipboard->Open()) {
! 353: auto obj = new wxTextDataObject(text);
! 354: wxTheClipboard->UsePrimarySelection(true);
! 355: wxTheClipboard->SetData(obj);
! 356: wxTheClipboard->Close();
! 357: }
! 358: #endif
! 359: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.