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