Annotation of nono/wx/wxtextscreen.cpp, revision 1.1.1.8

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: }

unix.superglobalmegacorp.com

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