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