1 struct TSCanvas : public wxScrolledWindow {
2     MyFrame *frame;
3     Document *doc;
4 
5     int mousewheelaccum;
6     bool lastrmbwaswithctrl;
7 
8     wxPoint lastmousepos;
9 
10     TSCanvas(MyFrame *fr, wxWindow *parent, const wxSize &size = wxDefaultSize)
frameTSCanvas11         : frame(fr),
12           wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, size,
13                            wxScrolledWindowStyle | wxWANTS_CHARS),
14           mousewheelaccum(0),
15           doc(nullptr),
16           lastrmbwaswithctrl(false) {
17         SetBackgroundStyle(wxBG_STYLE_PAINT);
18         SetBackgroundColour(*wxWHITE);
19         DisableKeyboardScrolling();
20         // Without this, ScrolledWindow does its own scrolling upon mousewheel events, which
21         // interferes with our own.
22         EnableScrolling(false, false);
23     }
24 
~TSCanvasTSCanvas25     ~TSCanvas() {
26         DELETEP(doc);
27         frame = nullptr;
28     }
29 
OnPaintTSCanvas30     void OnPaint(wxPaintEvent &event) {
31         #ifdef __WXMAC__
32             wxPaintDC dc(this);
33         #else
34             auto sz = GetClientSize();
35             wxBitmap buffer(sz.GetX(), sz.GetY(), 24);
36             wxBufferedPaintDC dc(this, buffer);
37         #endif
38         // DoPrepareDC(dc);
39         doc->Draw(dc);
40     };
41 
UpdateHoverTSCanvas42     void UpdateHover(int mx, int my, wxDC &dc) {
43         int x, y;
44         CalcUnscrolledPosition(mx, my, &x, &y);
45         DoPrepareDC(dc);
46         doc->Hover(x / doc->currentviewscale, y / doc->currentviewscale, dc);
47     }
48 
OnMotionTSCanvas49     void OnMotion(wxMouseEvent &me) {
50         wxClientDC dc(this);
51         UpdateHover(me.GetX(), me.GetY(), dc);
52         if (me.LeftIsDown() || me.RightIsDown()) {
53             doc->Drag(dc);
54         } else if (me.MiddleIsDown()) {
55             wxPoint p = me.GetPosition() - lastmousepos;
56             CursorScroll(-p.x, -p.y);
57         }
58 
59         lastmousepos = me.GetPosition();
60     }
61 
SelectClickTSCanvas62     void SelectClick(int mx, int my, bool right, int isctrlshift) {
63         if (mx < 0 || my < 0)
64             return;  // for some reason, using just the "menu" key sends a right-click at (-1, -1)
65         wxClientDC dc(this);
66         UpdateHover(mx, my, dc);
67         doc->Select(dc, right, isctrlshift);
68     }
69 
OnLeftDownTSCanvas70     void OnLeftDown(wxMouseEvent &me) {
71         #ifndef __WXMSW__
72         // seems to not want to give the sw focus otherwise (thinks its already in focus
73         // when its not?)
74         if (frame->filter) frame->filter->SetFocus();
75         #endif
76         SetFocus();
77         if (me.ShiftDown())
78             OnMotion(me);
79         else
80             SelectClick(me.GetX(), me.GetY(), false, me.CmdDown() + me.AltDown() * 2);
81     }
82 
OnLeftUpTSCanvas83     void OnLeftUp(wxMouseEvent &me) {
84         if (me.CmdDown() || me.AltDown()) doc->SelectUp();
85     }
86 
OnRightDownTSCanvas87     void OnRightDown(wxMouseEvent &me) {
88         SetFocus();
89         SelectClick(me.GetX(), me.GetY(), true, 0);
90         lastrmbwaswithctrl = me.CmdDown();
91         #ifndef __WXMSW__
92         me.Skip();  // otherwise EVT_CONTEXT_MENU won't be triggered?
93         #endif
94     }
95 
OnLeftDoubleClickTSCanvas96     void OnLeftDoubleClick(wxMouseEvent &me) {
97         wxClientDC dc(this);
98         UpdateHover(me.GetX(), me.GetY(), dc);
99         Status(doc->DoubleClick(dc));
100     }
101 
OnKeyDownTSCanvas102     void OnKeyDown(wxKeyEvent &ce) { ce.Skip(); }
OnCharTSCanvas103     void OnChar(wxKeyEvent &ce) {
104         /*
105         if (sys->insidefiledialog)
106         {
107             ce.Skip();
108             return;
109         }
110         */
111 
112         // Without this check, Alt+F (keyboard menu nav) Alt+1..6 (style changes), Alt+cursor
113         // (scrolling) don't work.
114         // The 128 makes sure unicode entry on e.g. Polish keyboards still works.
115         // (on Linux in particular).
116         if ((ce.GetModifiers() == wxMOD_ALT) && (ce.GetUnicodeKey() < 128)) {
117             ce.Skip();
118             return;
119         }
120 
121         wxClientDC dc(this);
122         DoPrepareDC(dc);
123         bool unprocessed = false;
124         Status(doc->Key(dc, ce.GetUnicodeKey(), ce.GetKeyCode(), ce.AltDown(), ce.CmdDown(),
125                         ce.ShiftDown(), unprocessed));
126         if (unprocessed) ce.Skip();
127     }
128 
OnMouseWheelTSCanvas129     void OnMouseWheel(wxMouseEvent &me) {
130         bool ctrl = me.CmdDown();
131         if (sys->zoomscroll) ctrl = !ctrl;
132         wxClientDC dc(this);
133         if (me.AltDown() || ctrl || me.ShiftDown()) {
134             mousewheelaccum += me.GetWheelRotation();
135             int steps = mousewheelaccum / me.GetWheelDelta();
136             if (!steps) return;
137             mousewheelaccum -= steps * me.GetWheelDelta();
138 
139             UpdateHover(me.GetX(), me.GetY(), dc);
140             Status(doc->Wheel(dc, steps, me.AltDown(), ctrl, me.ShiftDown()));
141         } else if (me.GetWheelAxis()) {
142             CursorScroll(me.GetWheelRotation() * g_scrollratewheel, 0);
143             UpdateHover(me.GetX(), me.GetY(), dc);
144         } else {
145             CursorScroll(0, -me.GetWheelRotation() * g_scrollratewheel);
146             UpdateHover(me.GetX(), me.GetY(), dc);
147         }
148     }
149 
OnSizeTSCanvas150     void OnSize(wxSizeEvent &se) { doc->Refresh(); }
OnContextMenuClickTSCanvas151     void OnContextMenuClick(wxContextMenuEvent &cme) {
152         if (lastrmbwaswithctrl) {
153             wxMenu *tagmenu = new wxMenu();
154             doc->RecreateTagMenu(*tagmenu);
155             PopupMenu(tagmenu);
156             delete tagmenu;
157         } else {
158             PopupMenu(frame->editmenupopup);
159         }
160     }
161 
CursorScrollTSCanvas162     void CursorScroll(int dx, int dy) {
163         int x, y;
164         GetViewStart(&x, &y);
165         x += dx;
166         y += dy;
167         // EnableScrolling(true, true);
168         Scroll(x, y);
169         // EnableScrolling(false, false);
170     }
171 
172     void Status(const wxChar *msg = nullptr) {
173         if (frame->GetStatusBar() && (!msg || *msg))
174             frame->SetStatusText(msg ? msg : L"", 0);
175     }
176 
177     DECLARE_EVENT_TABLE()
178 };
179