1 #include "CtrlCore.h"
2
3 namespace Upp {
4
5 #define LLOG(x) // DLOG(x)
6
7 Ptr<Ctrl> Ctrl::focusCtrl;
8 Ptr<Ctrl> Ctrl::focusCtrlWnd;
9 Ptr<Ctrl> Ctrl::lastActiveWnd;
10 Ptr<Ctrl> Ctrl::caretCtrl;
11 Rect Ctrl::caretRect;
12 bool Ctrl::ignorekeyup;
13
14 Ptr<Ctrl> Ctrl::defferedSetFocus;
15 Vector< Ptr<Ctrl> > Ctrl::defferedChildLostFocus;
16
17
18 static bool s_hotkey;
19
RefreshAccessKeys()20 void Ctrl::RefreshAccessKeys()
21 {
22 GuiLock __;
23 if(GetAccessKeys())
24 Refresh();
25 for(Ctrl *ctrl = GetFirstChild(); ctrl; ctrl = ctrl->GetNext())
26 ctrl->RefreshAccessKeys();
27 }
28
RefreshAccessKeysDo(bool vis)29 void Ctrl::RefreshAccessKeysDo(bool vis)
30 {
31 GuiLock __;
32 if(GUI_AltAccessKeys() && vis != akv) {
33 akv = vis;
34 RefreshAccessKeys();
35 }
36 }
37
DispatchKey(dword keycode,int count)38 bool Ctrl::DispatchKey(dword keycode, int count)
39 {
40 GuiLock __;
41 if(GUI_AltAccessKeys()) {
42 bool alt = GetAlt();
43 Ctrl *c = GetActiveCtrl();
44 if(c)
45 c->RefreshAccessKeysDo(alt);
46 }
47 // RLOGBLOCK("Ctrl::DispatchKey");
48 // RLOG("DispatchKey: focusCtrl = " << FormatIntHex((int)~focusCtrl) << ", wnd = " << FormatIntHex((int)~focusCtrlWnd) << ")");
49 LLOG("DispatchKey " << keycode << " (0x" << Sprintf("%08x", keycode)
50 << ", " << GetKeyDesc(keycode) << "), count:" << count
51 << " focusCtrl:" << UPP::Name(focusCtrl) << " focusCtrlWnd:" << UPP::Name(focusCtrlWnd));
52 if((keycode & K_KEYUP) && ignorekeyup)
53 {
54 ignorekeyup = false;
55 return true;
56 }
57 for(int i = 0; i < keyhook().GetCount(); i++)
58 if((*keyhook()[i])(focusCtrl, keycode, count))
59 return true;
60 dword k = keycode;
61 word l = LOWORD(keycode);
62 if(!(k & K_DELTA) && l >= 32 && l != 127 && GetDefaultCharset() != CHARSET_UNICODE)
63 k = MAKELONG((word)FromUnicode(l, CHARSET_DEFAULT), HIWORD(keycode));
64 if(!focusCtrl)
65 return false;
66 Ptr<Ctrl> p = focusCtrl;
67 if(Ini::user_log) {
68 String kl;
69 dword k = keycode;
70 const char *l = "";
71 if(k < 65536) {
72 kl << "CHAR \'" << ToUtf8((wchar)keycode) << "\' (" << keycode << ')';
73 l = " ";
74 }
75 else {
76 kl << "KEY";
77 if(k & K_KEYUP) {
78 kl << "UP";
79 k &= ~K_KEYUP;
80 l = " ";
81 }
82 kl << " " << GetKeyDesc(k);
83 }
84 USRLOG(l << kl);
85 }
86 for(;;) {
87 LLOG("Trying to DispatchKey: p = " << Desc(p));
88 if(p->IsEnabled() && p->Key(p->unicode ? keycode : k, count))
89 {
90 LLOG("Ctrl::DispatchKey(" << FormatIntHex(keycode) << ", " << GetKeyDesc(keycode)
91 << "): eaten in " << Desc(p));
92 if(Ini::user_log)
93 USRLOG(" -> " << Desc(p));
94 eventCtrl = focusCtrl;
95 return true;
96 }
97 s_hotkey = true;
98 if(!p->GetParent()) {
99 if(p->HotKey(keycode)) {
100 eventCtrl = focusCtrl;
101 return true;
102 }
103 return false;
104 }
105 p = p->GetParent();
106 }
107
108 USRLOG(" key was ignored");
109
110 return false;
111 }
112
HotKey(dword key)113 bool Ctrl::HotKey(dword key)
114 {
115 GuiLock __;
116 LLOG("HotKey " << GetKeyDesc(key) << " at " << Name(this));
117 if(!IsEnabled() || !IsVisible()) return false;
118 for(Ptr<Ctrl> ctrl = firstchild; ctrl; ctrl = ctrl->next)
119 {
120 LLOG("Trying HotKey " << GetKeyDesc(key) << " at " << Name(ctrl));
121 if(ctrl->IsOpen() && ctrl->IsVisible() && ctrl->IsEnabled() && ctrl->HotKey(key))
122 {
123 if(Ini::user_log && s_hotkey) {
124 USRLOG(" HOT-> " << UPP::Name(ctrl));
125 s_hotkey = false;
126 }
127 LLOG("ACCEPTED");
128 return true;
129 }
130 }
131 return false;
132 }
133
DoDeactivate(Ptr<Ctrl> pfocusCtrl,Ptr<Ctrl> nfocusCtrl)134 void Ctrl::DoDeactivate(Ptr<Ctrl> pfocusCtrl, Ptr<Ctrl> nfocusCtrl)
135 {
136 GuiLock __;
137 if(pfocusCtrl) {
138 Ptr<Ctrl> ptop = pfocusCtrl->GetTopCtrl();
139 Ctrl *ntop = nfocusCtrl ? nfocusCtrl->GetTopCtrl() : NULL;
140 if(ntop != ptop && !ptop->destroying) {
141 LLOG("DoDeactivate " << UPP::Name(ptop) << " in favor of " << UPP::Name(ntop));
142 ptop->DeactivateBy(ntop);
143 ptop->Deactivate();
144 if(ptop)
145 ptop->StateH(DEACTIVATE);
146 if(ptop)
147 ptop->RefreshAccessKeysDo(false);
148 }
149 }
150 }
151
DoKillFocus(Ptr<Ctrl> pfocusCtrl,Ptr<Ctrl> nfocusCtrl)152 void Ctrl::DoKillFocus(Ptr<Ctrl> pfocusCtrl, Ptr<Ctrl> nfocusCtrl)
153 {
154 GuiLock __;
155 if(pfocusCtrl && !pfocusCtrl->destroying) {
156 pfocusCtrl->StateH(FOCUS);
157 LLOG("LostFocus: " << Name(pfocusCtrl));
158 pfocusCtrl->LostFocus();
159 }
160 if(pfocusCtrl && pfocusCtrl->parent && !pfocusCtrl->parent->destroying)
161 pfocusCtrl->parent->ChildLostFocus();
162 SyncCaret();
163 }
164
DoSetFocus(Ptr<Ctrl> pfocusCtrl,Ptr<Ctrl> nfocusCtrl,bool activate)165 void Ctrl::DoSetFocus(Ptr<Ctrl> pfocusCtrl, Ptr<Ctrl> nfocusCtrl, bool activate)
166 {
167 GuiLock __;
168 if(activate && focusCtrl == nfocusCtrl && nfocusCtrl) {
169 Ctrl *top = nfocusCtrl->GetTopCtrl();
170 if((!pfocusCtrl || pfocusCtrl->GetTopCtrl() != top) && !top->destroying) {
171 top->StateH(ACTIVATE);
172 top->Activate();
173 top->RefreshAccessKeysDo(top->VisibleAccessKeys());
174 }
175 }
176
177 if(focusCtrl == nfocusCtrl && nfocusCtrl && !nfocusCtrl->destroying) {
178 nfocusCtrl->GotFocus();
179 nfocusCtrl->StateH(FOCUS);
180 }
181 if(focusCtrl == nfocusCtrl && nfocusCtrl && nfocusCtrl->parent &&
182 !nfocusCtrl->parent->destroying)
183 nfocusCtrl->parent->ChildGotFocus();
184
185 SyncCaret();
186 }
187
188 /*
189 bool Ctrl::SetFocus0(bool activate)
190 {
191 GuiLock __;
192 if(IsUsrLog())
193 UsrLogT(6, String().Cat() << "SETFOCUS " << Desc(this));
194 LLOG("Ctrl::SetFocus " << Desc(this));
195 LLOG("focusCtrlWnd " << UPP::Name(focusCtrlWnd));
196 LLOG("Ctrl::SetFocus0 -> deferredSetFocus = NULL; was: " << UPP::Name(defferedSetFocus));
197 defferedSetFocus = NULL;
198 if(focusCtrl == this) return true;
199 if(!IsOpen() || !IsEnabled() || !IsVisible()) return false;
200 Ptr<Ctrl> pfocusCtrl = focusCtrl;
201 Ptr<Ctrl> topwindow = GetTopWindow();
202 Ptr<Ctrl> topctrl = GetTopCtrl();
203 Ptr<Ctrl> _this = this;
204 if(!topwindow) topwindow = topctrl;
205 LLOG("SetFocus -> SetWndFocus: topwindow = " << UPP::Name(topwindow) << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd));
206 if(!topwindow->HasWndFocus() && !topwindow->SetWndFocus()) return false;// cxl 31.1.2004
207 if(activate) // Dolik/fudadmin 2011-5-1
208 topctrl->SetWndForeground(); // cxl 2007-4-27
209 LLOG("SetFocus -> focusCtrl = this: " << FormatIntHex(this) << ", _this = " << FormatIntHex(~_this) << ", " << UPP::Name(_this));
210 focusCtrl = _this;
211 focusCtrlWnd = topwindow;
212 DoKillFocus(pfocusCtrl, _this);
213 LLOG("SetFocus 2");
214 DoDeactivate(pfocusCtrl, _this);
215 DoSetFocus(pfocusCtrl, _this, activate);
216 if(topwindow)
217 lastActiveWnd = topwindow;
218 return true;
219 }
220 */
221
SetFocus0(bool activate)222 bool Ctrl::SetFocus0(bool activate)
223 {
224 GuiLock __;
225 USRLOG(" SETFOCUS " << Desc(this));
226 LLOG("Ctrl::SetFocus " << Desc(this) << ", activate: " << activate);
227 LLOG("focusCtrlWnd " << UPP::Name(focusCtrlWnd));
228 LLOG("Ctrl::SetFocus0 -> deferredSetFocus = NULL; was: " << UPP::Name(defferedSetFocus));
229 defferedSetFocus = NULL;
230 if(focusCtrl == this) return true;
231 if(!IsOpen() || !IsEnabled() || !IsVisible()) return false;
232 Ptr<Ctrl> pfocusCtrl = focusCtrl;
233 Ptr<Ctrl> topwindow = GetTopWindow();
234 Ptr<Ctrl> topctrl = GetTopCtrl();
235 Ptr<Ctrl> _this = this;
236 #ifdef PLATFORM_COCOA0 // hopefully no needed
237 topwindow = topctrl;
238 #else
239 if(!topwindow) topwindow = topctrl;
240 #endif
241 LLOG("SetFocus -> SetWndFocus: topwindow = " << UPP::Name(topwindow) << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd));
242 if(!topwindow->HasWndFocus() && !topwindow->SetWndFocus()) return false;// cxl 31.1.2004
243 #ifdef PLATFORM_OSX11 // ugly temporary hack - popups not behaving right in MacOS
244 // before 2012-9-2 was #ifdef GUI_X11, but that caused issues in most linux distros (cxl)
245 // as parent window of popup always manages focus/keyboard for popup in X11
246 if(activate) // Dolik/fudadmin 2011-5-1
247 topctrl->SetWndForeground();
248 #else
249 topwindow->SetWndForeground(); // cxl 2007-4-27
250 #endif
251 LLOG("SetFocus -> focusCtrl = this: " << FormatIntHex(this) << ", _this = " << FormatIntHex(~_this) << ", " << UPP::Name(_this));
252 focusCtrl = _this;
253 focusCtrlWnd = topwindow;
254 DoKillFocus(pfocusCtrl, _this);
255 LLOG("SetFocus 2");
256 DoDeactivate(pfocusCtrl, _this);
257 DoSetFocus(pfocusCtrl, _this, activate);
258 if(topwindow)
259 lastActiveWnd = topwindow;
260 return true;
261 }
262
SetFocus()263 bool Ctrl::SetFocus()
264 {
265 GuiLock __;
266 LLOG("Ctrl::SetFocus(" << Name() << ")");
267 return SetFocus0(true);
268 }
269
ActivateWnd()270 void Ctrl::ActivateWnd()
271 {
272 GuiLock __;
273 // notification, don't set physical focus here
274 LLOG("ActivateWnd " << Name());
275 Ptr<Ctrl> nfocusCtrl = this;
276 Ptr<Ctrl> pfocusCtrl = focusCtrl;
277 LLOG("About to set focus: " << UPP::Name(nfocusCtrl));
278 DoDeactivate(pfocusCtrl, nfocusCtrl);
279 focusCtrl = nfocusCtrl;
280 focusCtrlWnd = this;
281 DoKillFocus(pfocusCtrl, nfocusCtrl);
282 DoSetFocus(pfocusCtrl, nfocusCtrl, true);
283 LLOG("Focus: " << UPP::Name(focusCtrl) << " FocusWnd:" << UPP::Name(focusCtrlWnd));
284 }
285
SetFocusWnd()286 void Ctrl::SetFocusWnd()
287 {
288 GuiLock __;
289 // notification, don't set host platform focus here
290 LLOG("Ctrl::SetFocusWnd");
291 if(focusCtrlWnd != this) {
292 LLOG("Ctrl::SetFocusWnd->ActivateWnd");
293 ActivateWnd();
294 }
295 }
296
KillFocusWnd()297 void Ctrl::KillFocusWnd()
298 {
299 GuiLock __;
300 // notification, don't set host platform focus here
301 LLOG("KillFocusWnd " << Name());
302 if(this == ~focusCtrlWnd) {
303 Ptr<Ctrl> pfocusCtrl = focusCtrl;
304 DoDeactivate(pfocusCtrl, NULL);
305 focusCtrl = focusCtrlWnd = NULL;
306 DoKillFocus(pfocusCtrl, NULL);
307 }
308 }
309
ClickActivateWnd()310 void Ctrl::ClickActivateWnd()
311 {
312 GuiLock __;
313 LLOG("Ctrl::ClickActivateWnd " << Name(this));
314 if(this == ~focusCtrlWnd && focusCtrl && focusCtrl->GetTopCtrl() != this) {
315 LLOG("Ctrl::ClickActivateWnd -> ActivateWnd");
316 ActivateWnd();
317 }
318 }
319
DefferedFocusSync()320 void Ctrl::DefferedFocusSync()
321 {
322 GuiLock __;
323 while(defferedChildLostFocus.GetCount() || defferedSetFocus) {
324 LLOG("Ctrl::DeferredFocusSync, defferedSetFocus = " << UPP::Name(defferedSetFocus));
325 Vector< Ptr<Ctrl> > b = pick(defferedChildLostFocus);
326 defferedChildLostFocus.Clear();
327 for(int i = 0; i < b.GetCount(); i++)
328 if(b[i]) {
329 LLOG("Ctrl::DeferredFocusSync -> ChildLostFocus " << UPP::Name(b[i]));
330 b[i]->ChildLostFocus();
331 }
332 if(defferedSetFocus) {
333 LLOG("Ctrl::DeferredFocusSync -> SetFocus " << UPP::Name(defferedSetFocus));
334 defferedSetFocus->SetFocus();
335 }
336 defferedSetFocus = NULL;
337 SyncCaret();
338 }
339 }
340
RefreshCaret()341 void Ctrl::RefreshCaret()
342 {
343 GuiLock __;
344 if(caretCtrl)
345 caretCtrl->Refresh(caretCtrl->caretx, caretCtrl->carety,
346 caretCtrl->caretcx, caretCtrl->caretcy);
347 }
348
GetActiveWindow()349 Ctrl *Ctrl::GetActiveWindow()
350 {
351 GuiLock __;
352 Ctrl *q = GetActiveCtrl();
353 return q ? q->GetTopWindow() : NULL;
354 }
355
HasFocusDeep() const356 bool Ctrl::HasFocusDeep() const
357 {
358 GuiLock __;
359 if(HasFocus() || HasChildDeep(FocusCtrl())) return true;
360 Ctrl *a = GetActiveCtrl();
361 if(!a || !a->IsPopUp()) return false;
362 a = a->GetOwnerCtrl();
363 return a && HasChildDeep(a);
364 }
365
366 Tuple<dword, const char *> KeyNames__[ ] = {
367 { K_A, tt_("key\vA") }, { K_B, tt_("key\vB") }, { K_C, tt_("key\vC") }, { K_D, tt_("key\vD") },
368 { K_E, tt_("key\vE") }, { K_F, tt_("key\vF") }, { K_G, tt_("key\vG") }, { K_H, tt_("key\vH") },
369 { K_I, tt_("key\vI") }, { K_J, tt_("key\vJ") }, { K_K, tt_("key\vK") }, { K_L, tt_("key\vL") },
370 { K_M, tt_("key\vM") }, { K_N, tt_("key\vN") }, { K_O, tt_("key\vO") }, { K_P, tt_("key\vP") },
371 { K_Q, tt_("key\vQ") }, { K_R, tt_("key\vR") }, { K_S, tt_("key\vS") }, { K_T, tt_("key\vT") },
372 { K_U, tt_("key\vU") }, { K_V, tt_("key\vV") }, { K_W, tt_("key\vW") }, { K_X, tt_("key\vX") },
373 { K_Y, tt_("key\vY") }, { K_Z, tt_("key\vZ") }, { K_0, tt_("key\v0") }, { K_1, tt_("key\v1") },
374 { K_2, tt_("key\v2") }, { K_3, tt_("key\v3") }, { K_4, tt_("key\v4") }, { K_5, tt_("key\v5") },
375 { K_6, tt_("key\v6") }, { K_7, tt_("key\v7") }, { K_8, tt_("key\v8") }, { K_9, tt_("key\v9") },
376 { K_F1, tt_("key\vF1") }, { K_F2, tt_("key\vF2") }, { K_F3, tt_("key\vF3") }, { K_F4, tt_("key\vF4") },
377 { K_F5, tt_("key\vF5") }, { K_F6, tt_("key\vF6") }, { K_F7, tt_("key\vF7") }, { K_F8, tt_("key\vF8") },
378 { K_F9, tt_("key\vF9") }, { K_F10, tt_("key\vF10") }, { K_F11, tt_("key\vF11") }, { K_F12, tt_("key\vF12") },
379 { K_TAB, tt_("key\vTab") }, { K_SPACE, tt_("key\vSpace") },
380 { K_RETURN, tt_("key\vEnter") }, { K_BACKSPACE, tt_("key\vBackspace") },
381 { K_CAPSLOCK, tt_("key\vCaps Lock") }, { K_ESCAPE, tt_("key\vEsc") },
382 { K_PAGEUP, tt_("key\vPage Up") }, { K_PAGEDOWN, tt_("key\vPage Down") },
383 { K_END, tt_("key\vEnd") }, { K_HOME, tt_("key\vHome") },
384 { K_LEFT, tt_("key\vLeft") }, { K_UP, tt_("key\vUp") },
385 { K_RIGHT, tt_("key\vRight") }, { K_DOWN, tt_("key\vDown") },
386 { K_INSERT, tt_("key\vInsert") }, { K_DELETE, tt_("key\vDelete") },{ K_BREAK, tt_("key\vBreak") },
387 { K_MULTIPLY, tt_("key\vNum[*]") }, { K_ADD, tt_("key\vNum[+]") }, { K_SUBTRACT, tt_("key\vNum[-]") }, { K_DIVIDE, tt_("key\vNum[/]") },
388 { K_ALT_KEY, tt_("key\vAlt") }, { K_SHIFT_KEY, tt_("key\vShift") }, { K_CTRL_KEY, tt_("key\vCtrl") },
389 { K_PLUS, tt_("key\v[+]") }, { K_MINUS, tt_("key\v[-]") }, { K_COMMA, tt_("key\v[,]") }, { K_PERIOD, tt_("key\v[.]") },
390 { K_SEMICOLON, tt_("key\v[;]") }, { K_SLASH, tt_("key\v[/]") }, { K_GRAVE, tt_("key\v[`]") }, { K_LBRACKET, tt_("key\v[[]") },
391 { K_BACKSLASH, tt_("key\v[\\]") }, { K_RBRACKET, tt_("key\v[]]") }, { K_QUOTEDBL, tt_("key\v[']") },
392 #ifdef PLATFORM_COCOA
393 { K_OPTION_KEY, tt_("key\vOption") },
394 #endif
395 { IK_DBL_CLICK, tt_("key\v[double-click]") },
396 { 0, NULL }
397 };
398
GetKeyDesc(dword key)399 String GetKeyDesc(dword key)
400 {
401 String desc;
402 // key &= 0xFFFF;
403
404 if(key == 0)
405 return desc;
406 // TODO: Cocoa graphics https://tech.karbassi.com/2009/05/27/command-option-shift-symbols-in-unicode/
407 #ifdef PLATFORM_COCOA
408 if(key & K_KEYUP) desc << t_("key\vUP ");
409 if(key & K_CTRL) desc << t_("key\v⌘");
410 if(key & K_ALT) desc << t_("key\v⌃");
411 if(key & K_SHIFT) desc << t_("key\v⇧");
412 if(key & K_OPTION) desc << t_("key\v⌥");
413 key &= ~(K_CTRL | K_ALT | K_SHIFT | K_KEYUP | K_OPTION);
414 #else
415 if(key & K_KEYUP) desc << t_("key\vUP ");
416 if(key & K_CTRL) desc << t_("key\vCtrl+");
417 if(key & K_ALT) desc << t_("key\vAlt+");
418 if(key & K_SHIFT) desc << t_("key\vShift+");
419 key &= ~(K_CTRL | K_ALT | K_SHIFT | K_KEYUP);
420 #endif
421
422 #ifdef PLATFORM_COCOA
423 key &= ~(K_OPTION);
424 #endif
425 if(key < K_DELTA && key > 32 && key != K_DELETE)
426 return desc + ToUtf8((wchar)key);
427 if(key >= K_NUMPAD0 && key <= K_NUMPAD9)
428 desc << "Num " << (char)(key - K_NUMPAD0 + '0');
429 else {
430 for(int i = 0; KeyNames__[i].a; i++)
431 if(KeyNames__[i].a == key) {
432 desc << GetLngString(KeyNames__[i].b);
433 return desc;
434 }
435 desc << Format("%04x", (int)key);
436 }
437 return desc;
438 }
439
440 }
441