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