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