Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Windows.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Frameworks_Led_Platform_Windows_h_
5#define _Stroika_Frameworks_Led_Platform_Windows_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include <Windows.h>
10
14#include "Stroika/Foundation/Characters/LineEndings.h"
19#include "Stroika/Frameworks/Led/TextInteractor.h"
20
21/*
22@MODULE: Led_Win32
23@DESCRIPTION:
24 <p>Led_Win32 is a module providing a an interface between the Win32 operating system/platform and
25 the Led class library. It is not class-library specific. It only depends on the Win32 API.
26 To get support specifically tuned to MFC - for example - see @'Led_MFC'.</p>
27 */
28
29namespace Stroika::Frameworks::Led::Platform {
30
31 /*
32 **************** Windows Specific configuration variables **************
33 */
34
35/*
36 @CONFIGVAR: qSupportWindowsSDKCallbacks
37 @DESCRIPTION: <p>This might be wanted if you have code which currently depends on these
38 callbacks, but its probably better, and more portable (across platforms)
39 to just override the appropriate Led method. And if you do that, there
40 is no reason to pay the performance cost of sending unheeded messages.</p>
41 <p>Turn OFF by default - LGP 970710</p>
42 */
43#ifndef qSupportWindowsSDKCallbacks
44#define qSupportWindowsSDKCallbacks 0
45#endif
46
47/*
48 @CONFIGVAR: qSupportFunnyMSPageUpDownAdjustSelectionBehavior
49 @DESCRIPTION: <p>OBSOLETE as of Led 3.1a9. Use @'Led_Win32_Helper<BASE_INTERACTOR>::SetFunnyMSPageUpDownAdjustSelectionBehavior' instead.</p>
50 */
51#ifdef qSupportFunnyMSPageUpDownAdjustSelectionBehavior
52#error "This flag is OBSOLETE. Use Led_Win32_Helper<BASE_INTERACTOR>::SetFunnyMSPageUpDownAdjustSelectionBehavior instead".
53#endif
54
55/*
56 @CONFIGVAR: qScrollTextDuringThumbTracking
57 @DESCRIPTION: <p>On Windows many applications scroll their content as the thumb is tracking.
58 This CAN make thumb movement slower, and may be undesriable for other reasons</p>
59 <p>But its common enuf now that I make this behavior ON by default</p>
60 */
61#ifndef qScrollTextDuringThumbTracking
62#define qScrollTextDuringThumbTracking 1
63#endif
64
65/*
66 @CONFIGVAR: qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug
67 @DESCRIPTION: <p>On Windows 2000, you SUPPOSEDLY can now get UNICODE characters from the new IME.
68 However - in practice - unless you build a so-called UNICODE application (really this means call RegisterClassW
69 rather than RegisterClassA).
70 Then the Win32 API IsWindowUnicode () returns TRUE or FALSE according to which way
71 you registered your window class.
72 Then the IME decides whether to hand your app UNICODE characters (WM_CHAR/WM_IME_CHAR) based on IsWindowUnicode () API.</p>
73 <p>However - UNICODE applications (so-called) cannot run under Win98 and earlier. For many (most applications)
74 this is not an acceptable limitation (which is why Led supports so-called 'Partial UNICODE' configurations - @'Led_tChar'
75 is UNICODE - but @'Led_SDKChar' is not).</p>
76 <p>Another relevant problem is that MFC allows you to build an application that is either fully UNICODE (-D_UNICODE) or
77 fully ANSI (not -D_UNICODE). Nothing in between.</p>
78 <p>So - if you want your app to use MFC - and you want UNICODE support - you are out of luck getting characters from the IME
79 (unless you set your OS SYSTEM DEFAULT LOCALE for the one locale all your characters character set can be found in).</p>
80 <p>In steps the qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug - to rescue the day.
81 I asked MSFT for help on this question (CASE ID #SRX991213601271). Basicly they weren't very helpful. But after
82 much discussion - I seem to have elicited a kludge that seems to work pretty well.</p>
83 <p>When ever I get an IME_CHAR message - instead of trying to decode the multibyte character inside
84 (which will be bogus frequently) - I call ImmGetCompositionStringW () and then grab the characters out of that
85 buffer directly. The only tricky part - and this was VERY tricky - is which characters to grab!</p>
86 <p>Eventually, through trial and error - I have evolved into doing this. Set an index counter to zero. And for
87 every WM_IME_CHAR - grab the char at that index, and bump my index. And for ever WM_IMECOMPOSITION or WM_IME_ENDCOMPOSITION
88 message - reset that counter to zero.</p>
89 <p>I've tried this on NT4Japanese, Win98J, and Win2K(RC2) with the default Locale Japanese or English. It seems to work.</p>
90 <p>This is probably somewhat error prone or risky. It is for that reason that I'm making this bug workaround
91 optional - and easy to shut off. But I leave it on - by default (when building for UNICODE - but without -D_UNICODE), since
92 in that case - you will almost certainly want that to work.</p>
93 <p>Default Value: (qWideCharacters && !qTargetPlatformSDKUseswchar_t)</p>
94 */
95#ifndef qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug
96#define qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug (!qTargetPlatformSDKUseswchar_t)
97#endif
98
99 /*
100 @CLASS: FunnyMSPageUpDownAdjustSelectionHelper
101 @DESCRIPTION: <p>Helper class to implement common MS-Windows UI where paging up/down, and sometimes other activities try
102 to presever the window-relative location of the blinking caret (by adjusting the selection accordingly). I think
103 this is a strange UI - but it does apepar to be common.</p>
104 <p>See also @'Led_Win32_Helper<BASE_INTERACTOR>::SetFunnyMSPageUpDownAdjustSelectionBehavior'</p>
105 */
106 class FunnyMSPageUpDownAdjustSelectionHelper {
107 public:
108 FunnyMSPageUpDownAdjustSelectionHelper ();
109
110 public:
111 nonvirtual void CaptureInfo (TextInteractor& ti);
112 nonvirtual void CompleteAdjustment (TextInteractor& ti);
113
114 private:
115 size_t fRowNum;
116 };
117
118 DISABLE_COMPILER_MSC_WARNING_START (4250) // inherits via dominance warning
119 /*
120 @CLASS: Led_Win32_Helper<BASE_INTERACTOR>
121 @BASES: BASE_INTERACTOR = @'TextInteractor'
122 @DESCRIPTION: <p>DOCUMENT SOON - BASED ON @'Led_MFC_Helper<MFC_BASE_CLASS,BASE_INTERACTOR>'</p>
123 */
124 template <typename BASE_INTERACTOR = TextInteractor>
125 class Led_Win32_Helper : public virtual BASE_INTERACTOR {
126 private:
127 using inherited = BASE_INTERACTOR;
128
129 public:
130 using UpdateMode = TextInteractor::UpdateMode;
131 using UpdateInfo = MarkerOwner::UpdateInfo;
132
133 protected:
134 Led_Win32_Helper ();
135
136 public:
137 virtual ~Led_Win32_Helper ();
138
139 // Routines subclasses must override so that this class can work...
140 public:
141 virtual HWND GetHWND () const = 0;
142
143 protected:
144 virtual LRESULT DefWindowProc (UINT message, WPARAM wParam, LPARAM lParam);
145
146 // Message Map Hook Functions (these must hook into whatever message map mechanism you are using - either WndProc or MFC / ATL Message Maps)
147 protected:
148 virtual LRESULT OnCreate_Msg (LPCREATESTRUCT createStruct);
149 virtual void OnPaint_Msg ();
150 virtual void OnSize_Msg ();
151 virtual void OnChar_Msg (UINT nChar, LPARAM lKeyData);
152 virtual LRESULT OnUniChar_Msg (WPARAM nChar, LPARAM lParam);
153#if qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug
154 virtual LONG OnIMEChar_Msg (WPARAM wParam, LPARAM lParam);
155 virtual LONG OnIME_COMPOSITION_Msg (WPARAM wParam, LPARAM lParam);
156 virtual LONG OnIME_ENDCOMPOSITION_Msg (WPARAM wParam, LPARAM lParam);
157#endif
158 virtual void OnKeyDown_Msg (UINT nChar, LPARAM lKeyData);
159 virtual bool OnSetCursor_Msg (HWND hWnd, UINT nHitTest, UINT message);
160 virtual UINT OnGetDlgCode_Msg ();
161 virtual void OnSetFocus_Msg (HWND oldWnd);
162 virtual void OnKillFocus_Msg (HWND newWnd);
163 virtual bool OnEraseBkgnd_Msg (HDC hDC);
164 virtual void OnTimer_Msg (UINT_PTR nEventID, TIMERPROC* proc);
165 virtual void OnLButtonDown_Msg (UINT nFlags, int x, int y);
166 virtual void OnLButtonUp_Msg (UINT nFlags, int x, int y);
167 virtual void OnLButtonDblClk_Msg (UINT nFlags, int x, int y);
168 virtual void OnMouseMove_Msg (UINT nFlags, int x, int y);
169 virtual void OnVScroll_Msg (UINT nSBCode, UINT nPos, HWND hScrollBar);
170 virtual void OnHScroll_Msg (UINT nSBCode, UINT nPos, HWND hScrollBar);
171 virtual bool OnMouseWheel_Msg (WPARAM wParam, LPARAM lParam);
172 virtual void OnEnable_Msg (bool enable);
173
174 protected:
175 short fAccumulatedWheelDelta;
176
177 public:
178 nonvirtual TWIPS_Rect GetDefaultWindowMargins () const;
179 nonvirtual void SetDefaultWindowMargins (const TWIPS_Rect& defaultWindowMargins);
180
181 private:
182 TWIPS_Rect fDefaultWindowMargins;
183
184 public:
185 nonvirtual bool GetControlArrowsScroll () const;
186 nonvirtual void SetControlArrowsScroll (bool controlArrowsScroll);
187
188 private:
189 bool fControlArrowsScroll;
190
191 public:
192 nonvirtual bool GetFunnyMSPageUpDownAdjustSelectionBehavior () const;
193 nonvirtual void SetFunnyMSPageUpDownAdjustSelectionBehavior (bool funnyMSPageUpDownAdjustSelectionBehavior);
194
195 private:
196 bool fFunnyMSPageUpDownAdjustSelectionBehavior;
197
198#if qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug
199 protected:
200 size_t fIMECurCharIdx;
201#endif
202
203 // Message Map Helpers - often do the REAL work of handling the messages...
204 protected:
205 nonvirtual void OnSize_ ();
206
207 /*
208 * Overrides of the Led (TextInteractor) code that must thunk down to Win32 SDK calls
209 */
210 protected:
211 virtual void RefreshWindowRect_ (const Led_Rect& windowRectArea, UpdateMode updateMode) const override;
212 virtual void UpdateWindowRect_ (const Led_Rect& windowRectArea) const override;
213 virtual bool QueryInputKeyStrokesPending () const override;
214
215 // Tablet API
216 public:
217 /*
218 @CLASS: Led_Win32_Helper<BASE_CLASS>::TemporarilyUseTablet
219 @DESCRIPTION: <p>Utility class to use (with caution), to temporarily for a given tablet to be
220 used for a given Led_Win32_Helper,
221 TextImager. NB: This causes the @'TextImager::TabletChangedMetrics' method by default
222 (unless called with special arg).</p>
223 */
224 class TemporarilyUseTablet {
225 public:
226 enum DoTextMetricsChangedCall {
227 eDoTextMetricsChangedCall,
228 eDontDoTextMetricsChangedCall
229 };
230 TemporarilyUseTablet (Led_Win32_Helper<BASE_INTERACTOR>& editor, Tablet* t, DoTextMetricsChangedCall tmChanged = eDoTextMetricsChangedCall);
231 ~TemporarilyUseTablet ();
232
233 private:
234 Led_Win32_Helper<BASE_INTERACTOR>& fEditor;
235 Tablet* fOldTablet;
236 DoTextMetricsChangedCall fDoTextMetricsChangedCall;
237 };
238
239 private:
240 friend class TemporarilyUseTablet;
241
242 protected:
243 virtual Tablet* AcquireTablet () const override;
244 virtual void ReleaseTablet (Tablet* tablet) const override;
245
246 private:
247 Tablet* fUpdateTablet; // assigned in stack-based fasion during update/draw calls.
248 mutable Tablet fAllocatedTablet; // if we needed to allocate a tablet, store it here, and on the
249 // last release of it, free it...
250 mutable size_t fAcquireCount;
251
252 public:
253 virtual void WindowDrawHelper (Tablet* tablet, const Led_Rect& subsetToDraw, bool printing);
254
255 protected:
256 virtual void EraseBackground (Tablet* tablet, const Led_Rect& subsetToDraw, bool printing) override;
257
258 // Keyboard Processing:
259 private:
260 nonvirtual bool CheckIfDraggingBeepAndReturn ();
261
262 protected:
263 virtual void HandleTabCharacterTyped ();
264
265 public:
266 virtual void AboutToUpdateText (const UpdateInfo& updateInfo) override;
267 virtual void DidUpdateText (const UpdateInfo& updateInfo) noexcept override;
268
269 protected:
270 nonvirtual void DidUpdateText_ (const UpdateInfo& updateInfo) noexcept;
271
272 // Mouse Processing
273 protected:
274 Led_Point fMouseTrackingLastPoint;
275
276 protected:
277 size_t fDragAnchor; // only used while dragging mouse
278 private:
279#if qScrollTextDuringThumbTracking
280 bool fSBarThumbTracking;
281#endif
282
283 protected:
284 nonvirtual void OnNormalLButtonDown (UINT nFlags, const Led_Point& at);
285
286 // Timer support for autoscrolling (normal and drag).
287 private:
288 nonvirtual void StartAutoscrollTimer ();
289 nonvirtual void StopAutoscrollTimer ();
290
291 private:
292 enum {
293 eAutoscrollingTimerEventID = 434
294 }; // Magic#
295 UINT_PTR fAutoScrollTimerID; // zero means no timer
296
297 // Scrolling Support
298 public:
299 using ScrollBarType = TextInteractor::ScrollBarType; // redundant typedef to keep compiler happy...LGP 2000-10-05-MSVC60
300 using VHSelect = TextInteractor::VHSelect; // redundant typedef to keep compiler happy...LGP 2000-10-05-MSVC60
301
302 public:
303 virtual void SetScrollBarType (VHSelect vh, ScrollBarType scrollBarType) override;
304
305 protected:
306 virtual bool ShouldUpdateHScrollBar () const;
307 virtual bool ShouldUpdateVScrollBar () const;
308 virtual bool TypeAndScrollInfoSBVisible (ScrollBarType scrollbarAppears, const SCROLLINFO& scrollInfo) const;
309 virtual SCROLLINFO GetHScrollInfo (UINT nMask = SIF_ALL) const;
310 virtual void SetHScrollInfo (ScrollBarType scrollbarAppears, const SCROLLINFO& scrollInfo, bool redraw = true);
311 nonvirtual void SetHScrollInfo (const SCROLLINFO& scrollInfo, bool redraw = true);
312 virtual SCROLLINFO GetVScrollInfo (UINT nMask = SIF_ALL) const;
313 virtual void SetVScrollInfo (ScrollBarType scrollbarAppears, const SCROLLINFO& scrollInfo, bool redraw = true);
314 nonvirtual void SetVScrollInfo (const SCROLLINFO& scrollInfo, bool redraw = true);
315
316 public:
317 virtual void InvalidateScrollBarParameters () override;
318
319 protected:
320 nonvirtual void InvalidateScrollBarParameters_ ();
321
322 protected:
323 virtual void InvalidateCaretState () override;
324 nonvirtual void UpdateCaretState_ ();
325
326 protected:
327 virtual void UpdateScrollBars () override;
328
329 // Clipboard Support
330 protected:
331 virtual bool OnCopyCommand_Before () override;
332 virtual void OnCopyCommand_After () override;
333 virtual bool OnPasteCommand_Before () override;
334 virtual void OnPasteCommand_After () override;
335
336 public:
337 nonvirtual DWORD GetStyle () const;
338
339 // Private Misc Helpers
340 private:
341 nonvirtual int GetWindowID () const;
342
343 protected:
344 nonvirtual HWND GetValidatedHWND () const;
345 };
346 DISABLE_COMPILER_MSC_WARNING_END (4250) // inherits via dominance warning
347
348 DISABLE_COMPILER_MSC_WARNING_START (4250) // inherits via dominance warning
349 /*
350 @CLASS: Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>
351 @BASES: BASECLASS
352 @DESCRIPTION: <p>Mimicry of the starndard Win32 messages sent to an edit control. We cannot
353 mimic ALL the messages. Some just don't make sense (like GETHANDLE). But for those that do make sense, we
354 do our best.</p>
355 */
356 template <typename BASECLASS>
357 class Led_Win32_Win32SDKMessageMimicHelper : public BASECLASS {
358 private:
359 using inherited = BASECLASS;
360
361 protected:
362 Led_Win32_Win32SDKMessageMimicHelper ();
363
364 public:
365 virtual bool HandleMessage (UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result);
366
367 protected:
368 virtual LRESULT OnMsgGetText (WPARAM wParam, LPARAM lParam);
369 virtual LRESULT OnMsgSetText (WPARAM /*wParam*/, LPARAM lParam);
370 virtual LRESULT OnMsgGetTextLength (WPARAM /*wParam*/, LPARAM /*lParam*/);
371 virtual LRESULT OnMsgGetSel (WPARAM wParam, LPARAM lParam);
372 virtual LRESULT OnMsgSetReadOnly (WPARAM wParam, LPARAM lParam);
373 virtual LRESULT OnMsgGetFirstVisibleLine (WPARAM wParam, LPARAM lParam);
374 virtual LRESULT OnMsgLineIndex (WPARAM wParam, LPARAM lParam);
375 virtual LRESULT OnMsgLineCount (WPARAM wParam, LPARAM lParam);
376 virtual LRESULT OnMsgCanUndo (WPARAM wParam, LPARAM lParam);
377 virtual LRESULT OnMsgUndo (WPARAM wParam, LPARAM lParam);
378 virtual LRESULT OnMsgEmptyUndoBuffer (WPARAM wParam, LPARAM lParam);
379 virtual LRESULT OnMsgClear (WPARAM wParam, LPARAM lParam);
380 virtual LRESULT OnMsgCut (WPARAM wParam, LPARAM lParam);
381 virtual LRESULT OnMsgCopy (WPARAM wParam, LPARAM lParam);
382 virtual LRESULT OnMsgPaste (WPARAM wParam, LPARAM lParam);
383 virtual LRESULT OnMsgLineFromChar (WPARAM wParam, LPARAM lParam);
384 virtual LRESULT OnMsgLineLength (WPARAM wParam, LPARAM lParam);
385 virtual LRESULT OnMsgLineScroll (WPARAM wParam, LPARAM lParam);
386 virtual LRESULT OnMsgReplaceSel (WPARAM wParam, LPARAM lParam);
387 virtual LRESULT OnMsgSetSel (WPARAM wParam, LPARAM lParam);
388 virtual LRESULT OnMsgScrollCaret (WPARAM wParam, LPARAM lParam);
389 virtual LRESULT OnMsgGetFont (WPARAM wParam, LPARAM lParam);
390 virtual LRESULT OnMsgSetFont (WPARAM wParam, LPARAM lParam);
391
392 private:
393 FontObject fDefaultFontCache; // used to be able to answer WM_GETFONT calls...
394 };
395 DISABLE_COMPILER_MSC_WARNING_END (4250) // inherits via dominance warning
396
397 /*
398 @CLASS: SimpleWin32WndProcHelper
399 @DESCRIPTION: <p>Simple window subclassing/WindowProc hook. Doesn't really work with Led-based code - particular. Helpful for
400 other windows you need to hook, like dialogs etc. This could be integrated with Led's window classes
401 at some future point.</p>
402 */
403 class SimpleWin32WndProcHelper {
404 public:
405 SimpleWin32WndProcHelper ();
406
407 public:
408 nonvirtual HWND GetHWND () const;
409 nonvirtual void SetHWND (HWND hWnd);
410 nonvirtual HWND GetValidatedHWND () const;
411
412 private:
413 HWND fHWnd;
414
415 public:
416 nonvirtual void Create (LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight,
417 HWND hWndParent, HMENU hMenu, HINSTANCE hInstance);
418 nonvirtual void Create (DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth,
419 int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance);
420
421 public:
422 nonvirtual bool SubclassWindow (HWND hWnd);
423 nonvirtual bool SubclassWindowW (HWND hWnd);
424
425 private:
426 WNDPROC fSuperWindowProc;
427
428 public:
429 nonvirtual LRESULT SendMessage (UINT msg, WPARAM wParam, LPARAM lParam);
430
431 public:
432 nonvirtual bool IsWindowRealized () const;
433 nonvirtual void Assert_Window_Realized () const;
434
435 protected:
436 nonvirtual void Require_Window_Realized () const;
437
438 public:
439 nonvirtual bool IsWindowUNICODE () const;
440
441 public:
442 nonvirtual bool IsWindowShown () const;
443 nonvirtual void SetWindowVisible (bool shown = true);
444
445 public:
446 static LRESULT CALLBACK StaticWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
447
448 protected:
449 virtual LRESULT WndProc (UINT message, WPARAM wParam, LPARAM lParam);
450
451 protected:
452 virtual LRESULT DefWindowProc (UINT message, WPARAM wParam, LPARAM lParam);
453 };
454
455 DISABLE_COMPILER_MSC_WARNING_START (4250) // inherits via dominance warning
456 /*
457 @CLASS: Led_Win32_SimpleWndProc_Helper
458 @BASES: @'Led_Win32_Helper<BASE_INTERACTOR>'
459 @DESCRIPTION: <p>Add a simple Win32 WndProc static function to be used in a window class, and have that call the right
460 entries in BASE_WIN32_HELPER to handle the calls. Not a generic message map mechanism - but instead one simple
461 and hardwired for our purposes here.</p>
462 */
463 template <typename BASE_WIN32_HELPER = Led_Win32_Helper<>>
464 class Led_Win32_SimpleWndProc_Helper : public BASE_WIN32_HELPER {
465 private:
466 using inherited = BASE_WIN32_HELPER;
467
468 public:
469 Led_Win32_SimpleWndProc_Helper ();
470
471 public:
472 virtual ~Led_Win32_SimpleWndProc_Helper ();
473
474 public:
475 nonvirtual void Create (LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight,
476 HWND hWndParent, HMENU hMenu, HINSTANCE hInstance);
477 nonvirtual void Create (DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth,
478 int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance);
479
480 public:
481 nonvirtual void SetHWND (HWND hWnd);
482
483 private:
484 HWND fHWnd;
485
486 // Led_Win32_Helper overrides.
487 public:
488 virtual HWND GetHWND () const override;
489
490 public:
491 virtual void OnNCDestroy_Msg ();
492
493 public:
494 nonvirtual bool SubclassWindow (HWND hWnd);
495 nonvirtual bool ReplaceWindow (HWND hWnd);
496
497 protected:
498 virtual void HookSubclassWindow ();
499
500 private:
501 WNDPROC fSuperWindowProc;
502
503 public:
504 static LRESULT CALLBACK StaticWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
505
506 protected:
507 virtual LRESULT WndProc (UINT message, WPARAM wParam, LPARAM lParam);
508
509 protected:
510 virtual LRESULT DefWindowProc (UINT message, WPARAM wParam, LPARAM lParam) override;
511 };
512 DISABLE_COMPILER_MSC_WARNING_END (4250) // inherits via dominance warning
513
514 DISABLE_COMPILER_MSC_WARNING_START (4250) // inherits via dominance warning
515 /*
516 @CLASS: Led_Win32_SimpleWndProc_HelperWithSDKMessages
517 @BASES: @'Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>' with BASE_INTERACTOR= @'Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>'
518 @DESCRIPTION: <p>Simple mixin to combine these two.</p>
519 */
520 template <typename BASE_CLASS>
521 class Led_Win32_SimpleWndProc_HelperWithSDKMessages : public Led_Win32_SimpleWndProc_Helper<Led_Win32_Win32SDKMessageMimicHelper<BASE_CLASS>> {
522 private:
523 using inherited = Led_Win32_SimpleWndProc_Helper<Led_Win32_Win32SDKMessageMimicHelper<BASE_CLASS>>;
524
525 public:
526 Led_Win32_SimpleWndProc_HelperWithSDKMessages ();
527
528 protected:
529 virtual LRESULT WndProc (UINT message, WPARAM wParam, LPARAM lParam) override;
530 };
531 DISABLE_COMPILER_MSC_WARNING_END (4250) // inherits via dominance warning
532
533 /*
534 ********************************************************************************
535 ***************************** Implementation Details ***************************
536 ********************************************************************************
537 */
538#if (_WIN32_WINNT < 0x0501)
539// Make sure WM_UNICHAR & UNICODE_NOCHAR are defined, even if a user builds with old header files
540// or inconsistent settings of _WIN32_WINNT ... LGP 2003-01-29
541#ifndef WM_UNICHAR
542#define WM_UNICHAR 0x0109
543#endif
544#ifndef UNICODE_NOCHAR
545#define UNICODE_NOCHAR 0xFFFF
546#endif
547#endif
548
549 namespace Private {
550 /*
551 * Hack to assure the Led_Win32.o module is linked in. Without it being linked in,
552 * the IdleManagerOSImpl_Win32 won't get installed.
553 */
554 struct IdleMangerLinkerSupport {
555 IdleMangerLinkerSupport ();
556 };
557 static IdleMangerLinkerSupport sIdleMangerLinkerSupport;
558 }
559
560 // class Led_Win32_Helper<BASE_INTERACTOR>
561 template <typename BASE_INTERACTOR>
562 Led_Win32_Helper<BASE_INTERACTOR>::Led_Win32_Helper ()
563 : fAccumulatedWheelDelta (0)
564 , fDefaultWindowMargins ()
565 , fControlArrowsScroll (false)
566 , fFunnyMSPageUpDownAdjustSelectionBehavior (true)
567#if qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug
568 , fIMECurCharIdx (0)
569#endif
570 , fUpdateTablet (nullptr)
571 , fAllocatedTablet ()
572 , fAcquireCount (0)
573 , fMouseTrackingLastPoint ()
574 , fDragAnchor (0)
575#if qScrollTextDuringThumbTracking
576 , fSBarThumbTracking (false)
577#endif
578 , fAutoScrollTimerID (0)
579 {
580 fDefaultWindowMargins.top = TWIPS (2 * TWIPS::kPoint);
581 fDefaultWindowMargins.left = TWIPS (2 * TWIPS::kPoint);
582 fDefaultWindowMargins.bottom = TWIPS (2 * TWIPS::kPoint);
583 fDefaultWindowMargins.right = TWIPS (2 * TWIPS::kPoint);
584 }
585 template <typename BASE_INTERACTOR>
586 Led_Win32_Helper<BASE_INTERACTOR>::~Led_Win32_Helper ()
587 {
588 Assert (fUpdateTablet == nullptr);
589 Assert (fAllocatedTablet.m_hDC == nullptr);
590 Assert (fAllocatedTablet.m_hAttribDC == nullptr);
591 Assert (fAcquireCount == 0);
592 Assert (fAutoScrollTimerID == 0); // I don't see how we can get destroyed while tracking?
593 }
594 template <typename BASE_INTERACTOR>
595 LRESULT Led_Win32_Helper<BASE_INTERACTOR>::DefWindowProc (UINT message, WPARAM wParam, LPARAM lParam)
596 {
597 return ::DefWindowProc (this->GetValidatedHWND (), message, wParam, lParam);
598 }
599 template <typename BASE_INTERACTOR>
600 LRESULT Led_Win32_Helper<BASE_INTERACTOR>::OnCreate_Msg (LPCREATESTRUCT createStruct)
601 {
602 RequireNotNull (createStruct);
603 if ((createStruct->style & WS_VSCROLL) and this->GetScrollBarType (TextInteractor::v) == TextInteractor::eScrollBarNever) {
604 this->SetScrollBarType (TextInteractor::v, TextInteractor::eScrollBarAlways);
605 }
606 if ((createStruct->style & WS_HSCROLL) and this->GetScrollBarType (TextInteractor::h) == TextInteractor::eScrollBarNever) {
607 this->SetScrollBarType (TextInteractor::h, TextInteractor::eScrollBarAlways);
608 }
609 return this->DefWindowProc (WM_CREATE, 0, reinterpret_cast<LPARAM> (createStruct));
610 }
611 template <typename BASE_INTERACTOR>
612 void Led_Win32_Helper<BASE_INTERACTOR>::OnPaint_Msg ()
613 {
614 HWND hWnd = this->GetValidatedHWND ();
615 PAINTSTRUCT ps;
616 HDC hdc = ::BeginPaint (hWnd, &ps);
617 if (hdc != nullptr) {
618 RECT boundsRect;
619 Verify (::GetClipBox (hdc, &boundsRect) != ERROR);
620 Tablet tablet (hdc, Tablet::eDoesntOwnDC);
621 try {
622 this->WindowDrawHelper (&tablet, AsLedRect (boundsRect), false);
623 }
624 catch (...) {
625 ::EndPaint (hWnd, &ps);
626 throw;
627 }
628 ::EndPaint (hWnd, &ps);
629 }
630 }
631 template <typename BASE_INTERACTOR>
632 void Led_Win32_Helper<BASE_INTERACTOR>::OnSize_Msg ()
633 {
634 this->OnSize_ ();
635 }
636 template <typename BASE_INTERACTOR>
637 /*
638 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::OnChar_Msg
639 @DESCRIPTION: <p>Hook the Win32 SDK WM_CHAR message to handle user typing. Probably should not be called directly,
640 except maybe to simulate user typing.</p>
641 */
642 void Led_Win32_Helper<BASE_INTERACTOR>::OnChar_Msg (UINT nChar, LPARAM /*lKeyData*/)
643 {
644#if !qTargetPlatformSDKUseswchar_t
645 {
646 CodePage useCodePage = Characters::Platform::Windows::Win32PrimaryLangIDToCodePage (LOWORD (::GetKeyboardLayout (nullptr)));
647 char ccc = nChar;
648 wchar_t outC = 0;
649 int xx = ::MultiByteToWideChar (useCodePage, 0, &ccc, 1, &outC, 1);
650 if (xx == 1) {
651 nChar = outC;
652 }
653 }
654#endif
655
656 if (nChar == VK_ESCAPE) {
657 // Ignore when user types ESC key - that is what Windows NotePad and MS Word seem todo...
658 return;
659 }
660
661 if (this->CheckIfDraggingBeepAndReturn ()) {
662 return;
663 }
664
665 if (nChar == '\t') {
666 this->HandleTabCharacterTyped ();
667 return;
668 }
669
670 if (nChar == '\r') {
671 nChar = '\n';
672 }
673 if ((nChar == 0x7f) and (::GetKeyState (VK_CONTROL) & 0x8000)) {
674 nChar = '\b';
675 }
676
677 this->OnTypedNormalCharacter (static_cast<Led_tChar> (nChar), false, !!(::GetKeyState (VK_SHIFT) & 0x8000), false,
678 !!(::GetKeyState (VK_CONTROL) & 0x8000), !!(::GetKeyState (VK_MENU) & 0x8000));
679
680#if qSupportWindowsSDKCallbacks
681 HWND hWnd = this->GetValidatedHWND ();
682 (void)::SendMessage (::GetParent (hWnd), WM_COMMAND, MAKELONG (GetWindowID (), EN_CHANGE), (LPARAM)hWnd);
683#endif
684 }
685 template <typename BASE_INTERACTOR>
686 /*
687 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::OnUniChar_Msg
688 @DESCRIPTION: <p>Hook the Win32 SDK WM_UNICHAR message to handle user typing. Probably should not be called directly,
689 except maybe to simulate user typing.</p>
690 */
691 LRESULT Led_Win32_Helper<BASE_INTERACTOR>::OnUniChar_Msg (WPARAM nChar, LPARAM /*lParam*/)
692 {
693 if (nChar == UNICODE_NOCHAR) {
694 return 1;
695 }
696
697 if (nChar == VK_ESCAPE) {
698 // Ignore when user types ESC key - that is what Windows NotePad and MS Word seem todo...
699 return 0;
700 }
701
702 if (CheckIfDraggingBeepAndReturn ()) {
703 return 0;
704 }
705
706 if (nChar == '\t') {
707 HandleTabCharacterTyped ();
708 return 0;
709 }
710
711 if (nChar == '\r') {
712 nChar = '\n';
713 }
714 if ((nChar == 0x7f) and (::GetKeyState (VK_CONTROL) & 0x8000)) {
715 nChar = '\b';
716 }
717
718 this->OnTypedNormalCharacter (static_cast<Led_tChar> (nChar), false, !!(::GetKeyState (VK_SHIFT) & 0x8000), false,
719 !!(::GetKeyState (VK_CONTROL) & 0x8000), !!(::GetKeyState (VK_MENU) & 0x8000));
720
721#if qSupportWindowsSDKCallbacks
722 HWND hWnd = this->GetValidatedHWND ();
723 (void)::SendMessage (::GetParent (hWnd), WM_COMMAND, MAKELONG (GetWindowID (), EN_CHANGE), (LPARAM)hWnd);
724#endif
725 return 0;
726 }
727#if qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug
728 template <typename BASE_INTERACTOR>
729 /*
730 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::OnIMEChar_Msg
731 @DESCRIPTION: <p>Part of @'qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug' bug workaround.</p>
732 */
733 LONG Led_Win32_Helper<BASE_INTERACTOR>::OnIMEChar_Msg (WPARAM wParam, LPARAM lParam)
734 {
735 /*
736 * Win32 SDK docs don't say what value to return for WM_IME_CHAR - so return 0 for now - LGP20000111
737 */
738 UINT nChar = wParam;
739 if (nChar == VK_ESCAPE) {
740 // Ignore when user types ESC key - that is what Windows NotePad and MS Word seem todo...
741 return 0;
742 }
743 if (nChar == '\t') {
744 HandleTabCharacterTyped ();
745 return 0;
746 }
747
748 if (CheckIfDraggingBeepAndReturn ()) {
749 return 0;
750 }
751
752 if (nChar == '\r') {
753 nChar = '\n';
754 }
755
756 if (qTargetPlatformSDKUseswchar_t || ::IsWindowUnicode (this->GetValidatedHWND ())) {
757 // do nothing - 'nChar' is already a fine UNICODE character
758 // NB: we COULD just check qTargetPlatformSDKUseswchar_t. But be nicer that MSFT. Allow for that a user
759 // might want to create a UNICODE window without defining -D_UNICODE (see comments in
760 // qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug)
761 }
762#if !qTargetPlatformSDKUseswchar_t
763 else {
764 wstring tmpIMEBugWorkaroundCompString = IME::Get ().GetCompositionResultStringW (this->GetValidatedHWND ());
765 if (fIMECurCharIdx < tmpIMEBugWorkaroundCompString.length ()) {
766 nChar = tmpIMEBugWorkaroundCompString[fIMECurCharIdx];
767 ++fIMECurCharIdx;
768 }
769 else {
770 /*
771 * This shouldn't happen. And it won't do much good except on systems where the default locale
772 * is set to use a characterset used by the IME. But at least for those cases - do what
773 * we can...
774 */
775 wchar_t convertedChars[2];
776 memset (&convertedChars, 0, sizeof (convertedChars));
777 int nWideChars = ::MultiByteToWideChar (CP_ACP, 0, reinterpret_cast<char*> (&nChar), 2, convertedChars, 2);
778 wchar_t convertedChar = convertedChars[0];
779 if (nWideChars == 0) {
780 OnBadUserInput ();
781 return 0;
782 }
783 nChar = convertedChar;
784 }
785 }
786#endif
787
788 OnTypedNormalCharacter (nChar, false, !!(::GetKeyState (VK_SHIFT) & 0x8000), false, !!(::GetKeyState (VK_CONTROL) & 0x8000),
789 !!(::GetKeyState (VK_MENU) & 0x8000));
790
791#if qSupportWindowsSDKCallbacks
792 HWND hWnd = this->GetValidatedHWND ();
793 (void)::SendMessage (::GetParent (hWnd), WM_COMMAND, MAKELONG (GetWindowID (), EN_CHANGE), (LPARAM)hWnd);
794#endif
795 return 0;
796 }
797 template <typename BASE_INTERACTOR>
798 /*
799 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::OnIME_COMPOSITION_Msg
800 @DESCRIPTION: <p>Part of @'qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug' bug workaround.</p>
801 */
802 LONG Led_Win32_Helper<BASE_INTERACTOR>::OnIME_COMPOSITION_Msg (WPARAM wParam, LPARAM lParam)
803 {
804 fIMECurCharIdx = 0;
805 return DefWindowProc (WM_IME_COMPOSITION, wParam, lParam);
806 }
807 template <typename BASE_INTERACTOR>
808 /*
809 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::OnIME_ENDCOMPOSITION_Msg
810 @DESCRIPTION: <p>Part of @'qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug' bug workaround.</p>
811 */
812 LONG Led_Win32_Helper<BASE_INTERACTOR>::OnIME_ENDCOMPOSITION_Msg (WPARAM wParam, LPARAM lParam)
813 {
814 fIMECurCharIdx = 0;
815 return DefWindowProc (WM_IME_ENDCOMPOSITION, wParam, lParam);
816 }
817#endif
818 template <typename BASE_INTERACTOR>
819 /*
820 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::OnKeyDown_Msg
821 @DESCRIPTION: <p>Hook the Win32 SDK WM_KEYDOWN message to handle user typing. Most typing handled via Led_MFC::OnChar (). But
822 some keystrokes only make it to here, and on the WM_CHAR message, for some reason.</p>
823 */
824 void Led_Win32_Helper<BASE_INTERACTOR>::OnKeyDown_Msg (UINT nChar, LPARAM /*lKeyData*/)
825 {
826 bool shiftPressed = !!(::GetKeyState (VK_SHIFT) & 0x8000);
827 bool controlPressed = !!(::GetKeyState (VK_CONTROL) & 0x8000);
828
829 using TextInteractor::eDefaultUpdate;
830 using TextInteractor::eImmediateUpdate;
831
832 using TextInteractor::eCursorBack;
833 using TextInteractor::eCursorByBuffer;
834 using TextInteractor::eCursorByChar;
835 using TextInteractor::eCursorByLine;
836 using TextInteractor::eCursorByRow;
837 using TextInteractor::eCursorByWord;
838 using TextInteractor::eCursorExtendingSelection;
839 using TextInteractor::eCursorForward;
840 using TextInteractor::eCursorMoving;
841 using TextInteractor::eCursorToEnd;
842 using TextInteractor::eCursorToStart;
843
844 /*
845 * There are zillions of these virtual keycodes, and I'm unsure exactly if/how
846 * I'm to respond to each. All the ones here are listed in numeric order (the
847 * order they are found in WinUser.h). We (will) omit any which we do nothing
848 * for - and only include those we react to.
849 */
850 switch (nChar) {
851 case VK_BACK: {
852 // Seems to get called in OnChar() - so ignore it here...
853 } break;
854
855 case VK_TAB: {
856 } break;
857
858 case VK_CLEAR: {
859 } break;
860
861 case VK_RETURN: {
862 } break;
863
864 case VK_PRIOR: { // page UP
865 if (CheckIfDraggingBeepAndReturn ()) {
866 return;
867 }
868 this->BreakInGroupedCommands ();
869
870 if (controlPressed) {
871 this->SetSelection (this->GetMarkerPositionOfStartOfWindow (), this->GetMarkerPositionOfStartOfWindow ());
872 }
873 else {
874 FunnyMSPageUpDownAdjustSelectionHelper selectionAdjuster;
875 bool doFunny = this->GetFunnyMSPageUpDownAdjustSelectionBehavior ();
876 if (doFunny) {
877 selectionAdjuster.CaptureInfo (*this);
878 }
879
880 // NB: this isn't QUITE right for pageup - with differing height rows!!!!
881 // Do the actual scrolling - this is the only part that makes any sense!
882 this->ScrollByIfRoom (-(int)this->GetTotalRowsInWindow ());
883
884 if (doFunny) {
885 selectionAdjuster.CompleteAdjustment (*this);
886 }
887 }
888#if qPeekForMoreCharsOnUserTyping
889 this->UpdateIfNoKeysPending ();
890#endif
891 } break;
892
893 case VK_NEXT: { // page DOWN
894 if (this->CheckIfDraggingBeepAndReturn ()) {
895 return;
896 }
897 this->BreakInGroupedCommands ();
898
899 if (controlPressed) {
900 this->SetSelection (this->GetMarkerPositionOfEndOfWindow (), this->GetMarkerPositionOfEndOfWindow ());
901 }
902 else {
903 FunnyMSPageUpDownAdjustSelectionHelper selectionAdjuster;
904 bool doFunny = this->GetFunnyMSPageUpDownAdjustSelectionBehavior ();
905 if (doFunny) {
906 selectionAdjuster.CaptureInfo (*this);
907 }
908
909 this->ScrollByIfRoom (this->GetTotalRowsInWindow ());
910
911 if (doFunny) {
912 selectionAdjuster.CompleteAdjustment (*this);
913 }
914 }
915#if qPeekForMoreCharsOnUserTyping
916 this->UpdateIfNoKeysPending ();
917#endif
918 } break;
919
920 case VK_END: {
921 if (this->CheckIfDraggingBeepAndReturn ()) {
922 return;
923 }
924 this->BreakInGroupedCommands ();
925 this->DoSingleCharCursorEdit (eCursorToEnd, controlPressed ? eCursorByBuffer : eCursorByRow,
926 shiftPressed ? eCursorExtendingSelection : eCursorMoving,
927 qPeekForMoreCharsOnUserTyping ? eDefaultUpdate : eImmediateUpdate);
928#if qPeekForMoreCharsOnUserTyping
929 this->UpdateIfNoKeysPending ();
930#endif
931 } break;
932
933 case VK_HOME: {
934 if (this->CheckIfDraggingBeepAndReturn ()) {
935 return;
936 }
937 this->BreakInGroupedCommands ();
938 this->DoSingleCharCursorEdit (eCursorToStart, controlPressed ? eCursorByBuffer : eCursorByRow,
939 shiftPressed ? eCursorExtendingSelection : eCursorMoving,
940 qPeekForMoreCharsOnUserTyping ? eDefaultUpdate : eImmediateUpdate);
941#if qPeekForMoreCharsOnUserTyping
942 this->UpdateIfNoKeysPending ();
943#endif
944 } break;
945
946 case VK_LEFT: {
947 if (this->CheckIfDraggingBeepAndReturn ()) {
948 return;
949 }
950 this->BreakInGroupedCommands ();
951 this->DoSingleCharCursorEdit (eCursorBack, controlPressed ? eCursorByWord : eCursorByChar,
952 shiftPressed ? eCursorExtendingSelection : eCursorMoving,
953 qPeekForMoreCharsOnUserTyping ? eDefaultUpdate : eImmediateUpdate);
954#if qPeekForMoreCharsOnUserTyping
955 this->UpdateIfNoKeysPending ();
956#endif
957 } break;
958
959 case VK_UP: {
960 if (this->CheckIfDraggingBeepAndReturn ()) {
961 return;
962 }
963 this->BreakInGroupedCommands ();
964 if (controlPressed) {
965 if (this->GetControlArrowsScroll ()) {
966 FunnyMSPageUpDownAdjustSelectionHelper selectionAdjuster;
967 bool doFunny = this->GetFunnyMSPageUpDownAdjustSelectionBehavior ();
968 if (doFunny) {
969 selectionAdjuster.CaptureInfo (*this);
970 }
971 this->ScrollByIfRoom (-1);
972 if (doFunny) {
973 selectionAdjuster.CompleteAdjustment (*this);
974 }
975 }
976 else {
977 this->DoSingleCharCursorEdit (eCursorToStart, eCursorByLine, shiftPressed ? eCursorExtendingSelection : eCursorMoving,
978 qPeekForMoreCharsOnUserTyping ? eDefaultUpdate : eImmediateUpdate);
979 }
980 }
981 else {
982 this->DoSingleCharCursorEdit (eCursorBack, eCursorByRow, shiftPressed ? eCursorExtendingSelection : eCursorMoving,
983 qPeekForMoreCharsOnUserTyping ? eDefaultUpdate : eImmediateUpdate);
984 }
985#if qPeekForMoreCharsOnUserTyping
986 this->UpdateIfNoKeysPending ();
987#endif
988 } break;
989
990 case VK_RIGHT: {
991 if (this->CheckIfDraggingBeepAndReturn ()) {
992 return;
993 }
994 this->BreakInGroupedCommands ();
995 this->DoSingleCharCursorEdit (eCursorForward, controlPressed ? eCursorByWord : eCursorByChar,
996 shiftPressed ? eCursorExtendingSelection : eCursorMoving,
997 qPeekForMoreCharsOnUserTyping ? eDefaultUpdate : eImmediateUpdate);
998#if qPeekForMoreCharsOnUserTyping
999 this->UpdateIfNoKeysPending ();
1000#endif
1001 } break;
1002
1003 case VK_DOWN: {
1004 if (this->CheckIfDraggingBeepAndReturn ()) {
1005 return;
1006 }
1007 this->BreakInGroupedCommands ();
1008 if (controlPressed) {
1009 if (this->GetControlArrowsScroll ()) {
1010 FunnyMSPageUpDownAdjustSelectionHelper selectionAdjuster;
1011 bool doFunny = this->GetFunnyMSPageUpDownAdjustSelectionBehavior ();
1012 if (doFunny) {
1013 selectionAdjuster.CaptureInfo (*this);
1014 }
1015 this->ScrollByIfRoom (1);
1016 if (doFunny) {
1017 selectionAdjuster.CompleteAdjustment (*this);
1018 }
1019 }
1020 else {
1021 this->DoSingleCharCursorEdit (eCursorToEnd, eCursorByLine, shiftPressed ? eCursorExtendingSelection : eCursorMoving,
1022 qPeekForMoreCharsOnUserTyping ? eDefaultUpdate : eImmediateUpdate);
1023 }
1024 }
1025 else {
1026 this->DoSingleCharCursorEdit (eCursorForward, eCursorByRow, shiftPressed ? eCursorExtendingSelection : eCursorMoving,
1027 qPeekForMoreCharsOnUserTyping ? eDefaultUpdate : eImmediateUpdate);
1028 }
1029#if qPeekForMoreCharsOnUserTyping
1030 this->UpdateIfNoKeysPending ();
1031#endif
1032 } break;
1033
1034 case VK_SELECT: {
1035 } break;
1036
1037 case VK_PRINT: {
1038 } break;
1039
1040 case VK_EXECUTE: {
1041 } break;
1042
1043 case VK_SNAPSHOT: {
1044 } break;
1045
1046 case VK_INSERT: {
1047 } break;
1048
1049 case VK_DELETE: {
1050 if (this->CheckIfDraggingBeepAndReturn ()) {
1051 return;
1052 }
1053
1054 this->BreakInGroupedCommands ();
1055
1056 /*
1057 * If the selection is empty, then delete the following character (if any)
1058 * and if it is non-empty - simply delete its contents.
1059 */
1060 if (this->GetSelectionStart () == this->GetSelectionEnd ()) {
1061 // note this doesn't change the selection - since we only delete following
1062 // the selection...
1063 //
1064 // Also note that we count on the fact that it is safe toocall FindNextCharacter ()
1065 // at the buffers end and it will pin to that end...
1066 this->SetSelection (this->GetSelectionStart (), this->FindNextCharacter (this->GetSelectionStart ()), eDefaultUpdate);
1067 this->InteractiveReplace (LED_TCHAR_OF (""), 0);
1068 }
1069 else {
1070 this->OnPerformCommand (TextInteractor::kClear_CmdID);
1071 }
1072 this->ScrollToSelection ();
1073 this->Update ();
1074#if qSupportWindowsSDKCallbacks
1075 // Actually - sends EN_CHANGE even if selStart=sselEnd==ENDOFBUFFER (so nothing changed). But hopefully thats OK...
1076 HWND hWnd = this->GetValidatedHWND ();
1077 (void)::SendMessage (::GetParent (hWnd), WM_COMMAND, MAKELONG (GetWindowID (), EN_CHANGE), (LPARAM)hWnd);
1078#endif
1079 } break;
1080
1081 case VK_HELP: {
1082 } break;
1083
1084 default: {
1085 // just ignore any of the others...
1086 } break;
1087 }
1088 }
1089 template <typename BASE_INTERACTOR>
1090 /*
1091 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::OnSetCursor_Msg
1092 @DESCRIPTION: <p>Hook the Win32 SDK WM_SETCURSOR message to handle set the cursor to an I-Beam, as appropriate. When over
1093 draggable text, instead use a standard arrow cursor.</p>
1094 */
1095 bool Led_Win32_Helper<BASE_INTERACTOR>::OnSetCursor_Msg (HWND hWnd, UINT nHitTest, UINT message)
1096 {
1097 if (nHitTest == HTCLIENT and hWnd == this->GetValidatedHWND ()) {
1098 /*
1099 * The SDK docs seem to indicate that you should call the inherited one, to see
1100 * if your parent changed the cursor. But if you do, it changes the cursor
1101 * to an arrow, and we switch back quickly - producing flicker. I probably
1102 * should do something closer to what the SDK recmends - but this seems good
1103 * enuf for now - LGP 950212
1104 */
1105 BOOL result = false;
1106 if (result) {
1107 return true; // parent window handled it - see SDK docs
1108 }
1109
1110 // If cursor is over draggable text, then change cursor to an arrow.
1111 // Doug Stein says is according to Apple HIG guidelines - LGP 960804
1112 // SPR#0371
1113 if (this->GetSelectionStart () != this->GetSelectionEnd ()) {
1114 Region r;
1115 this->GetSelectionWindowRegion (&r, this->GetSelectionStart (), this->GetSelectionEnd ());
1116 if (r.PtInRegion (AsPOINT (fMouseTrackingLastPoint))) {
1117 ::SetCursor (::LoadCursor (nullptr, IDC_ARROW));
1118 return true;
1119 }
1120 }
1121
1122 ::SetCursor (::LoadCursor (nullptr, IDC_IBEAM));
1123
1124 return true;
1125 }
1126 else {
1127 return !!this->DefWindowProc (WM_SETCURSOR, WPARAM (hWnd), MAKELPARAM (nHitTest, message));
1128 }
1129 }
1130 template <typename BASE_INTERACTOR>
1131 /*
1132 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::OnGetDlgCode_Msg
1133 @DESCRIPTION: <p>Hook the Win32 SDK WM_GETDLGCODE message so that windows knows which messages to send to our WindowProc.
1134 See the Win32 SDK for more details.</p>
1135 */
1136 UINT Led_Win32_Helper<BASE_INTERACTOR>::OnGetDlgCode_Msg ()
1137 {
1138 DWORD style = GetStyle ();
1139 UINT dlogCode = DLGC_WANTARROWS | DLGC_HASSETSEL | DLGC_WANTCHARS;
1140 if ((style & ES_MULTILINE) and (style & ES_WANTRETURN)) {
1141 dlogCode |= DLGC_WANTALLKEYS;
1142 }
1143 return (dlogCode);
1144 }
1145 template <typename BASE_INTERACTOR>
1146 /*
1147 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::OnSetFocus_Msg
1148 @DESCRIPTION: <p>Called by the system when we receive the input focus. React by adjusting IME, showing cursor, etc.</p>
1149 */
1150 void Led_Win32_Helper<BASE_INTERACTOR>::OnSetFocus_Msg (HWND /*oldWnd*/)
1151 {
1152#if qStroika_Frameworks_Led_ProvideIMESupport
1153 IME::Get ().ForgetPosition ();
1154 IME::Get ().Enable ();
1155#endif
1156
1157 this->SetCaretShown (true);
1158 this->SetSelectionShown (true);
1159
1160#if qSupportWindowsSDKCallbacks
1161 // Notify the parent window...
1162 HWND hWnd = this->GetValidatedHWND ();
1163 (void)::SendMessage (::GetParent (hWnd), WM_COMMAND, (WPARAM)MAKELONG (GetWindowID (), EN_SETFOCUS), (LPARAM)(hWnd));
1164#endif
1165 }
1166 template <typename BASE_INTERACTOR>
1167 /*
1168 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::OnSetFocus_Msg
1169 @DESCRIPTION: <p>Called by the system when we lose the input focus. React by adjusting IME, showing cursor, etc.</p>
1170 */
1171 void Led_Win32_Helper<BASE_INTERACTOR>::OnKillFocus_Msg (HWND /*newWnd*/)
1172 {
1173 if (this->PeekAtTextStore () == nullptr) {
1174 /*
1175 * SPR#0893 - Its a reasonable practice to call SpecifyTextStore(nullptr) in the DTOR of some subclass of a Led-based editor.
1176 * Then - if the window was focused - in later DTORs - the window will be destroyed (Led_Win32_Helper<BASE_INTERACTOR>::DTOR calls
1177 * DestroyWindow). That invokes KillFocus. In that case - just silently drop the killfocus on the floor.
1178 */
1179 return;
1180 }
1181 this->BreakInGroupedCommands ();
1182
1183 this->SetCaretShown (false);
1184 this->SetSelectionShown (false);
1185
1186#if qSupportWindowsSDKCallbacks
1187 // Notify the parent window...
1188 HWND hWnd = this->GetValidatedHWND ();
1189 (void)::SendMessage (::GetParent (hWnd), WM_COMMAND, (WPARAM)MAKELONG (GetWindowID (), EN_KILLFOCUS), (LPARAM)(hWnd));
1190#endif
1191 }
1192 template <typename BASE_INTERACTOR>
1193 bool Led_Win32_Helper<BASE_INTERACTOR>::OnEraseBkgnd_Msg ([[maybe_unused]] HDC hDC)
1194 {
1195 // We don't need our background erased - we take care of that when we draw
1196 // Allowing it to be erased just produces flicker...
1197 return true;
1198 }
1199 template <typename BASE_INTERACTOR>
1200 void Led_Win32_Helper<BASE_INTERACTOR>::OnTimer_Msg (UINT_PTR nEventID, TIMERPROC* proc)
1201 {
1202 HWND hWnd = this->GetValidatedHWND ();
1203 if (nEventID == eAutoscrollingTimerEventID) {
1204 if (::GetCapture () == hWnd) {
1205 // Since we have the capture, we got the last mouse moved event, and so we have a VALID value stored in
1206 // fMouseTrackingLastPoint for the mouse location. Use that.
1207 this->WhileSimpleMouseTracking (fMouseTrackingLastPoint, fDragAnchor);
1208 }
1209 else {
1210 this->StopAutoscrollTimer ();
1211 }
1212 }
1213 else {
1214 this->DefWindowProc (WM_TIMER, nEventID, LPARAM (proc));
1215 }
1216 }
1217 template <typename BASE_INTERACTOR>
1218 void Led_Win32_Helper<BASE_INTERACTOR>::OnLButtonDown_Msg (UINT nFlags, int x, int y)
1219 {
1220 this->UpdateClickCount (Foundation::Time::GetTickCount (), Led_Point (y, x));
1221 this->OnNormalLButtonDown (nFlags, Led_Point (y, x));
1222 }
1223 template <typename BASE_INTERACTOR>
1224 void Led_Win32_Helper<BASE_INTERACTOR>::OnLButtonUp_Msg (UINT /*nFlags*/, int x, int y)
1225 {
1226 HWND hWnd = this->GetValidatedHWND ();
1227 AssertNotNull (hWnd);
1228 if (::GetCapture () == hWnd) {
1229 this->StopAutoscrollTimer ();
1230 Verify (::ReleaseCapture ());
1231 this->WhileSimpleMouseTracking (Led_Point (y, x), fDragAnchor);
1232 }
1233 }
1234 template <typename BASE_INTERACTOR>
1235 void Led_Win32_Helper<BASE_INTERACTOR>::OnLButtonDblClk_Msg (UINT /*nFlags*/, int x, int y)
1236 {
1237 this->IncrementCurClickCount (Foundation::Time::GetTickCount ());
1238
1239 bool extendSelection = !!(::GetKeyState (VK_SHIFT) & 0x8000);
1240 if (not this->ProcessSimpleClick (Led_Point (y, x), this->GetCurClickCount (), extendSelection, &fDragAnchor)) {
1241 return;
1242 }
1243
1244 HWND hWnd = this->GetValidatedHWND ();
1245 (void)::SetCapture (hWnd); // We could loop doing a sub-eventloop here - but it seems more common
1246 // Windows practice to just store up state information and wait on
1247 // WM_MOUSEMOVE and WM_LButtonUp messages...
1248 this->StartAutoscrollTimer ();
1249 }
1250 template <typename BASE_INTERACTOR>
1251 void Led_Win32_Helper<BASE_INTERACTOR>::OnMouseMove_Msg (UINT /*nFlags*/, int x, int y)
1252 {
1253 HWND hWnd = this->GetValidatedHWND ();
1254 fMouseTrackingLastPoint = Led_Point (y, x); // save for OnTimer_Msg ()
1255 if (::GetCapture () == hWnd) {
1256 this->WhileSimpleMouseTracking (Led_Point (y, x), fDragAnchor);
1257 }
1258 }
1259 template <typename BASE_INTERACTOR>
1260 void Led_Win32_Helper<BASE_INTERACTOR>::OnVScroll_Msg (UINT nSBCode, UINT /*nPos*/, HWND /*hScrollBar*/)
1261 {
1262 using namespace Characters::Literals;
1263 DbgTrace ("Led_Win32_Helper<BASE_INTERACTOR>::OnVScroll_Msg (nSBCode={},...)"_f, nSBCode);
1264
1265 using TextInteractor::eDefaultUpdate;
1266 using TextInteractor::eImmediateUpdate;
1267
1268 /*
1269 * NB: the nPos is a 16-bit value - and we could have a 32-bit offset - so use GetScrollInfo () to get the POS - rather
1270 * than using this parameter.
1271 */
1272 if (nSBCode == SB_LINEDOWN or nSBCode == SB_LINEUP or nSBCode == SB_PAGEDOWN or nSBCode == SB_PAGEUP) {
1273 if (not this->DelaySomeForScrollBarClick ()) {
1274 return;
1275 }
1276 }
1277
1278#if qDynamiclyChooseAutoScrollIncrement
1279 Foundation::Time::TimePointSeconds now = Foundation::Time::GetTickCount ();
1280 static Foundation::Time::TimePointSeconds sLastTimeThrough{};
1281 const Foundation::Time::DurationSeconds kClickThreshold = Led_GetDoubleClickTime () / 3;
1282 bool firstClick = (now - sLastTimeThrough > kClickThreshold);
1283
1284 int increment = firstClick ? 1 : 2;
1285#else
1286 const int increment = 1;
1287#endif
1288
1289#if qScrollTextDuringThumbTracking
1290 fSBarThumbTracking = (nSBCode == SB_THUMBTRACK);
1291#endif
1292 switch (nSBCode) {
1293 case SB_BOTTOM: {
1294 this->ScrollSoShowing (this->GetLength ());
1295 } break;
1296
1297 case SB_ENDSCROLL: {
1298 // Ignore
1299 } break;
1300
1301 case SB_LINEDOWN: {
1302 this->ScrollByIfRoom (increment, eImmediateUpdate);
1303 } break;
1304
1305 case SB_LINEUP: {
1306 this->ScrollByIfRoom (-increment, eImmediateUpdate);
1307 } break;
1308
1309 case SB_PAGEDOWN: {
1310 this->ScrollByIfRoom (this->GetTotalRowsInWindow ());
1311 } break;
1312
1313 case SB_PAGEUP: {
1314 this->ScrollByIfRoom (-(int)this->GetTotalRowsInWindow ());
1315 } break;
1316
1317 case SB_THUMBTRACK:
1318#if qScrollTextDuringThumbTracking
1319// Fall through into SB_THUMBPOSITION code
1320#else
1321 break;
1322#endif
1323 case SB_THUMBPOSITION: {
1324 size_t newPos = 0;
1325 {
1326 /*
1327 * It is not totally clear why we use GetVScrollInfo instead of the passed in
1328 * 'nPos' for tracking. The current docs (2003-01-20) seem to indicate
1329 * we should use the passed in 'nPos'. Oh well - this seems to be working OK.
1330 * -- LGP 2003-01-20.
1331 */
1332 SCROLLINFO scrollInfo = this->GetVScrollInfo ();
1333#if qScrollTextDuringThumbTracking
1334 newPos = scrollInfo.nTrackPos;
1335#else
1336 newPos = scrollInfo.nPos;
1337#endif
1338 newPos = min (newPos, this->GetLength ());
1339
1340 /*
1341 * Beware about the fact that the verticalWindow size changes as we scroll (since it
1342 * is measured in number of Led_tChars on display in the window).
1343 */
1344 if (newPos + scrollInfo.nPage >= static_cast<UINT> (scrollInfo.nMax)) {
1345 newPos = this->GetLength ();
1346 }
1347
1348#if qScrollTextDuringThumbTracking
1349 // Make sure nPos matches nTrackPos after tracking done
1350 scrollInfo.cbSize = sizeof (scrollInfo);
1351 scrollInfo.fMask = SIF_POS;
1352 scrollInfo.nPos = static_cast<UINT> (newPos);
1353 SetVScrollInfo (this->GetScrollBarType (TextInteractor::v), scrollInfo);
1354#endif
1355 }
1356
1357#if qScrollTextDuringThumbTracking
1358 this->InvalidateScrollBarParameters (); // In case below SetHScrollPos doesn't cause inval (due to caching), make sure
1359 // things really get recomputed
1360#endif
1361
1362 this->SetTopRowInWindowByMarkerPosition (newPos);
1363 } break;
1364
1365 case SB_TOP: {
1366 this->ScrollSoShowing (0);
1367 } break;
1368
1369 default: {
1370 Assert (false); // should be safe to ignore these - but if there are any xtras
1371 // I'd like to know...And asserts will be compiled out before
1372 // shipping- LGP 941026
1373 } break;
1374 }
1375#if qDynamiclyChooseAutoScrollIncrement
1376 sLastTimeThrough = now;
1377#endif
1378 }
1379 template <typename BASE_INTERACTOR>
1380 void Led_Win32_Helper<BASE_INTERACTOR>::OnHScroll_Msg (UINT nSBCode, UINT /*nPos*/, HWND /*hScrollBar*/)
1381 {
1382 /*
1383 * NB: the nPos is a 16-bit value - and we could have a 32-bit offset - so use GetScrollInfo () to get the POS - rather
1384 * than using this parameter.
1385 */
1386 if (nSBCode == SB_LINEDOWN or nSBCode == SB_LINEUP or nSBCode == SB_PAGEDOWN or nSBCode == SB_PAGEUP) {
1387 if (not this->DelaySomeForScrollBarClick ()) {
1388 return;
1389 }
1390 }
1391
1392#if qDynamiclyChooseAutoScrollIncrement
1393 Foundation::Time::TimePointSeconds now = Foundation::Time::GetTickCount ();
1394 static Foundation::Time::TimePointSeconds sLastTimeThrough{};
1395 const Foundation::Time::DurationSeconds kClickThreshold = Led_GetDoubleClickTime ();
1396 bool firstClick = (now - sLastTimeThrough > kClickThreshold);
1397
1398 int increment = firstClick ? 1 : 10;
1399#else
1400 const int increment = 1;
1401#endif
1402
1403 switch (nSBCode) {
1404 case SB_BOTTOM: {
1405 this->SetHScrollPos (this->ComputeMaxHScrollPos ());
1406 } break;
1407
1408 case SB_ENDSCROLL: {
1409 // ignore end of scrolling
1410 } break;
1411
1412 case SB_LINEDOWN: {
1413 this->SetHScrollPos (min<CoordinateType> (this->GetHScrollPos () + increment, this->ComputeMaxHScrollPos ()));
1414 } break;
1415
1416 case SB_LINEUP: {
1417 if (this->GetHScrollPos () > 0) {
1418 this->SetHScrollPos (max<CoordinateType> (0, int (this->GetHScrollPos ()) - increment));
1419 }
1420 } break;
1421
1422 case SB_PAGEDOWN: {
1423 const CoordinateType kPixelsAtATime = this->GetWindowRect ().GetWidth () / 2;
1424 this->SetHScrollPos (min<CoordinateType> (this->GetHScrollPos () + kPixelsAtATime, this->ComputeMaxHScrollPos ()));
1425 } break;
1426
1427 case SB_PAGEUP: {
1428 const CoordinateType kPixelsAtATime = this->GetWindowRect ().GetWidth () / 2;
1429 if (this->GetHScrollPos () > kPixelsAtATime) {
1430 this->SetHScrollPos (this->GetHScrollPos () - kPixelsAtATime);
1431 }
1432 else {
1433 this->SetHScrollPos (0);
1434 }
1435 } break;
1436
1437 case SB_THUMBTRACK:
1438#if qScrollTextDuringThumbTracking
1439// Fall through into SB_THUMBPOSITION code
1440#else
1441 break;
1442#endif
1443 case SB_THUMBPOSITION: {
1444#if qScrollTextDuringThumbTracking
1445 fSBarThumbTracking = (nSBCode == SB_THUMBTRACK);
1446#endif
1447 /*
1448 * It is not totally clear why we use GetHScrollInfo instead of the passed in
1449 * 'nPos' for tracking. The current docs (2003-01-20) seem to indicate
1450 * we should use the passed in 'nPos'. Oh well - this seems to be working OK.
1451 * -- LGP 2003-01-20.
1452 */
1453 SCROLLINFO scrollInfo = this->GetHScrollInfo ();
1454#if qScrollTextDuringThumbTracking
1455 size_t newPos = scrollInfo.nTrackPos;
1456#else
1457 size_t newPos = scrollInfo.nPos;
1458#endif
1459
1460#if qScrollTextDuringThumbTracking
1461 // Make sure nPos matches nTrackPos after tracking done
1462 scrollInfo.cbSize = sizeof (scrollInfo);
1463 scrollInfo.fMask = SIF_POS;
1464 scrollInfo.nPos = static_cast<int> (newPos);
1465 this->SetHScrollInfo (this->GetScrollBarType (TextInteractor::h), scrollInfo);
1466#endif
1467
1468#if qScrollTextDuringThumbTracking
1469 this->InvalidateScrollBarParameters (); // In case below SetHScrollPos doesn't cause inval (due to caching), make sure
1470 // things really get recomputed
1471#endif
1472
1473 this->SetHScrollPos (min<CoordinateType> (static_cast<CoordinateType> (newPos), this->ComputeMaxHScrollPos ()));
1474 } break;
1475
1476 case SB_TOP: {
1477 this->SetHScrollPos (0);
1478 } break;
1479
1480 default: {
1481 Assert (false); // should be safe to ignore these - but if there are any xtras
1482 // I'd like to know...And asserts will be compiled out before
1483 // shipping- LGP 941026
1484 } break;
1485 }
1486#if qDynamiclyChooseAutoScrollIncrement
1487 sLastTimeThrough = now;
1488#endif
1489 }
1490 template <typename BASE_INTERACTOR>
1491 bool Led_Win32_Helper<BASE_INTERACTOR>::OnMouseWheel_Msg (WPARAM wParam, LPARAM lParam)
1492 {
1493 using TextInteractor::eImmediateUpdate;
1494#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
1495 UINT keyState = LOWORD (wParam); //GET_KEYSTATE_WPARAM (wParam);
1496 // we don't handle anything but scrolling just now (comment from MFC).
1497 // Not really sure how these special keys are SUPPOSED to be treated?
1498 if (keyState & (MK_SHIFT | MK_CONTROL)) {
1499 return !!this->DefWindowProc (WM_MOUSEWHEEL, wParam, lParam);
1500 }
1501
1502 short zDelta = (short)HIWORD (wParam); //GET_WHEEL_DELTA_WPARAM (wParam);
1503 if ((zDelta >= 0) != (fAccumulatedWheelDelta >= 0)) {
1504 fAccumulatedWheelDelta = 0;
1505 }
1506 fAccumulatedWheelDelta += zDelta;
1507
1508 int nTicks = fAccumulatedWheelDelta / WHEEL_DELTA;
1509
1510 fAccumulatedWheelDelta %= WHEEL_DELTA;
1511
1512 int scrollLines = 3; //default
1513 Verify (::SystemParametersInfo (SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0));
1514
1515 // Pin - cuz result can be 'WHEEL_PAGESCROLL' - or just exceed a single page - and we are only supposed to scroll a max of
1516 // page per delta/WHEEL_DELTA, according to WM_MOUSEWHEEL docs.
1517 int totalRowsInWindow = static_cast<int> (this->GetTotalRowsInWindow ());
1518 scrollLines = min (scrollLines, totalRowsInWindow);
1519
1520 this->ScrollByIfRoom (-nTicks * scrollLines, eImmediateUpdate);
1521#endif
1522 return true;
1523 }
1524 template <typename BASE_INTERACTOR>
1525 /*
1526 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::GetDefaultWindowMargins
1527 @DESCRIPTION: <p>Arguments can be nullptr, and only non-nullptr pointers filled in.</p>
1528 <p>See @'Led_Win32_Helper<BASE_INTERACTOR>::SetDefaultWindowMargins'.</p>
1529 */
1530 inline TWIPS_Rect Led_Win32_Helper<BASE_INTERACTOR>::GetDefaultWindowMargins () const
1531 {
1532 return fDefaultWindowMargins;
1533 }
1534 template <typename BASE_INTERACTOR>
1535 /*
1536 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::SetDefaultWindowMargins
1537 @DESCRIPTION: <p>Sets the 'default window margins', retreivable through calls to
1538 @'Led_Win32_Helper<BASE_INTERACTOR>::GetDefaultWindowMargins'.</p>
1539 <p>These margins are not to be confused with those specified in @'WordWrappedTextImager::GetLayoutMargins'.</p>
1540 <p>These margins simply specify a small inset to be automatically applied to the ::GetClientRect()
1541 in the WM_SIZE message (handler) so that there is a border around this control. The WindowRect is simply
1542 inset by the amount given (and the borders automatically erased in this classes WM_ERASEBKGND method).</p>
1543 <p>Margins default to zero, and so its as if this feature simply defaults to being off.</p>
1544 <p>Margins are specified in @'TWIPS'.</p>
1545 */
1546 void Led_Win32_Helper<BASE_INTERACTOR>::SetDefaultWindowMargins (const TWIPS_Rect& defaultWindowMargins)
1547 {
1548 if (fDefaultWindowMargins != defaultWindowMargins) {
1549 fDefaultWindowMargins = defaultWindowMargins;
1550 if (this->GetHWND () != nullptr) {
1551 this->OnSize_ (); // take into account new margins...
1552 }
1553 }
1554 }
1555 template <typename BASE_INTERACTOR>
1556 /*
1557 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::GetControlArrowsScroll
1558 @DESCRIPTION: <p>See also @'Led_Win32_Helper<BASE_INTERACTOR>::SetControlArrowsScroll'</p>
1559 */
1560 inline bool Led_Win32_Helper<BASE_INTERACTOR>::GetControlArrowsScroll () const
1561 {
1562 return fControlArrowsScroll;
1563 }
1564 template <typename BASE_INTERACTOR>
1565 /*
1566 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::SetControlArrowsScroll
1567 @DESCRIPTION: <p>If this property is true - then when the user does a CNTRL->UP-ARROW key or CNTRL-DOWN-ARROW key,
1568 this does scrolling. If false - it does the default behavior of going to the start or end of
1569 the current paragraph (or if already at that end point - to the start/end of the prev/following paragraph)</p>
1570 <p>This property defaults to <em>false</em>.</p>
1571 <p>See also @'Led_Win32_Helper<BASE_INTERACTOR>::GetControlArrowsScroll'</p>
1572 */
1573 void Led_Win32_Helper<BASE_INTERACTOR>::SetControlArrowsScroll (bool controlArrowsScroll)
1574 {
1575 fControlArrowsScroll = controlArrowsScroll;
1576 }
1577 template <typename BASE_INTERACTOR>
1578 /*
1579 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::GetFunnyMSPageUpDownAdjustSelectionBehavior
1580 @DESCRIPTION: <p>See @'Led_Win32_Helper<BASE_INTERACTOR>::SetFunnyMSPageUpDownAdjustSelectionBehavior'.</p>
1581 */
1582 inline bool Led_Win32_Helper<BASE_INTERACTOR>::GetFunnyMSPageUpDownAdjustSelectionBehavior () const
1583 {
1584 return fFunnyMSPageUpDownAdjustSelectionBehavior;
1585 }
1586 template <typename BASE_INTERACTOR>
1587 /*
1588 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::SetFunnyMSPageUpDownAdjustSelectionBehavior
1589 @DESCRIPTION: <p>In Windows page up/down have a very funny interpretation. Playing with
1590 MSWord and Notepad, I see they each do something different, and I really
1591 understood neither. But both agreed that after a page up/down, you reset
1592 the selection somehow. I tried to mimic the behavior of MS-word as well
1593 as I could. This behavior is SO bizzare, I've made it optional. But since
1594 it appears to be an MS standard - its ON by default.</p>
1595 <p>Turned ON by default</p>
1596 <p>See also @'Led_Win32_Helper<BASE_INTERACTOR>::GetFunnyMSPageUpDownAdjustSelectionBehavior'.</p>
1597 <p>See also @'FunnyMSPageUpDownAdjustSelectionHelper'.</p>
1598 */
1599 void Led_Win32_Helper<BASE_INTERACTOR>::SetFunnyMSPageUpDownAdjustSelectionBehavior (bool funnyMSPageUpDownAdjustSelectionBehavior)
1600 {
1601 fFunnyMSPageUpDownAdjustSelectionBehavior = funnyMSPageUpDownAdjustSelectionBehavior;
1602 }
1603 template <typename BASE_INTERACTOR>
1604 void Led_Win32_Helper<BASE_INTERACTOR>::OnEnable_Msg (bool /*enable*/)
1605 {
1606 this->Refresh ();
1607 }
1608 template <typename BASE_INTERACTOR>
1609 void Led_Win32_Helper<BASE_INTERACTOR>::OnSize_ ()
1610 {
1611 using namespace Characters::Literals;
1612 this->InvalidateScrollBarParameters (); // Cuz even if no layoutwidth change, we still change page size for scrollbars, if any...
1613 RECT cr;
1614 Led_Rect r;
1615 {
1616 HWND hWnd = this->GetValidatedHWND ();
1617 Verify (::GetClientRect (hWnd, &cr));
1618 r = AsLedRect (cr);
1619 }
1620 try {
1621 TextInteractor::Tablet_Acquirer tablet (this);
1622 Led_Rect wmr = tablet->CvtFromTWIPS (GetDefaultWindowMargins ());
1623 r.top += wmr.GetTop ();
1624 r.left += wmr.GetLeft ();
1625 r.bottom -= wmr.GetBottom ();
1626 r.right -= wmr.GetRight ();
1627 }
1628 catch (...) {
1629 Assert (false);
1630 }
1631 // Assure margins didn't produce an invalid WindowRect
1632 if (r.top >= r.bottom) {
1633 r.bottom = r.top + 1;
1634 }
1635 if (r.left >= r.right) {
1636 r.right = r.left + 1;
1637 }
1638 DbgTrace ("Led_Win32_Helper<>::OnSize_ (clientRect=(%d,%d,%d,%d), windowRect <= (%d,%d,%d,%d))"_f, cr.top, cr.left, cr.bottom,
1639 cr.right, r.top, r.left, r.bottom, r.right);
1640 this->SetWindowRect (r);
1641 }
1642 template <typename BASE_INTERACTOR>
1643 void Led_Win32_Helper<BASE_INTERACTOR>::RefreshWindowRect_ (const Led_Rect& windowRectArea, UpdateMode updateMode) const
1644 {
1645 HWND hWnd = GetHWND ();
1646 if (hWnd != nullptr) {
1647 Assert (::IsWindow (hWnd));
1648 updateMode = this->RealUpdateMode (updateMode);
1649 switch (updateMode) {
1650 case TextInteractor::eDelayedUpdate: {
1651 if (not windowRectArea.IsEmpty ()) {
1652 RECT tmp = AsRECT (windowRectArea);
1653 Verify (::InvalidateRect (hWnd, &tmp, true));
1654 }
1655 } break;
1656 case TextInteractor::eImmediateUpdate: {
1657 if (not windowRectArea.IsEmpty ()) {
1658 RECT tmp = AsRECT (windowRectArea);
1659 Verify (::RedrawWindow (hWnd, &tmp, nullptr, RDW_INVALIDATE | RDW_UPDATENOW));
1660 }
1661 } break;
1662 }
1663 }
1664 }
1665 template <typename BASE_INTERACTOR>
1666 void Led_Win32_Helper<BASE_INTERACTOR>::UpdateWindowRect_ (const Led_Rect& windowRectArea) const
1667 {
1668 if (not windowRectArea.IsEmpty ()) {
1669 // Unclear if I should use RedrawWindow (&windowRectArea, RDW_UPDATENOW or UpdateWindow???
1670 RECT tmp = AsRECT (windowRectArea);
1671 HWND hWnd = GetHWND ();
1672 if (hWnd != nullptr) {
1673 Assert (::IsWindow (hWnd));
1674 Verify (::RedrawWindow (hWnd, &tmp, nullptr, RDW_UPDATENOW));
1675 }
1676 }
1677 }
1678 template <typename BASE_INTERACTOR>
1679 bool Led_Win32_Helper<BASE_INTERACTOR>::QueryInputKeyStrokesPending () const
1680 {
1681 MSG msg;
1682 return (!!::PeekMessage (&msg, GetValidatedHWND (), WM_KEYDOWN, WM_KEYDOWN, PM_NOREMOVE));
1683 }
1684 template <typename BASE_INTERACTOR>
1685 Tablet* Led_Win32_Helper<BASE_INTERACTOR>::AcquireTablet () const
1686 {
1687 Require (fAcquireCount < 100); // not really a requirement - but hard to see how this could happen in LEGIT usage...
1688 // almost certainly a bug...
1689 if (fUpdateTablet != nullptr) {
1690 ++fAcquireCount;
1691 return fUpdateTablet;
1692 }
1693
1694 /*
1695 * Note that we only allocate the memory once, and don't free it, only constructing the
1696 * windows object on the fly so as to avoid memory fragmentation resulting from the
1697 * editor (for LEC's memory concerns - LGP 950524).
1698 */
1699 if (fAcquireCount == 0) {
1700 Assert (fAllocatedTablet.m_hDC == nullptr);
1701 Assert (fAllocatedTablet.m_hAttribDC == nullptr);
1702 HWND hWnd = GetHWND ();
1703 if (hWnd == nullptr) {
1704 throw TextInteractor::NoTabletAvailable ();
1705 }
1706 Assert (::IsWindow (hWnd));
1707 HDC hdc = ::GetWindowDC (hWnd);
1708 AssertNotNull (hdc);
1709 Verify (fAllocatedTablet.Attach (hdc));
1710 }
1711 Assert (fAllocatedTablet.m_hDC != nullptr);
1712 Assert (fAllocatedTablet.m_hAttribDC != nullptr);
1713 fAcquireCount++;
1714 return (&fAllocatedTablet);
1715 }
1716 template <typename BASE_INTERACTOR>
1717 void Led_Win32_Helper<BASE_INTERACTOR>::ReleaseTablet (Tablet* tablet) const
1718 {
1719 AssertNotNull (tablet);
1720 Assert (fAcquireCount > 0);
1721 fAcquireCount--;
1722 Assert (tablet == fUpdateTablet or tablet == &fAllocatedTablet);
1723 if (fAcquireCount == 0 and tablet == &fAllocatedTablet) {
1724 Verify (::ReleaseDC (GetValidatedHWND (), fAllocatedTablet.Detach ()));
1725 }
1726 }
1727 template <typename BASE_INTERACTOR>
1728 /*
1729 @METHOD: Led_Win32_Helper<BASE_CLASS>::WindowDrawHelper
1730 @DESCRIPTION: <p>Share some code among various methods which invoke message-paint-based drawing. Helpful in some
1731 places like ActiveLedIt! where we don't get message WM_PAINT at all, but get from the control.</p>
1732 */
1733 void Led_Win32_Helper<BASE_INTERACTOR>::WindowDrawHelper (Tablet* tablet, const Led_Rect& subsetToDraw, bool printing)
1734 {
1735 using namespace Characters::Literals;
1736 DbgTrace ("Led_Win32_Helper<>::WindowDrawHelper (subsetToDraw= (%d, %d, %d, %d))"_f, subsetToDraw.top, subsetToDraw.left,
1737 subsetToDraw.bottom, subsetToDraw.right);
1738 TemporarilyUseTablet tmpUseTablet{*this, tablet, TemporarilyUseTablet::eDontDoTextMetricsChangedCall};
1739 this->Draw (subsetToDraw, printing);
1740
1741 /*
1742 * The user specified a DefaultWindowMargin - lies outside of our WindowRect, and so
1743 * won't get drawn by the normal Led draw mechanism. Make sure it gets erased here.
1744 */
1745 Led_Rect wr = this->GetWindowRect ();
1746 TWIPS_Rect wm = this->GetDefaultWindowMargins ();
1747 if (wm.GetTop () != 0 or wm.GetLeft () != 0 or wm.GetBottom () != 0 or wm.GetRight () != 0) {
1748 Led_Rect wmr = tablet->CvtFromTWIPS (wm);
1749 // Erase our TOP margin
1750 {
1751 Led_Rect barRect = subsetToDraw;
1752 barRect.bottom = wr.top;
1753 if (not barRect.IsEmpty ()) {
1754 this->EraseBackground (tablet, barRect, false);
1755 }
1756 }
1757 // Erase our LHS margin
1758 {
1759 Led_Rect barRect = subsetToDraw;
1760 barRect.right = wr.left;
1761 if (not barRect.IsEmpty ()) {
1762 this->EraseBackground (tablet, barRect, false);
1763 }
1764 }
1765 // Erase our Bottom margin
1766 {
1767 Led_Rect barRect = subsetToDraw;
1768 barRect.top = wr.bottom;
1769 if (not barRect.IsEmpty ()) {
1770 this->EraseBackground (tablet, barRect, false);
1771 }
1772 }
1773 // Erase our RHS margin
1774 {
1775 Led_Rect barRect = subsetToDraw;
1776 barRect.left = wr.right;
1777 if (not barRect.IsEmpty ()) {
1778 this->EraseBackground (tablet, barRect, false);
1779 }
1780 }
1781 }
1782
1783 this->UpdateCaretState_ ();
1784 }
1785 template <typename BASE_INTERACTOR>
1786 /*
1787 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::EraseBackground
1788 @DESCRIPTION:
1789 */
1790 void Led_Win32_Helper<BASE_INTERACTOR>::EraseBackground (Tablet* tablet, const Led_Rect& subsetToDraw, bool printing)
1791 {
1792 DWORD dwStyle = GetStyle ();
1793 if (((dwStyle & WS_DISABLED) or (dwStyle & ES_READONLY)) and (not printing)) {
1794 //const Color kReadOnlyBackground = Color (RGB (0xc0,0xc0,0xc0));
1795 //const Color kReadOnlyBackground = Color (::GetSysColor (COLOR_SCROLLBAR));
1796 const Color kReadOnlyBackground = Color (::GetSysColor (COLOR_BTNFACE));
1797 tablet->EraseBackground_SolidHelper (subsetToDraw, kReadOnlyBackground);
1798 }
1799 else {
1800 inherited::EraseBackground (tablet, subsetToDraw, printing);
1801 }
1802 }
1803 template <typename BASE_INTERACTOR>
1804 bool Led_Win32_Helper<BASE_INTERACTOR>::CheckIfDraggingBeepAndReturn ()
1805 {
1806 HWND hWnd = GetHWND ();
1807 if (hWnd != nullptr and ::GetCapture () == hWnd) {
1808// we must be tracking - drop characters typed at that point on the floor
1809// send a beep message as well to indicate that the characters are being dropped!
1810#if 1
1811 // As temphack to ameliorate SPR#1065 - just directly do a sysbeep here
1812 Led_BeepNotify ();
1813#else
1814 OnBadUserInput ();
1815#endif
1816 return true;
1817 }
1818 return false;
1819 }
1820 template <typename BASE_INTERACTOR>
1821 /*
1822 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::HandleTabCharacterTyped
1823 @DESCRIPTION: <p>Win32 SDK kind of queer in its OnGetDlgCode () API. In order to get enter keys, we see to have
1824 to request ALL_KEYS. Then - that means we get TAB keys - even if we didn't want them, and wanted them processed by
1825 the outer window automatically.</p>
1826 <p>So - try to guestimate the right default behavior here for handing a tab character. And in the end - leave it
1827 virtual so users can get what they want.</p>
1828 */
1829 void Led_Win32_Helper<BASE_INTERACTOR>::HandleTabCharacterTyped ()
1830 {
1831 HWND hWnd = this->GetValidatedHWND ();
1832 if (this->GetStyle () & WS_TABSTOP) {
1833 HWND parent = ::GetParent (hWnd);
1834 if (parent != nullptr) {
1835 bool shiftPressed = !!(::GetKeyState (VK_SHIFT) & 0x8000);
1836 if (shiftPressed) {
1837 ::PostMessage (parent, WM_NEXTDLGCTL, 1, false);
1838 }
1839 else {
1840 ::PostMessage (parent, WM_NEXTDLGCTL, 0, false);
1841 }
1842 }
1843 }
1844 else {
1845 this->OnTypedNormalCharacter ('\t', false, !!(::GetKeyState (VK_SHIFT) & 0x8000), false,
1846 !!(::GetKeyState (VK_CONTROL) & 0x8000), !!(::GetKeyState (VK_MENU) & 0x8000));
1847#if qSupportWindowsSDKCallbacks
1848 (void)::SendMessage (::GetParent (hWnd), WM_COMMAND, MAKELONG (GetWindowID (), EN_CHANGE), (LPARAM)hWnd);
1849#endif
1850 }
1851 }
1852 template <typename BASE_INTERACTOR>
1853 /*
1854 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::AboutToUpdateText
1855 @DESCRIPTION: <p>Override to hook @'MarkerOwner::AboutToUpdateText' and check for modifications when we are READONLY or DISABLED.</p>
1856 */
1857 void Led_Win32_Helper<BASE_INTERACTOR>::AboutToUpdateText (const UpdateInfo& updateInfo)
1858 {
1859 if (GetHWND () != nullptr and this->CheckIfCurrentUpdateIsInteractive () and updateInfo.fRealContentUpdate) {
1860 /*
1861 * Make sure we've been realized (fully constructed), and also only check if this is an interactive update.
1862 */
1863 DWORD dwStyle = this->GetStyle ();
1864 if (dwStyle & WS_DISABLED || dwStyle & ES_READONLY) {
1865 this->OnBadUserInput (); // should throw out
1866 }
1867 }
1868 inherited::AboutToUpdateText (updateInfo);
1869 }
1870 template <typename BASE_INTERACTOR>
1871 /*
1872 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::DidUpdateText
1873 @DESCRIPTION: <p>Override to hook @'MarkerOwner::DidUpdateText' and call @'Led_Win32_Helper<BASE_INTERACTOR>::DidUpdateText_'.</p>
1874 */
1875 void Led_Win32_Helper<BASE_INTERACTOR>::DidUpdateText (const UpdateInfo& updateInfo) noexcept
1876 {
1877 inherited::DidUpdateText (updateInfo);
1878 this->DidUpdateText_ (updateInfo);
1879 }
1880 template <typename BASE_INTERACTOR>
1881 /*
1882 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::DidUpdateText_
1883 @DESCRIPTION: <p>When the text is modified between clicks, that should reset any click count. Besides the logic of this,
1884 its actually IMPORTANT todo in case the change in text invalidates the fDragAnchor.</p>
1885 */
1886 inline void Led_Win32_Helper<BASE_INTERACTOR>::DidUpdateText_ (const UpdateInfo& updateInfo) noexcept
1887 {
1888 if (updateInfo.fTextModified) {
1889 this->SetCurClickCount (0, Foundation::Time::GetTickCount ());
1890 }
1891 fDragAnchor = min (fDragAnchor, this->GetEnd ()); // SPR#0637 reported we could sometimes get crash while mousing/typing. This SHOULD prevent fDragAnchor ever
1892 // getting invalid... NB: I never reproduced the problem.
1893 }
1894 template <typename BASE_INTERACTOR>
1895 void Led_Win32_Helper<BASE_INTERACTOR>::SetScrollBarType (VHSelect vh, ScrollBarType scrollBarType)
1896 {
1897 if (this->GetScrollBarType (vh) != scrollBarType) {
1898 inherited::SetScrollBarType (vh, scrollBarType);
1899 HWND hWnd = this->GetHWND ();
1900 if (hWnd != nullptr) {
1901 ::InvalidateRect (hWnd, nullptr, true);
1902 }
1903 }
1904 }
1905 template <typename BASE_INTERACTOR>
1906 /*
1907 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::ShouldUpdateHScrollBar
1908 @DESCRIPTION: <p>Return true if Led_Win32_Helper<BASE_INTERACTOR> should automaticly generate
1909 @'Led_Win32_Helper<BASE_INTERACTOR>::SetHScrollInfo' calls.</p>
1910 <p>See also @'Led_Win32_Helper<BASE_INTERACTOR>::SetHScrollInfo',
1911 @'Led_Win32_Helper<BASE_INTERACTOR>::ShouldUpdateVScrollBar'.</p>
1912 */
1913 bool Led_Win32_Helper<BASE_INTERACTOR>::ShouldUpdateHScrollBar () const
1914 {
1915 //NB: we must update sbar even if NEVER - when the style is ON, cuz we may need to HIDE the sbar
1916 return this->GetScrollBarType (TextInteractor::h) != TextInteractor::eScrollBarNever or (this->GetStyle () & WS_HSCROLL);
1917 }
1918 template <typename BASE_INTERACTOR>
1919 /*
1920 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::ShouldUpdateHScrollBar
1921 @DESCRIPTION: <p>See @'Led_Win32_Helper<BASE_INTERACTOR>::ShouldUpdateHScrollBar'.</p>
1922 */
1923 bool Led_Win32_Helper<BASE_INTERACTOR>::ShouldUpdateVScrollBar () const
1924 {
1925 //NB: we must update sbar even if NEVER - when the style is ON, cuz we may need to HIDE the sbar
1926 return this->GetScrollBarType (TextInteractor::v) != TextInteractor::eScrollBarNever or (GetStyle () & WS_VSCROLL);
1927 }
1928 template <typename BASE_INTERACTOR>
1929 /*
1930 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::TypeAndScrollInfoSBVisible
1931 @ACCESS: protected
1932 @DESCRIPTION: <p>Check if given the ScrollBarType and scrollInfo - if the scrollbar itself should be visible (possibly disabled)
1933 or not (invisible).</p>
1934 */
1935 bool Led_Win32_Helper<BASE_INTERACTOR>::TypeAndScrollInfoSBVisible (ScrollBarType scrollbarAppears, const SCROLLINFO& scrollInfo) const
1936 {
1937 return (scrollbarAppears == TextInteractor::eScrollBarAlways) or
1938 (scrollbarAppears == TextInteractor::eScrollBarAsNeeded and
1939 scrollInfo.nMin + static_cast<int> (scrollInfo.nPage) <= scrollInfo.nMax);
1940 }
1941 template <typename BASE_INTERACTOR>
1942 /*
1943 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::GetHScrollInfo
1944 @DESCRIPTION: <p>Simple, virtual wrapper on GetScrollInfo (SB_HORZ,...) call. Virtual so
1945 you can easily override it in a Led subclass, and use a source of scrollbars, other than
1946 the default - and still leverage the scrollbar maintainance logic in Led_Win32_Helper<BASE_INTERACTOR>.
1947 For example, if you had your OWN scroll bars in some outer window, which OWNED a Led subwindow,
1948 this would be an easy way to tie them together.</p>
1949 <p>See also @'Led_Win32_Helper<BASE_INTERACTOR>::SetHScrollInfo', @'Led_Win32_Helper<BASE_INTERACTOR>::GetVScrollInfo'.</p>
1950 */
1951 SCROLLINFO Led_Win32_Helper<BASE_INTERACTOR>::GetHScrollInfo (UINT nMask) const
1952 {
1953 ::SCROLLINFO scrollInfo{};
1954 scrollInfo.cbSize = sizeof (scrollInfo);
1955 scrollInfo.fMask = nMask;
1956 Verify (::GetScrollInfo (this->GetValidatedHWND (), SB_HORZ, &scrollInfo));
1957 return scrollInfo;
1958 }
1959 template <typename BASE_INTERACTOR>
1960 /*
1961 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::SetHScrollInfo
1962 @DESCRIPTION: <p>See @'Led_Win32_Helper<BASE_INTERACTOR>::GetHScrollInfo'</p>
1963 */
1964 void Led_Win32_Helper<BASE_INTERACTOR>::SetHScrollInfo (ScrollBarType scrollbarAppears, const SCROLLINFO& scrollInfo, bool redraw)
1965 {
1966 using namespace Characters::Literals;
1967 bool showBar = TypeAndScrollInfoSBVisible (scrollbarAppears, scrollInfo);
1968
1969 DbgTrace ("Led_Win32_Helper<>::SetHScrollInfo (scrollbarAppears={}, smin={}, smax={}, nPage={}, nPos={}) ==> showBar={})"_f,
1970 static_cast<int> (scrollbarAppears), scrollInfo.nMin, scrollInfo.nMax, scrollInfo.nPage, scrollInfo.nPos, showBar);
1971
1972 /*
1973 * As near as I can tell - the below call to ::SetScrollInfo () should be sufficient to show/hide the SBAR. And - often
1974 * it is. But - unfortunately - sometimes its not. This seems to be the only way to assure it always gets re-shown.
1975 * -- LGP 2003-11-05
1976 */
1977 ::ShowScrollBar (this->GetValidatedHWND (), SB_HORZ, showBar);
1978 SCROLLINFO si = scrollInfo;
1979 if (showBar) {
1980 si.fMask |= SIF_DISABLENOSCROLL;
1981 }
1982 else {
1983 si.fMask &= ~SIF_DISABLENOSCROLL;
1984 }
1985 (void)::SetScrollInfo (this->GetValidatedHWND (), SB_HORZ, &si, redraw);
1986 }
1987 template <typename BASE_INTERACTOR>
1988 void Led_Win32_Helper<BASE_INTERACTOR>::SetHScrollInfo (const SCROLLINFO& scrollInfo, bool redraw)
1989 {
1990 this->SetHScrollInfo (this->GetScrollBarType (TextInteractor::h), scrollInfo, redraw);
1991 }
1992 template <typename BASE_INTERACTOR>
1993 /*
1994 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::GetVScrollInfo
1995 @DESCRIPTION: <p>See @'Led_Win32_Helper<BASE_INTERACTOR>::GetHScrollInfo'</p>
1996 */
1997 SCROLLINFO Led_Win32_Helper<BASE_INTERACTOR>::GetVScrollInfo (UINT nMask) const
1998 {
1999 ::SCROLLINFO scrollInfo{};
2000 scrollInfo.cbSize = sizeof (scrollInfo);
2001 scrollInfo.fMask = nMask;
2002 Verify (::GetScrollInfo (this->GetValidatedHWND (), SB_VERT, &scrollInfo));
2003 return scrollInfo;
2004 }
2005 template <typename BASE_INTERACTOR>
2006 /*
2007 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::SetVScrollInfo
2008 @DESCRIPTION: <p>See @'Led_Win32_Helper<BASE_INTERACTOR>::GetHScrollInfo'</p>
2009 */
2010 void Led_Win32_Helper<BASE_INTERACTOR>::SetVScrollInfo (ScrollBarType scrollbarAppears, const SCROLLINFO& scrollInfo, bool redraw)
2011 {
2012 using namespace Characters::Literals;
2013 bool showBar = TypeAndScrollInfoSBVisible (scrollbarAppears, scrollInfo);
2014 DbgTrace ("Led_Win32_Helper<>::SetVScrollInfo (scrollbarAppears={}, smin={}, smax={}, nPage={}, nPos={}) ==> showBar={})"_f,
2015 static_cast<int> (scrollbarAppears), scrollInfo.nMin, scrollInfo.nMax, scrollInfo.nPage, scrollInfo.nPos, showBar);
2016
2017 /*
2018 * As near as I can tell - the below call to ::SetScrollInfo () should be sufficient to show/hide the SBAR. And - often
2019 * it is. But - unfortunately - sometimes its not. This seems to be the only way to assure it always gets re-shown.
2020 * -- LGP 2003-11-05
2021 */
2022 ::ShowScrollBar (this->GetValidatedHWND (), SB_VERT, showBar);
2023 SCROLLINFO si = scrollInfo;
2024 if (showBar) {
2025 si.fMask |= SIF_DISABLENOSCROLL;
2026 }
2027 else {
2028 si.fMask &= ~SIF_DISABLENOSCROLL;
2029 }
2030 (void)::SetScrollInfo (this->GetValidatedHWND (), SB_VERT, &si, redraw);
2031 }
2032 template <typename BASE_INTERACTOR>
2033 void Led_Win32_Helper<BASE_INTERACTOR>::SetVScrollInfo (const SCROLLINFO& scrollInfo, bool redraw)
2034 {
2035 this->SetVScrollInfo (this->GetScrollBarType (TextInteractor::v), scrollInfo, redraw);
2036 }
2037 template <typename BASE_INTERACTOR>
2038 void Led_Win32_Helper<BASE_INTERACTOR>::InvalidateScrollBarParameters ()
2039 {
2040 HWND hWnd = this->GetHWND ();
2041 if (hWnd != nullptr) { // only if we've been realized!
2042 Assert (::IsWindow (hWnd));
2043 inherited::InvalidateScrollBarParameters_ ();
2044 this->InvalidateScrollBarParameters_ ();
2045 }
2046 }
2047 template <typename BASE_INTERACTOR>
2048 inline void Led_Win32_Helper<BASE_INTERACTOR>::InvalidateScrollBarParameters_ ()
2049 {
2050 this->InvalidateCaretState ();
2051 }
2052 template <typename BASE_INTERACTOR>
2053 void Led_Win32_Helper<BASE_INTERACTOR>::InvalidateCaretState ()
2054 {
2055 /*
2056 * Note that we simply invalidate the area where the caret will go (or where it was)
2057 * and we actually update the position in our draw method because otherwise
2058 * if the user code changed the selection many times at once - we might see the caret
2059 * dance all over the screen. We could force an update on a timer even just
2060 * in case we never get a draw event - and that might be wise - but the problem is
2061 * that this might still happen while the owning software was performing so
2062 * long procedure. For now - we'll keep things simple. Maybe later if a need appears
2063 * we can use a timer - or some other notification.
2064 */
2065 HWND hWnd = this->GetHWND ();
2066 if (hWnd != nullptr) {
2067 Assert (::IsWindow (hWnd));
2068 ::HideCaret (hWnd);
2069 }
2070 inherited::InvalidateCaretState ();
2071 }
2072 template <typename BASE_INTERACTOR>
2073 void Led_Win32_Helper<BASE_INTERACTOR>::UpdateCaretState_ ()
2074 {
2075 HWND hWnd = this->GetHWND ();
2076 if (hWnd != nullptr) {
2077 if (this->GetCaretShown () and this->GetCaretShownSituation () and ::GetFocus () == hWnd) {
2078 // also turn off/on based on if empty selection...
2079
2080 ::HideCaret (hWnd);
2081
2082 Led_Rect caretRect = this->CalculateCaretRect ();
2083
2084 if (caretRect.IsEmpty ()) {
2085#if qStroika_Frameworks_Led_ProvideIMESupport
2086 // if caret is to be invisible, then make sure the IME is moved
2087 // offscreen (maybe should be hidden?) - SPR#1359
2088 Led_Rect wr = this->GetWindowRect ();
2089 IME::Get ().NotifyPosition (hWnd, (SHORT)wr.GetLeft (), (SHORT)wr.GetBottom () + 1000);
2090#endif
2091 }
2092 else {
2093 // NB: I tried being clever and not calling ::CreateCaret () if the height hadn't changed,
2094 // but I just got burned for my troubles. It seems when other windows create the caret, it
2095 // obliterates our creation. And we get no notification, and as far as I can tell - cannot
2096 // check the characteristics of the current caret. So we must re-create!!! Ugly - but seems
2097 // to do no harm (no flicker) - LGP 950215
2098 ::CreateCaret (hWnd, (HBITMAP)0, caretRect.GetWidth (), caretRect.GetHeight ());
2099
2100 /*
2101 * Somewhat risky approach to updating the caret - but the braindead win32 API really gives
2102 * us little choice. ShowCaret MAY NOT show caret. Either cuz we don't own it, it hasn't
2103 * been created, or cuz there have been too many hidecaret calls. We assure the first two
2104 * aren't the case, and then use a while loop to take care of the third problem. But if MS
2105 * intruduces some forth - and this fails - we could infinite loop here - UGH - LGP 950323
2106 */
2107 Assert (::IsWindow (hWnd));
2108 // This SHOULD work, according to the docs, but seems to hang on HT-J 3.5??? - LGP 950524
2109 // Seems to work on all versions since NTJ 3.5 however...
2110 while (not::ShowCaret (hWnd)) {
2111 }
2112
2113 ::SetCaretPos (caretRect.GetLeft (), caretRect.GetTop ());
2114
2115// When we support the IME - it probably needs to be notified here!!!
2116#if qStroika_Frameworks_Led_ProvideIMESupport
2117 IME::Get ().NotifyPosition (hWnd, (SHORT)caretRect.GetLeft (), (SHORT)caretRect.GetTop ());
2118#endif
2119 }
2120 }
2121 else {
2122 ::HideCaret (hWnd);
2123 }
2124 }
2125 }
2126 template <typename BASE_INTERACTOR>
2127 /*
2128 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::UpdateScrollBars
2129 @DESCRIPTION: <p>Hook the @'TextInteractor::UpdateScrollBars' () notification routine to update the values in
2130 our scrollbars.</p>
2131 */
2132 void Led_Win32_Helper<BASE_INTERACTOR>::UpdateScrollBars ()
2133 {
2134 using namespace Characters::Literals;
2135 DbgTrace ("Led_Win32_Helper<>::UpdateScrollBars () with winStart={}, winEnd={})"_f, this->GetMarkerPositionOfStartOfWindow (),
2136 this->GetMarkerPositionOfEndOfWindow ());
2137
2138// Don't allow SetVScrollInfo/SetHScrollInfo () calls during a thumb track - because MS Windows scrollbar
2139// control SOMETIMES doesn't react well (npos not properly adjusted) when you reset the page size during
2140// a drag of the thumb.
2141// It APEARS BUG IS THAT IT DOESNT CHANGE GRAPHIC SIZE OF THUMB WHILE IN THE MIDDLE OF A THUMB DRAG -
2142// EXCEPT!!! IF WE RESIZE WINDOW AT SAME TIME (by chaning
2143// whther HORZ scrollbar is shown due to dynamic compuation of it being needed - and THEN we get it resized at that point).
2144// That makes compute of if we are at end of sbar or where we are almost impossible (cuz we don't know graphic/visual size of thumb)
2145// -- LGP 2003-11-04
2146#if qScrollTextDuringThumbTracking
2147 if (fSBarThumbTracking) {
2148 return;
2149 }
2150#endif
2151
2152 inherited::UpdateScrollBars_ ();
2153
2154 HWND hWnd = this->GetHWND ();
2155 if (hWnd != nullptr) {
2156// Hmm. Think this is wrong ... Wrong place for this ... Leave alone for now. Fix later (and HORZ CASE TOO) LGP 970107
2157#if qSupportWindowsSDKCallbacks
2158 (void)::SendMessage (::GetParent (hWnd), WM_COMMAND, MAKELONG (GetWindowID (), EN_VSCROLL), (LPARAM)hWnd);
2159#endif
2160
2161 if (this->ShouldUpdateHScrollBar ()) {
2162 ::SCROLLINFO scrollInfo;
2163 (void)::memset (&scrollInfo, 0, sizeof (scrollInfo));
2164 scrollInfo.cbSize = sizeof (scrollInfo);
2165 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
2166
2167 scrollInfo.nMin = 0; // always use zero as base
2168 scrollInfo.nPage = this->GetWindowRect ().GetWidth ();
2169 scrollInfo.nMax = scrollInfo.nPage + this->ComputeMaxHScrollPos ();
2170 scrollInfo.nPos = this->GetHScrollPos ();
2171
2172 // set pagesize ++ since that appears to be what the MS SDK expects. They expect
2173 // min+page > (not >=) max implies the evevator is full, and nPos+nPage> (not >=) nMax implies at END OF DOC
2174 // -- LGP 2003-11-05
2175 ++scrollInfo.nPage;
2176
2177 this->SetHScrollInfo (this->GetScrollBarType (TextInteractor::h), scrollInfo);
2178 }
2179 if (this->ShouldUpdateVScrollBar ()) {
2180 size_t startOfWindow = this->GetMarkerPositionOfStartOfWindow ();
2181 size_t endOfWindow = this->GetMarkerPositionOfEndOfWindow ();
2182 size_t verticalWindowSpan = endOfWindow - startOfWindow;
2183
2184 ::SCROLLINFO scrollInfo;
2185 (void)::memset (&scrollInfo, 0, sizeof (scrollInfo));
2186 scrollInfo.cbSize = sizeof (scrollInfo);
2187 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
2188
2189 scrollInfo.nMin = 0; // always use zero as base
2190 scrollInfo.nMax = static_cast<int> (this->GetLength ());
2191 scrollInfo.nPage = static_cast<UINT> (verticalWindowSpan);
2192 scrollInfo.nPos = static_cast<int> (startOfWindow);
2193
2194 // set pagesize ++ since that appears to be what the MS SDK expects. They expect
2195 // min+page > (not >=) max implies the evevator is full, and nPos+nPage> (not >=) nMax implies at END OF DOC
2196 // -- LGP 2003-11-05
2197 ++scrollInfo.nPage;
2198
2199 this->SetVScrollInfo (this->GetScrollBarType (TextInteractor::v), scrollInfo);
2200 }
2201 }
2202 }
2203 template <typename BASE_INTERACTOR>
2204 void Led_Win32_Helper<BASE_INTERACTOR>::OnNormalLButtonDown (UINT /*nFlags*/, const Led_Point& at)
2205 {
2206 this->BreakInGroupedCommands ();
2207 fMouseTrackingLastPoint = at;
2208 if (not(this->GetStyle () & WS_DISABLED)) {
2209 HWND hWnd = this->GetValidatedHWND ();
2210
2211 if (::GetFocus () != hWnd) {
2212 (void)::SetFocus (hWnd);
2213 }
2214
2215 bool extendSelection = !!(::GetKeyState (VK_SHIFT) & 0x8000);
2216
2217 if (not this->ProcessSimpleClick (at, this->GetCurClickCount (), extendSelection, &fDragAnchor)) {
2218 return;
2219 }
2220
2221 /*
2222 * Note that we do this just AFTER setting the first selection. This is to avoid
2223 * (if we had done it earlier) displaying the OLD selection and then quickly
2224 * erasing it (flicker). We do an Update () rather than simply use eImmediateUpdate
2225 * mode on the SetSelection () because at this point we want to redisplay the entire
2226 * window if part of it needed it beyond just the part within the selection.
2227 */
2228 // Update_ ();
2229 this->Update ();
2230
2231 (void)::SetCapture (hWnd); // We could loop doing a sub-eventloop here - but it seems more common
2232 // Windows practice to just store up state information and wait on
2233 // WM_MOUSEMOVE and WM_LButtonUp messages...
2234 this->StartAutoscrollTimer ();
2235
2236 Assert (fDragAnchor <= this->GetEnd ()); // Subtle point. fDragAnchor is passed as a hidden variable to
2237 // other routines - but ONLY when we have the CAPTURE. If the capture
2238 // is not set, then 'fDragAnchor' is assumed to have an invalid value,
2239 // This implies that text shouldn't be changed while we have the mouse
2240 // captured.
2241 }
2242 }
2243 template <typename BASE_INTERACTOR>
2244 void Led_Win32_Helper<BASE_INTERACTOR>::StartAutoscrollTimer ()
2245 {
2246 /*
2247 * Not sure about this - just for debug sake??? - Did LButonUp get dropped on the floor somehow? - LGP 960530
2248 */
2249 Assert (fAutoScrollTimerID == 0);
2250
2251 if (fAutoScrollTimerID == 0) {
2252 const int kTimeout = 20; // 20 milliseconds - update autoscroll every 1/50
2253 // second.
2254 Verify ((fAutoScrollTimerID = ::SetTimer (this->GetValidatedHWND (), eAutoscrollingTimerEventID, kTimeout, nullptr)) != 0);
2255 }
2256 }
2257 template <typename BASE_INTERACTOR>
2258 void Led_Win32_Helper<BASE_INTERACTOR>::StopAutoscrollTimer ()
2259 {
2260 if (fAutoScrollTimerID != 0) {
2261 Verify (::KillTimer (this->GetValidatedHWND (), eAutoscrollingTimerEventID));
2262 fAutoScrollTimerID = 0;
2263 }
2264 }
2265 template <typename BASE_INTERACTOR>
2266 bool Led_Win32_Helper<BASE_INTERACTOR>::OnCopyCommand_Before ()
2267 {
2268 if (not::OpenClipboard (this->GetValidatedHWND ())) {
2269 this->OnBadUserInput ();
2270 return false;
2271 }
2272 (void)::EmptyClipboard (); // should we test for errors?
2273 bool result = inherited::OnCopyCommand_Before ();
2274 if (not result) {
2275 Verify (::CloseClipboard ());
2276 return false;
2277 }
2278 return result;
2279 }
2280 template <typename BASE_INTERACTOR>
2281 void Led_Win32_Helper<BASE_INTERACTOR>::OnCopyCommand_After ()
2282 {
2283 inherited::OnCopyCommand_After ();
2284 if (not::CloseClipboard ()) {
2285 this->OnBadUserInput ();
2286 }
2287 }
2288 template <typename BASE_INTERACTOR>
2289 bool Led_Win32_Helper<BASE_INTERACTOR>::OnPasteCommand_Before ()
2290 {
2291 if (not::OpenClipboard (this->GetValidatedHWND ())) {
2292 this->OnBadUserInput ();
2293 return false;
2294 }
2295 bool result = inherited::OnPasteCommand_Before ();
2296 if (not result) {
2297 Verify (::CloseClipboard ());
2298 return false;
2299 }
2300 return result;
2301 }
2302 template <typename BASE_INTERACTOR>
2303 void Led_Win32_Helper<BASE_INTERACTOR>::OnPasteCommand_After ()
2304 {
2305 inherited::OnPasteCommand_After ();
2306 if (not::CloseClipboard ()) {
2307 this->OnBadUserInput ();
2308 }
2309 }
2310 template <typename BASE_INTERACTOR>
2311 /*
2312 @METHOD: Led_Win32_Helper<BASE_INTERACTOR>::GetStyle
2313 @DESCRIPTION: <p>Return the HWND's style (::GetWindowLong(...,GWL_STYLE). If the
2314 HWND is nullptr, then return a style of 0.</p>
2315 */
2316 nonvirtual DWORD Led_Win32_Helper<BASE_INTERACTOR>::GetStyle () const
2317 {
2318 HWND hWnd = GetHWND ();
2319 if (hWnd == nullptr) {
2320 return 0;
2321 }
2322 else {
2323 Assert (::IsWindow (hWnd));
2324 return (DWORD)::GetWindowLong (hWnd, GWL_STYLE);
2325 }
2326 }
2327 template <typename BASE_INTERACTOR>
2328 inline int Led_Win32_Helper<BASE_INTERACTOR>::GetWindowID () const
2329 {
2330 return (::GetWindowLong (this->GetValidatedHWND (), GWL_ID));
2331 }
2332 template <typename BASE_INTERACTOR>
2333 inline HWND Led_Win32_Helper<BASE_INTERACTOR>::GetValidatedHWND () const
2334 {
2335 HWND hWnd = GetHWND ();
2336 AssertNotNull (hWnd);
2337 Assert (::IsWindow (hWnd));
2338 return (hWnd);
2339 }
2340
2341//class Led_Win32_Helper<BASE_INTERACTOR>::TemporarilyUseTablet
2342#if !qNestedClassesInTemplateClassesDontExpandCompilerBug
2343 template <typename BASE_INTERACTOR>
2344 inline Led_Win32_Helper<BASE_INTERACTOR>::TemporarilyUseTablet::TemporarilyUseTablet (Led_Win32_Helper<BASE_INTERACTOR>& editor,
2345 Tablet* t, DoTextMetricsChangedCall tmChanged)
2346 : fEditor{editor}
2347 , fOldTablet{editor.fUpdateTablet}
2348 , fDoTextMetricsChangedCall{tmChanged}
2349 {
2350 editor.fUpdateTablet = t;
2351 if (tmChanged == eDoTextMetricsChangedCall) {
2352 editor.TabletChangedMetrics ();
2353 }
2354 }
2355 template <typename BASE_INTERACTOR>
2356 inline Led_Win32_Helper<BASE_INTERACTOR>::TemporarilyUseTablet::~TemporarilyUseTablet ()
2357 {
2358 fEditor.fUpdateTablet = fOldTablet;
2359 if (fDoTextMetricsChangedCall == eDoTextMetricsChangedCall) {
2360 fEditor.TabletChangedMetrics ();
2361 }
2362 }
2363#endif
2364
2365 //class Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>
2366 template <typename BASECLASS>
2367 Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::Led_Win32_Win32SDKMessageMimicHelper ()
2368 : inherited{}
2369 {
2370 }
2371 template <typename BASECLASS>
2372 /*
2373 @METHOD: Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::HandleMessage
2374 @DESCRIPTION: <p>Handle the given windows message, and return true if handled, and false otherwise. If returns
2375 true, then 'lResult' is set on exit.</p>
2376 */
2377 bool Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::HandleMessage (UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result)
2378 {
2379 RequireNotNull (result);
2380 switch (message) {
2381 case WM_SETTEXT:
2382 *result = this->OnMsgSetText (wParam, lParam);
2383 return true;
2384 case WM_GETTEXT:
2385 *result = this->OnMsgGetText (wParam, lParam);
2386 return true;
2387 case WM_GETTEXTLENGTH:
2388 *result = this->OnMsgGetTextLength (wParam, lParam);
2389 return true;
2390 case EM_GETSEL:
2391 *result = this->OnMsgGetSel (wParam, lParam);
2392 return true;
2393 case EM_SETREADONLY:
2394 *result = this->OnMsgSetReadOnly (wParam, lParam);
2395 return true;
2396 case EM_GETFIRSTVISIBLELINE:
2397 *result = this->OnMsgGetFirstVisibleLine (wParam, lParam);
2398 return true;
2399 case EM_LINEINDEX:
2400 *result = this->OnMsgLineIndex (wParam, lParam);
2401 return true;
2402 case EM_GETLINECOUNT:
2403 *result = this->OnMsgLineCount (wParam, lParam);
2404 return true;
2405 case EM_CANUNDO:
2406 *result = this->OnMsgCanUndo (wParam, lParam);
2407 return true;
2408 case EM_UNDO:
2409 *result = this->OnMsgUndo (wParam, lParam);
2410 return true;
2411 case EM_EMPTYUNDOBUFFER:
2412 *result = this->OnMsgEmptyUndoBuffer (wParam, lParam);
2413 return true;
2414 case WM_CLEAR:
2415 *result = this->OnMsgClear (wParam, lParam);
2416 return true;
2417 case WM_CUT:
2418 *result = this->OnMsgCut (wParam, lParam);
2419 return true;
2420 case WM_COPY:
2421 *result = this->OnMsgCopy (wParam, lParam);
2422 return true;
2423 case WM_PASTE:
2424 *result = this->OnMsgPaste (wParam, lParam);
2425 return true;
2426 case EM_LINEFROMCHAR:
2427 *result = this->OnMsgLineFromChar (wParam, lParam);
2428 return true;
2429 case EM_LINELENGTH:
2430 *result = this->OnMsgLineLength (wParam, lParam);
2431 return true;
2432 case EM_LINESCROLL:
2433 *result = this->OnMsgLineScroll (wParam, lParam);
2434 return true;
2435 case EM_REPLACESEL:
2436 *result = this->OnMsgReplaceSel (wParam, lParam);
2437 return true;
2438 case EM_SETSEL:
2439 *result = this->OnMsgSetSel (wParam, lParam);
2440 return true;
2441 case EM_SCROLLCARET:
2442 *result = this->OnMsgScrollCaret (wParam, lParam);
2443 return true;
2444 case WM_GETFONT:
2445 *result = this->OnMsgGetFont (wParam, lParam);
2446 return true;
2447 case WM_SETFONT:
2448 *result = this->OnMsgSetFont (wParam, lParam);
2449 return true;
2450 default:
2451 return false;
2452 }
2453 }
2454 template <typename BASECLASS>
2455 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgGetText (WPARAM wParam, LPARAM lParam)
2456 {
2457 using namespace Stroika::Foundation;
2458 int cchTextMax = (int)wParam;
2459 LPSTR lpText = (LPSTR)lParam;
2460 Require (cchTextMax > 0); // cuz we require appending NUL character
2461
2462 size_t len = this->GetLength ();
2463 Memory::StackBuffer<Led_tChar> buf{Memory::eUninitialized, len};
2464 this->CopyOut (0, len, buf.data ());
2465 size_t len2 = 2 * len;
2466 Memory::StackBuffer<Led_tChar> buf2{Memory::eUninitialized, len2};
2467 len2 = Characters::NLToNative<Led_tChar> (buf.data (), len, buf2.data (), len2);
2468 // Assume they want ANSI code page text?
2469 int nChars = ::WideCharToMultiByte (CP_ACP, 0, buf2.data (), static_cast<int> (len2), lpText, cchTextMax - 1, nullptr, nullptr);
2470 lpText[nChars] = '\0';
2471 return nChars;
2472 }
2473 template <typename BASECLASS>
2474 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgSetText (WPARAM /*wParam*/, LPARAM lParam)
2475 {
2476 using namespace Stroika::Foundation;
2477 LPSTR lpText = (LPSTR)lParam;
2478
2479 /*
2480 * Note that by doing a Delete followed by an Insert() instead of just doing
2481 * a replace - we are in-effect interpretting this as destroying all
2482 * markers. And - also - this SetTextPtr () operation could fail in the middle
2483 * leaving an empty text buffer as the result.
2484 */
2485 this->Replace (0, this->GetEnd (), LED_TCHAR_OF (""), 0);
2486 if (lpText != nullptr) {
2487 size_t len = ::strlen (lpText);
2488 Memory::StackBuffer<Led_tChar> buf{Memory::eUninitialized, len};
2489 // Assume they want ANSI code page text?
2490 len = static_cast<size_t> (::MultiByteToWideChar (CP_ACP, 0, lpText, static_cast<int> (len), buf.data (), static_cast<int> (len)));
2491 len = Characters::NormalizeTextToNL<Led_tChar> (buf.data (), len, buf.data (), len);
2492 this->Replace (0, 0, buf.data (), len);
2493 }
2494
2495 return 0;
2496 }
2497 template <typename BASECLASS>
2498 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgGetTextLength (WPARAM /*wParam*/, LPARAM /*lParam*/)
2499 {
2500 //*2 cuz of CRLF worst case, and *2 cuz of unicode->MBYTE worst case (is that so - worst case? - maybe not needed to multiply there).
2501 return (4 * this->GetLength ()); // always enuf for unicode chars...
2502 }
2503 template <typename BASECLASS>
2504 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgGetSel (WPARAM wParam, LPARAM lParam)
2505 {
2506 DWORD iSelStart = static_cast<DWORD> (this->GetSelectionStart ());
2507 DWORD iSelEnd = static_cast<DWORD> (this->GetSelectionEnd ());
2508 if (wParam != 0) {
2509 *reinterpret_cast<DWORD*> (wParam) = iSelStart;
2510 }
2511 if (lParam != 0) {
2512 *reinterpret_cast<DWORD*> (lParam) = iSelEnd;
2513 }
2514 DWORD dw = (((WORD)iSelStart) << 16) | ((WORD)iSelEnd);
2515 return dw;
2516 }
2517 template <typename BASECLASS>
2518 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgGetFirstVisibleLine (WPARAM /*wParam*/, LPARAM /*lParam*/)
2519 {
2520 Assert (this->GetTopRowInWindow () >= 0);
2521 return this->GetTopRowInWindow ();
2522 }
2523 template <typename BASECLASS>
2524 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgSetReadOnly (WPARAM wParam, LPARAM /*lParam*/)
2525 {
2526 HWND hWnd = this->GetValidatedHWND ();
2527 DWORD dwStyle = this->GetStyle ();
2528 bool readOnly = !!wParam;
2529 if (readOnly) {
2530 dwStyle |= ES_READONLY;
2531 }
2532 else {
2533 dwStyle &= ~ES_READONLY;
2534 }
2535 DWORD dwStyle1 = ::GetWindowLong (hWnd, GWL_STYLE);
2536 if (dwStyle != dwStyle1) {
2537 ::SetWindowLong (hWnd, GWL_STYLE, dwStyle);
2538 {
2539 TextInteractor* ti = this;
2540 ti->Refresh ();
2541 } // funky refresh call to avoid ambiguity when baseclass has other 'Refresh' methods - as in COleControl
2542 if (this->GetCommandHandler () != nullptr) {
2543 this->GetCommandHandler ()->Commit ();
2544 }
2545 }
2546 return 1;
2547 }
2548 template <typename BASECLASS>
2549 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgLineIndex (WPARAM wParam, LPARAM /*lParam*/)
2550 {
2551 size_t row = (int (wParam) == -1) ? this->GetRowContainingPosition (this->GetSelectionEnd ()) : (size_t (wParam));
2552 if (row < 0) {
2553 row = 0;
2554 }
2555 row = min (row, this->GetRowCount () - 1);
2556 return (this->GetStartOfRow (row));
2557 }
2558 template <typename BASECLASS>
2559 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgLineCount (WPARAM /*wParam*/, LPARAM /*lParam*/)
2560 {
2561 return this->GetRowCount (); // 1-based for SDK API here...
2562 }
2563 template <typename BASECLASS>
2564 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgCanUndo (WPARAM /*wParam*/, LPARAM /*lParam*/)
2565 {
2566 CommandHandler* ch = this->GetCommandHandler ();
2567 return ch != nullptr and ch->CanUndo ();
2568 }
2569 template <typename BASECLASS>
2570 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgUndo (WPARAM /*wParam*/, LPARAM /*lParam*/)
2571 {
2572 CommandHandler* ch = this->GetCommandHandler ();
2573 if (ch != nullptr and ch->CanUndo ()) {
2574 this->OnUndoCommand ();
2575 return (true);
2576 }
2577 return (false);
2578 }
2579 template <typename BASECLASS>
2580 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgEmptyUndoBuffer (WPARAM /*wParam*/, LPARAM /*lParam*/)
2581 {
2582 CommandHandler* ch = this->GetCommandHandler ();
2583 if (ch != nullptr) {
2584 ch->Commit ();
2585 }
2586 return 0; // return value ignored...
2587 }
2588 template <typename BASECLASS>
2589 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgClear (WPARAM /*wParam*/, LPARAM /*lParam*/)
2590 {
2591 this->OnClearCommand ();
2592#if qSupportWindowsSDKCallbacks
2593 HWND hWnd = this->GetValidatedHWND ();
2594 (void)::SendMessage (::GetParent (hWnd), WM_COMMAND, MAKELONG (::GetWindowLong (hWnd, GWL_ID), EN_CHANGE), (LPARAM)hWnd);
2595#endif
2596 return 0; // return value ignored...
2597 }
2598 template <typename BASECLASS>
2599 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgCut (WPARAM /*wParam*/, LPARAM /*lParam*/)
2600 {
2601 this->OnCutCommand ();
2602#if qSupportWindowsSDKCallbacks
2603 HWND hWnd = this->GetValidatedHWND ();
2604 (void)::SendMessage (::GetParent (hWnd), WM_COMMAND, MAKELONG (::GetWindowLong (hWnd, GWL_ID), EN_CHANGE), (LPARAM)hWnd);
2605#endif
2606 return 0; // return value ignored...
2607 }
2608 template <typename BASECLASS>
2609 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgCopy (WPARAM /*wParam*/, LPARAM /*lParam*/)
2610 {
2611 this->OnCopyCommand ();
2612 return 0; // return value ignored...
2613 }
2614 template <typename BASECLASS>
2615 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgPaste (WPARAM /*wParam*/, LPARAM /*lParam*/)
2616 {
2617 this->OnPasteCommand ();
2618#if qSupportWindowsSDKCallbacks
2619 HWND hWnd = this->GetValidatedHWND ();
2620 (void)::SendMessage (::GetParent (hWnd), WM_COMMAND, MAKELONG (::GetWindowLong (hWnd, GWL_ID), EN_CHANGE), (LPARAM)hWnd);
2621#endif
2622 return 0; // return value ignored...
2623 }
2624 template <typename BASECLASS>
2625 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgLineFromChar (WPARAM wParam, LPARAM /*lParam*/)
2626 {
2627 ptrdiff_t nIndex = ptrdiff_t (wParam);
2628 if (nIndex == -1) {
2629 nIndex = static_cast<ptrdiff_t> (this->GetSelectionStart ());
2630 }
2631 if (nIndex < 0) {
2632 nIndex = 0;
2633 }
2634 Assert (nIndex >= 0); // so we can cast to size_t safely
2635 if (size_t (nIndex) > this->GetEnd ()) {
2636 nIndex = this->GetEnd ();
2637 }
2638 return this->GetRowContainingPosition (nIndex);
2639 }
2640 template <typename BASECLASS>
2641 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgLineLength (WPARAM wParam, LPARAM /*lParam*/)
2642 {
2643 size_t row = (int (wParam) == -1) ? this->GetRowContainingPosition (this->GetSelectionEnd ()) : (size_t (wParam));
2644 if (row < 0) {
2645 row = 0;
2646 }
2647 if (row > this->GetRowCount () - 1) {
2648 row = this->GetRowCount () - 1;
2649 }
2650 return this->GetEndOfRow (row) - this->GetStartOfRow (row); // Not sure if we should count NL, or not???
2651 }
2652 template <typename BASECLASS>
2653 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgLineScroll (WPARAM wParam, LPARAM lParam)
2654 {
2655 [[maybe_unused]] int nChars = int (wParam); // NOTE THAT FOR NOW WE IGNORE nChars since we only
2656 // support word-wrapped text for this release...
2657 int nLines = int (lParam);
2658 this->ScrollByIfRoom (nLines);
2659 return !!(this->GetStyle () & ES_MULTILINE);
2660 }
2661 template <typename BASECLASS>
2662 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgReplaceSel ([[maybe_unused]] WPARAM wParam, LPARAM lParam)
2663 {
2664 using namespace Stroika::Foundation;
2665 Assert (wParam == 0);
2666 LPCTSTR text = (LPCTSTR)lParam;
2667
2668 size_t len = ::_tcslen (text);
2669 Memory::StackBuffer<Led_tChar> buf{Memory::eUninitialized, len};
2670
2671#if qTargetPlatformSDKUseswchar_t
2672 //::_tcscpy (buf, text);
2673 (void)::memcpy (buf.begin (), text, (len + 1) * sizeof (text[0]));
2674#else
2675 len = ::MultiByteToWideChar (CP_ACP, 0, text, len, buf, len); // Assume they want ANSI code page text?
2676#endif
2677 size_t nLen = Characters::NormalizeTextToNL<Led_tChar> (buf.data (), len, buf.data (), len);
2678 Assert (ValidateTextForCharsetConformance (buf.data (), nLen));
2679 this->Replace (this->GetSelectionStart (), this->GetSelectionEnd (), buf.data (), nLen);
2680 return 0; // result ignored...
2681 }
2682 template <typename BASECLASS>
2683 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgSetSel (WPARAM wParam, LPARAM lParam)
2684 {
2685 // This code doesn't EXACTLY reproduce the quirks of the SDK Docs for this function
2686 // (in particular if nStart == 0, and removing a caret) - but it should be close
2687 // enough...
2688 ptrdiff_t nStart = ptrdiff_t (wParam);
2689 ptrdiff_t nEnd = ptrdiff_t (lParam);
2690 if (nEnd == -1) {
2691 nEnd = this->GetLength ();
2692 }
2693 if (nStart < 0) {
2694 nStart = 0;
2695 }
2696 if (nEnd < 0) {
2697 nEnd = 0;
2698 }
2699 if (size_t (nEnd) > this->GetEnd ()) {
2700 nEnd = static_cast<ptrdiff_t> (this->GetEnd ());
2701 }
2702 if (nStart > nEnd) {
2703 swap (nStart, nEnd);
2704 }
2705 this->SetSelection (static_cast<ptrdiff_t> (nStart), static_cast<ptrdiff_t> (nEnd));
2706 return 0; // result ignored...
2707 }
2708 template <typename BASECLASS>
2709 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgScrollCaret (WPARAM /*wParam*/, LPARAM /*lParam*/)
2710 {
2711 this->ScrollToSelection ();
2712 return 0; // result ignored...
2713 }
2714 template <typename BASECLASS>
2715 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgGetFont (WPARAM /*wParam*/, LPARAM /*lParam*/)
2716 {
2717 /*
2718 * just use fDefaultFontCache as a convenient way to keep track of this object
2719 * so we delete it in the end...
2720 *
2721 * But - we want to be careful not only delete this when need be. We could update
2722 * this cache every time the font changes. But I'd rather have all this localized,
2723 * and only pay the price when this function is called. This function is probably
2724 * quite rarely called (certainly not by anything within Led itself!).
2725 *
2726 * The reason we want to delete this only when we have to, is because we have no
2727 * way of knowing who kept refernces to the HFONT we return. So we delete it only
2728 * when we change fonts, and someone asks again - and on DTOR of this object.
2729 */
2730 FontSpecification defaultFont = this->GetDefaultSelectionFont ();
2731 LOGFONT defaultFontLF;
2732 defaultFont.GetOSRep (&defaultFontLF);
2733
2734 if (fDefaultFontCache.m_hObject != nullptr) { // Seeing if font changed...
2735 FontObject tmpHackToTestFont;
2736 Verify (tmpHackToTestFont.CreateFontIndirect (&defaultFontLF));
2737
2738 ::LOGFONT tmpHackToTestFontLF{};
2739 Verify (tmpHackToTestFont.GetObject (sizeof (tmpHackToTestFontLF), &tmpHackToTestFontLF));
2740
2741 ::LOGFONT currentLF{};
2742 Verify (fDefaultFontCache.GetObject (sizeof (currentLF), &currentLF));
2743
2744 if (::memcmp (&currentLF, &tmpHackToTestFontLF, sizeof (currentLF)) != 0) {
2745 fDefaultFontCache.DeleteObject ();
2746 Assert (fDefaultFontCache.m_hObject == nullptr);
2747 }
2748 }
2749
2750 if (fDefaultFontCache.m_hObject == nullptr) {
2751 fDefaultFontCache.CreateFontIndirect (&defaultFontLF);
2752 }
2753 return (LRESULT)fDefaultFontCache.m_hObject;
2754 }
2755 template <typename BASECLASS>
2756 LRESULT Led_Win32_Win32SDKMessageMimicHelper<BASECLASS>::OnMsgSetFont (WPARAM wParam, LPARAM lParam)
2757 {
2758 IncrementalFontSpecification fontSpec;
2759 {
2760 HFONT fontToUse = reinterpret_cast<HFONT> (wParam);
2761 if (fontToUse == nullptr) {
2762 fontToUse = reinterpret_cast<HFONT> (::GetStockObject (DEFAULT_GUI_FONT));
2763 }
2764 LOGFONT lf;
2765 ::GetObject (fontToUse, sizeof (lf), &lf);
2766 fontSpec.SetOSRep (lf);
2767 }
2768 this->SetDefaultFont (fontSpec, lParam ? TextInteractor::eDefaultUpdate : TextInteractor::eNoUpdate);
2769 return 0;
2770 }
2771
2772 // class SimpleWin32WndProcHelper
2773 inline SimpleWin32WndProcHelper::SimpleWin32WndProcHelper ()
2774 : fHWnd{nullptr}
2775 , fSuperWindowProc{nullptr}
2776 {
2777 }
2778 inline HWND SimpleWin32WndProcHelper::GetHWND () const
2779 {
2780 return fHWnd;
2781 }
2782 inline void SimpleWin32WndProcHelper::SetHWND (HWND hWnd)
2783 {
2784 if (fHWnd != nullptr) {
2785 ::SetWindowLongPtr (fHWnd, GWLP_USERDATA, 0); // reset back to original value
2786 }
2787 fHWnd = hWnd;
2788 if (fHWnd != nullptr) {
2789 ::SetWindowLongPtr (fHWnd, GWLP_USERDATA, reinterpret_cast<DWORD_PTR> (this));
2790 }
2791 }
2792 inline HWND SimpleWin32WndProcHelper::GetValidatedHWND () const
2793 {
2794 HWND hWnd = this->GetHWND ();
2795 AssertNotNull (hWnd);
2796 Assert (::IsWindow (hWnd));
2797 return (hWnd);
2798 }
2799 inline void SimpleWin32WndProcHelper::Create (LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth,
2800 int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance)
2801 {
2802 Create (0L, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance);
2803 }
2804 inline LRESULT SimpleWin32WndProcHelper::SendMessage (UINT msg, WPARAM wParam, LPARAM lParam)
2805 {
2806#if !qTargetPlatformSDKUseswchar_t
2807 if (IsWindowUNICODE ()) {
2808 return ::SendMessageW (this->GetValidatedHWND (), msg, wParam, lParam);
2809 }
2810#endif
2811 return ::SendMessage (this->GetValidatedHWND (), msg, wParam, lParam);
2812 }
2813 inline bool SimpleWin32WndProcHelper::IsWindowRealized () const
2814 {
2815 return this->GetHWND () != nullptr;
2816 }
2817 inline void SimpleWin32WndProcHelper::Assert_Window_Realized () const
2818 {
2819 Assert (this->IsWindowRealized ());
2820 }
2821 inline void SimpleWin32WndProcHelper::Require_Window_Realized () const
2822 {
2823 Require (this->IsWindowRealized ());
2824 }
2825 inline bool SimpleWin32WndProcHelper::IsWindowUNICODE () const
2826 {
2827 Require_Window_Realized ();
2828 return !!::IsWindowUnicode (GetHWND ());
2829 }
2830 inline bool SimpleWin32WndProcHelper::IsWindowShown () const
2831 {
2832 Require_Window_Realized ();
2833 return !!::IsWindowVisible (GetHWND ());
2834 }
2835 inline void SimpleWin32WndProcHelper::SetWindowVisible (bool shown)
2836 {
2837 Require_Window_Realized ();
2838 (void)::ShowWindow (GetHWND (), shown ? SW_SHOW : SW_HIDE);
2839 }
2840 /*
2841 @METHOD: SimpleWin32WndProcHelper::SubclassWindow
2842 @DESCRIPTION: <p>See also @'SimpleWin32WndProcHelper::SubclassWindowW'.</p>
2843 */
2844 inline bool SimpleWin32WndProcHelper::SubclassWindow (HWND hWnd)
2845 {
2846 Require (fSuperWindowProc == nullptr); // don't call twice!
2847 Require (fHWnd == nullptr); // don't call after already created! - use this instead of SetHWnd ()!!!
2848 RequireNotNull (hWnd);
2849 Require (::IsWindow (hWnd));
2850 fSuperWindowProc = reinterpret_cast<WNDPROC> (::SetWindowLongPtr (hWnd, GWLP_WNDPROC, reinterpret_cast<DWORD_PTR> (StaticWndProc)));
2851 if (fSuperWindowProc == nullptr) {
2852 return false;
2853 }
2854 SetHWND (hWnd);
2855 return true;
2856 }
2857 /*
2858 @METHOD: SimpleWin32WndProcHelper::SubclassWindowW
2859 @DESCRIPTION: <p>Same as @'SimpleWin32WndProcHelper::SubclassWindow' except that we create a 'UNICODE' window.</p>
2860 */
2861 inline bool SimpleWin32WndProcHelper::SubclassWindowW (HWND hWnd)
2862 {
2863 Require (fSuperWindowProc == nullptr); // don't call twice!
2864 Require (fHWnd == nullptr); // don't call after already created! - use this instead of SetHWnd ()!!!
2865 RequireNotNull (hWnd);
2866 Require (::IsWindow (hWnd));
2867 fSuperWindowProc = reinterpret_cast<WNDPROC> (::SetWindowLongPtr (hWnd, GWLP_WNDPROC, reinterpret_cast<DWORD_PTR> (StaticWndProc)));
2868 if (fSuperWindowProc == nullptr) {
2869 return false;
2870 }
2871 SetHWND (hWnd);
2872 return true;
2873 }
2874 inline LRESULT SimpleWin32WndProcHelper::WndProc (UINT message, WPARAM wParam, LPARAM lParam)
2875 {
2876 return this->DefWindowProc (message, wParam, lParam);
2877 }
2878 inline LRESULT SimpleWin32WndProcHelper::DefWindowProc (UINT message, WPARAM wParam, LPARAM lParam)
2879 {
2880#if !qTargetPlatformSDKUseswchar_t
2881 if (IsWindowUNICODE ()) {
2882 if (fSuperWindowProc == nullptr) {
2883 return ::DefWindowProcW (this->GetValidatedHWND (), message, wParam, lParam);
2884 }
2885 else {
2886 return ::CallWindowProcW (fSuperWindowProc, this->GetValidatedHWND (), message, wParam, lParam);
2887 }
2888 }
2889#endif
2890 if (fSuperWindowProc == nullptr) {
2891 return ::DefWindowProc (this->GetValidatedHWND (), message, wParam, lParam);
2892 }
2893 else {
2894 return ::CallWindowProc (fSuperWindowProc, this->GetValidatedHWND (), message, wParam, lParam);
2895 }
2896 }
2897
2898 // class Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>
2899 template <typename BASE_WIN32_HELPER>
2900 Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>::Led_Win32_SimpleWndProc_Helper ()
2901 : BASE_WIN32_HELPER{}
2902 , fHWnd{nullptr}
2903 , fSuperWindowProc{nullptr}
2904 {
2905 }
2906 template <typename BASE_WIN32_HELPER>
2907 Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>::~Led_Win32_SimpleWndProc_Helper ()
2908 {
2909 if (GetHWND () != nullptr) {
2910 ::DestroyWindow (GetHWND ());
2911 }
2912 }
2913 template <typename BASE_WIN32_HELPER>
2914 void Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>::Create (LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y,
2915 int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance)
2916 {
2917 Create (0L, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance);
2918 }
2919 template <typename BASE_WIN32_HELPER>
2920 /*
2921 @METHOD: Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::Create
2922 @DESCRIPTION: <p>Create a window - using the Windows SDK routine CreateWindowEx, but hooked to a Led class.
2923 </p>
2924 <p>See also @'Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::SubclassWindow' and
2925 @'Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::ReplaceWindow'.
2926 </p>
2927 */
2928 void Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>::Create (DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName,
2929 DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent,
2930 HMENU hMenu, HINSTANCE hInstance)
2931 {
2932 SDKString tmpClassName;
2933 if (lpClassName == nullptr) {
2934 tmpClassName = Foundation::Characters::CString::Format (_T("Led_Win32_SimpleWndProc_Helper<>-%d-%p"), ::GetCurrentProcessId (), &StaticWndProc);
2935 lpClassName = tmpClassName.c_str ();
2936 {
2937 static bool sRegistered = false;
2938 if (not sRegistered) {
2939 WNDCLASSEX wcex;
2940 memset (&wcex, 0, sizeof (wcex));
2941 wcex.cbSize = sizeof (WNDCLASSEX);
2942 wcex.lpfnWndProc = (WNDPROC)StaticWndProc;
2943 wcex.lpszClassName = lpClassName;
2944 ATOM regResult = ::RegisterClassEx (&wcex);
2945 if (regResult == 0) {
2946 DWORD lastErr = ::GetLastError ();
2947 if (lastErr == ERROR_CLASS_ALREADY_EXISTS) {
2948 // Shouldn't happen - but if it does - SB OK since StaticWndProc addr is the same!
2949 }
2950 else {
2951 Assert (false); // could be a problem?
2952 }
2953 }
2954 sRegistered = true;
2955 }
2956 }
2957 }
2958 [[maybe_unused]] HWND hWnd =
2959 ::CreateWindowEx (dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, this);
2960 Assert (hWnd == this->GetValidatedHWND ()); // already pre-set on the WM_CREATE message...
2961 }
2962 template <typename BASE_WIN32_HELPER>
2963 void Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>::SetHWND (HWND hWnd)
2964 {
2965 if (fHWnd != nullptr) {
2966 ::SetWindowLongPtr (fHWnd, GWLP_USERDATA, 0); // reset back to original value
2967 }
2968 fHWnd = hWnd;
2969 if (fHWnd != nullptr) {
2970 ::SetWindowLongPtr (fHWnd, GWLP_USERDATA, reinterpret_cast<DWORD_PTR> (this));
2971 }
2972 }
2973 template <typename BASE_WIN32_HELPER>
2974 HWND Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>::GetHWND () const
2975 {
2976 return fHWnd;
2977 }
2978 template <typename BASE_INTERACTOR>
2979 void Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::OnNCDestroy_Msg ()
2980 {
2981 (void)DefWindowProc (WM_NCDESTROY, 0, 0);
2982 SetHWND (nullptr);
2983 }
2984 template <typename BASE_INTERACTOR>
2985 /*
2986 @METHOD: Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::SubclassWindow
2987 @DESCRIPTION: <p>Used instead of @'Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::Create' to hook an
2988 existing window.</p>
2989 <p>See also @'Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::ReplaceWindow'.
2990 </p>
2991 */
2992 bool Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::SubclassWindow (HWND hWnd)
2993 {
2994 Require (fSuperWindowProc == nullptr); // don't call twice!
2995 Require (fHWnd == nullptr); // don't call after already created! - use this instead of SetHWnd ()!!!
2996 RequireNotNull (hWnd);
2997 Require (::IsWindow (hWnd));
2998 fSuperWindowProc = reinterpret_cast<WNDPROC> (::SetWindowLong (hWnd, GWLP_WNDPROC, reinterpret_cast<LONG> (StaticWndProc)));
2999 if (fSuperWindowProc == nullptr) {
3000 return false;
3001 }
3002 SetHWND (hWnd);
3003 HookSubclassWindow ();
3004 return true;
3005 }
3006 template <typename BASE_INTERACTOR>
3007 /*
3008 @METHOD: Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::ReplaceWindow
3009 @DESCRIPTION: <p>Used instead of @'Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::SubclassWindow' to replace
3010 an existing window. Instead of using window subclassing, and chaining handlers, just outright REPLACE
3011 the window.
3012 </p>
3013 <p>This seems like a somewhat unconventional thing todo, but it apears much safer, and less quirky. I've found
3014 that SUBCLASSING a window often produces slightly weird results (perhaps depnding on WHICH window you choose
3015 to subclass). Anyhow - this works well if you define a widget in your .rc file, and then use this to replace it
3016 with the roughly equivalently sized etc Led version.
3017 </p>
3018 <p>Returns false on failure, and true on success.</p>
3019 */
3020 bool Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::ReplaceWindow (HWND hWnd)
3021 {
3022 Require (fSuperWindowProc == nullptr); // don't call twice!
3023 Require (fHWnd == nullptr); // don't call after already created! - use this instead of SetHWnd ()!!!
3024 RequireNotNull (hWnd);
3025 Require (::IsWindow (hWnd));
3026
3027 HWND parent = ::GetParent (hWnd);
3028 if (parent == nullptr) {
3029 return false;
3030 }
3031 int id = ::GetWindowLong (hWnd, GWL_ID);
3032
3033 Assert (hWnd == ::GetDlgItem (parent, id));
3034
3035 DWORD dwStyle = ::GetWindowLong (hWnd, GWL_STYLE);
3036 DWORD exStyle = ::GetWindowLong (hWnd, GWL_EXSTYLE);
3037
3038 // Assume previous widget's position.
3039 WINDOWPLACEMENT wp;
3040 memset (&wp, 0, sizeof (wp));
3041 wp.length = sizeof (wp);
3042 Verify (::GetWindowPlacement (hWnd, &wp));
3043
3044 LOGFONT useFont;
3045 bool justUseSystemFont = true;
3046 {
3047 HFONT hFont = nullptr;
3048 if ((hFont = (HFONT)::SendMessage (hWnd, WM_GETFONT, 0, 0L)) != nullptr) {
3049 if (::GetObject (hFont, sizeof (LOGFONT), &useFont)) {
3050 justUseSystemFont = false;
3051 }
3052 }
3053 }
3054
3055 // Delete the old widget window.
3056 ::DestroyWindow (hWnd);
3057 DISABLE_COMPILER_MSC_WARNING_START (4312)
3058 Create (exStyle, nullptr, nullptr, dwStyle | WS_CHILD, wp.rcNormalPosition.left, wp.rcNormalPosition.top,
3059 wp.rcNormalPosition.right - wp.rcNormalPosition.left, wp.rcNormalPosition.bottom - wp.rcNormalPosition.top, parent, (HMENU)id, nullptr);
3060 DISABLE_COMPILER_MSC_WARNING_END (4312)
3061
3062 /*
3063 * Copy the font value from the original widget and set it into the replaced one.
3064 */
3065 {
3066 FontObject fontToUse;
3067 if (not justUseSystemFont) {
3068 Verify (fontToUse.CreateFontIndirect (&useFont));
3069 }
3070 bool redrawFlag = true;
3071 (void)::SendMessage (this->GetValidatedHWND (), WM_SETFONT,
3072 justUseSystemFont ? NULL : reinterpret_cast<WPARAM> (static_cast<HFONT> (fontToUse)), redrawFlag);
3073 }
3074 return true;
3075 }
3076 template <typename BASE_INTERACTOR>
3077 void Led_Win32_SimpleWndProc_Helper<BASE_INTERACTOR>::HookSubclassWindow ()
3078 {
3079 HWND hWnd = this->GetValidatedHWND ();
3080 DWORD dwStyle = ::GetWindowLong (hWnd, GWL_STYLE);
3081 if ((dwStyle & WS_VSCROLL) and this->GetScrollBarType (TextInteractor::v) == TextInteractor::eScrollBarNever) {
3082 this->SetScrollBarType (TextInteractor::v, TextInteractor::eScrollBarAlways);
3083 }
3084 if ((dwStyle & WS_HSCROLL) and this->GetScrollBarType (TextInteractor::h) == TextInteractor::eScrollBarNever) {
3085 this->SetScrollBarType (TextInteractor::h, TextInteractor::eScrollBarAlways);
3086 }
3087 this->OnSize_Msg ();
3088 }
3089 template <typename BASE_WIN32_HELPER>
3090 LRESULT CALLBACK Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>::StaticWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
3091 {
3092 if (message == WM_CREATE) {
3093 ::LPCREATESTRUCT lpcs = (::LPCREATESTRUCT)lParam;
3094 AssertNotNull (lpcs);
3095 Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>* pThis =
3096 reinterpret_cast<Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>*> (lpcs->lpCreateParams);
3097 Assert (pThis->GetHWND () == nullptr); // cuz not set yet...
3098 pThis->SetHWND (hWnd);
3099 }
3100
3101 Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>* pThis =
3102 reinterpret_cast<Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>*> (::GetWindowLongPtr (hWnd, GWLP_USERDATA));
3103
3104 if (pThis == nullptr) {
3105 /*
3106 * The only time I know of where this can happen is that these messages can be passed before the WM_CREATE.
3107 * Seeing this assertion isn't necesarily a bug - but its a clue something wrong maybe going on.
3108 *
3109 * As of 2012-09-16 - I see a few new (sb irrelevant) messages...
3110 */
3111 Assert (message == WM_GETMINMAXINFO or message == WM_NCCREATE or message == WM_NCCALCSIZE or message == 0x0093 or
3112 message == 0x0094 or message == WM_NCMOUSELEAVE);
3113 return ::DefWindowProc (hWnd, message, wParam, lParam);
3114 }
3115
3116 Assert (pThis != nullptr);
3117 Assert (pThis->GetHWND () == hWnd);
3118 return pThis->WndProc (message, wParam, lParam);
3119 }
3120 template <typename BASE_WIN32_HELPER>
3121 LRESULT Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>::WndProc (UINT message, WPARAM wParam, LPARAM lParam)
3122 {
3123#define MY_GET_X_LPARAM(lp) ((int)(short)LOWORD (lp))
3124#define MY_GET_Y_LPARAM(lp) ((int)(short)HIWORD (lp))
3125 switch (message) {
3126 case WM_CREATE:
3127 return this->OnCreate_Msg (reinterpret_cast<::LPCREATESTRUCT> (lParam));
3128 case WM_PAINT:
3129 this->OnPaint_Msg ();
3130 break;
3131 case WM_SIZE:
3132 this->OnSize_Msg ();
3133 break;
3134 case WM_CHAR:
3135 this->OnChar_Msg (static_cast<UINT> (wParam), lParam);
3136 break;
3137 case WM_UNICHAR:
3138 return this->OnUniChar_Msg (wParam, lParam);
3139 break;
3140#if qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug
3141 case WM_IME_CHAR:
3142 return this->OnIMEChar_Msg (wParam, lParam);
3143 case WM_IME_COMPOSITION:
3144 return this->OnIME_COMPOSITION_Msg (wParam, lParam);
3145 case WM_IME_ENDCOMPOSITION:
3146 return this->OnIME_ENDCOMPOSITION_Msg (wParam, lParam);
3147#endif
3148 case WM_KEYDOWN:
3149 this->OnKeyDown_Msg (static_cast<UINT> (wParam), lParam);
3150 break;
3151 case WM_SETCURSOR:
3152 return this->OnSetCursor_Msg ((HWND)wParam, LOWORD (lParam), HIWORD (lParam));
3153 case WM_GETDLGCODE:
3154 return this->OnGetDlgCode_Msg ();
3155 case WM_SETFOCUS:
3156 this->OnSetFocus_Msg (HWND (wParam));
3157 break;
3158 case WM_KILLFOCUS:
3159 this->OnKillFocus_Msg (HWND (wParam));
3160 break;
3161 case WM_ERASEBKGND:
3162 return this->OnEraseBkgnd_Msg (HDC (wParam));
3163 case WM_TIMER:
3164 this->OnTimer_Msg (wParam, reinterpret_cast<::TIMERPROC*> (lParam));
3165 break;
3166 case WM_LBUTTONDOWN:
3167 this->OnLButtonDown_Msg (static_cast<UINT> (wParam), MY_GET_X_LPARAM (lParam), MY_GET_Y_LPARAM (lParam));
3168 break;
3169 case WM_LBUTTONUP:
3170 this->OnLButtonUp_Msg (static_cast<UINT> (wParam), MY_GET_X_LPARAM (lParam), MY_GET_Y_LPARAM (lParam));
3171 break;
3172 case WM_LBUTTONDBLCLK:
3173 this->OnLButtonDblClk_Msg (static_cast<UINT> (wParam), MY_GET_X_LPARAM (lParam), MY_GET_Y_LPARAM (lParam));
3174 break;
3175 case WM_MOUSEMOVE:
3176 this->OnMouseMove_Msg (static_cast<UINT> (wParam), MY_GET_X_LPARAM (lParam), MY_GET_Y_LPARAM (lParam));
3177 break;
3178 case WM_VSCROLL:
3179 this->OnVScroll_Msg (LOWORD (wParam), (short)HIWORD (wParam), HWND (lParam));
3180 break;
3181 case WM_HSCROLL:
3182 this->OnHScroll_Msg (LOWORD (wParam), (short)HIWORD (wParam), HWND (lParam));
3183 break;
3184#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
3185 case WM_MOUSEWHEEL:
3186 return this->OnMouseWheel_Msg (wParam, lParam);
3187#endif
3188 case WM_ENABLE:
3189 this->OnEnable_Msg (!!wParam);
3190 break;
3191 case WM_NCDESTROY:
3192 this->OnNCDestroy_Msg ();
3193 break;
3194 default:
3195 return this->DefWindowProc (message, wParam, lParam);
3196 }
3197#undef MY_GET_X_LPARAM
3198#undef MY_GET_Y_LPARAM
3199 return 0;
3200 }
3201 template <typename BASE_WIN32_HELPER>
3202 LRESULT Led_Win32_SimpleWndProc_Helper<BASE_WIN32_HELPER>::DefWindowProc (UINT message, WPARAM wParam, LPARAM lParam)
3203 {
3204 if (fSuperWindowProc == nullptr) {
3205 return inherited::DefWindowProc (message, wParam, lParam);
3206 }
3207 else {
3208 return (*fSuperWindowProc) (this->GetValidatedHWND (), message, wParam, lParam);
3209 }
3210 }
3211
3212 // class Led_Win32_SimpleWndProc_HelperWithSDKMessages<BASE_CLASS>
3213 template <typename BASE_CLASS>
3214 inline Led_Win32_SimpleWndProc_HelperWithSDKMessages<BASE_CLASS>::Led_Win32_SimpleWndProc_HelperWithSDKMessages ()
3215 : inherited{}
3216 {
3217 }
3218 template <typename BASE_CLASS>
3219 LRESULT Led_Win32_SimpleWndProc_HelperWithSDKMessages<BASE_CLASS>::WndProc (UINT message, WPARAM wParam, LPARAM lParam)
3220 {
3221 LRESULT result = 0;
3222 if (this->HandleMessage (message, wParam, lParam, &result)) {
3223 return result;
3224 }
3225 else {
3226 return inherited::WndProc (message, wParam, lParam);
3227 }
3228 }
3229
3230}
3231
3232CompileTimeFlagChecker_HEADER (Stroika::Frameworks::Led::Platform, qSupportWindowsSDKCallbacks, qSupportWindowsSDKCallbacks);
3233#if qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug
3234CompileTimeFlagChecker_HEADER (Stroika::Frameworks::Led::Platform, qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug, 1);
3235#else
3236CompileTimeFlagChecker_HEADER (Stroika::Frameworks::Led::Platform, qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug, 0);
3237#endif
3238
3239#endif /*_Stroika_Frameworks_Led_Platform_Windows_h_*/
#define AssertNotNull(p)
Definition Assertions.h:333
#define RequireNotNull(p)
Definition Assertions.h:347
#define Verify(c)
Definition Assertions.h:419
#define CompileTimeFlagChecker_HEADER(NS_PREFIX, NAME, VALUE)
CompileTimeFlagChecker_HEADER () will generate a LINK ERROR if you ever compile a header with one val...
#define DbgTrace
Definition Trace.h:309
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
basic_string< SDKChar > SDKString
Definition SDKString.h:38