Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
TextImager.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Frameworks_Led_TextImager_h_
5#define _Stroika_Frameworks_Led_TextImager_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9/*
10@MODULE: TextImager
11@DESCRIPTION:
12 <p>The @'TextImager' class is one of central importance in Led. It is the (abstract) class which
13 does all the imaging - drawing of text to an output device.</p>
14 */
15
16#include "Stroika/Frameworks/Led/BiDiLayoutEngine.h"
17#include "Stroika/Frameworks/Led/GDI.h"
18#include "Stroika/Frameworks/Led/Marker.h"
19#include "Stroika/Frameworks/Led/Support.h"
20#include "Stroika/Frameworks/Led/TextStore.h"
21
22namespace Stroika::Frameworks::Led {
23
24 /*
25 @CLASS: TextImager::StandardTabStopList
26 @BASES: @'TabStopList'
27 @DESCRIPTION: <p>A simple tabstop implementation in the caller specifies the exact position of each tabstop. This
28 is most directly ananogous to the Win32SDK GetTabbedTextExtent () API apporach.</p>
29 */
30 class StandardTabStopList : public TabStopList {
31 public:
32 StandardTabStopList (); // default to 1/2 inch
33 StandardTabStopList (TWIPS eachWidth);
34 StandardTabStopList (const vector<TWIPS>& tabstops);
35 StandardTabStopList (const vector<TWIPS>& tabstops, TWIPS afterTabsWidth);
36
37 public:
38 virtual TWIPS ComputeIthTab (size_t i) const override;
39 virtual TWIPS ComputeTabStopAfterPosition (TWIPS afterPos) const override;
40
41 public:
42 TWIPS fDefaultTabWidth; // for tabs PAST the ones specified in the fTabStops list
43 vector<TWIPS> fTabStops;
44
45 public:
46 constexpr bool operator== (const StandardTabStopList& rhs) const;
47 };
48
49#if qStroika_Frameworks_Led_SupportGDI
50 /*
51 @CLASS: TextImager
52 @BASES: virtual @'MarkerOwner'
53 @DESCRIPTION: <p>The @'TextImager' class is one of central importance in Led.
54 TextImagers are responsible for imaging to a @'Tablet*' the
55 contents of a particular @'TextStore'. A TextImager (except in the degenerate case)
56 is always associated with one TextStore (though a single TextStore can have
57 multiple TextImagers displaying its contents). This long-term relationship allows
58 the TextImagers to keep track of cached data like word-wrapping information etc.
59 TextStores have a flexible notification mechanism so that TextImagers get notified
60 when 'interesting' modifications are made to the text (or @'Marker's) in the TextStore
61 a TextImager is pointing to.</p>
62
63 <p>A particular TextImager subclass takes the given text,
64 and figures out how to compute metrics on it, lay it out, cache layout information, and then
65 finally display the text. Different subclasses will apply different sorts of rules (and perhaps
66 keep track of additional information) to render the text differently. For example - a
67 @'WordWrappedTextImager' will apply word-wrapping rules to display the text differently
68 than a @'SimpleTextImager' would.</p>
69
70 <p>A @'TextImager' has no notion of 'windows' - in the sense of a UI. It has no
71 notion of user interaction, screen
72 updates, etc. It has no notion of style runs, paragraph formatting etc.
73 It is a generic API for getting text (in a @'TextStore') imaged onto
74 a some output @'Tablet*'.</p>
75
76 <p>Interesting subclasses of @'TextImager' include @'PartitioningTextImager', @'SimpleTextImager',
77 @'StyledTextImager', @'StandardStyledTextImager', and @'TextInteractor'.</p>
78
79
80 <h5>Window/Scrolling support</h5>
81 <p>We support scrolling through the concept of a window. We chose
82 very purposefully to define the window in terms of the top row
83 of text visible in the window. This was because we wanted to allow
84 for the display of word-wrapped text without having to compute the text
85 height or word-wrapping of the text above and below the window.</p>
86
87 <p>But note - we associate the topleft of the window with a particular
88 ROW - NOT ROW NUMBER! This borrows from (in fact uses) the concept of
89 markers. If people insert rows ABOVE the current window, while this
90 affects the ROW NUMBER of the top row shown in the window - it doesn't
91 affect what is displayed.</p>
92
93 <p>Also, note that we deal with HORIZONTAL and VERTICAL scrolling through
94 very different means. This is because in the case of horizontal scrolling, it is
95 basicly just scrolling over a few - perhaps hundreds of pixels). And scrolling
96 in terms of pixels is clear, unambiguous, and there is really no other obvious choice
97 (characters don't work well cuz text may be proportional, or even multifont).</p>
98
99 <p>And vertical scrolling, pixels - though workable - is a undesirable choice
100 for performance reasons (as alluded to above).</p>
101
102
103 <h5>Future enhancments:</h5>
104 <ul>
105 <li> Instead of a single ImageRect, we could have an array of image rects.
106 All would have real top/left/bottom/right, except the last which would
107 be interpretted as being bottomless (or at least WE define the button).
108 This would allow trivial implementation of multicolumn word processors,
109 and much more sophisticated page / text layout.
110 </li>
111
112 <li>Be sure we deal sensibly with exceptions (on insert/delete mainly - leave
113 markers in a good state - maybe preflight).
114 </li>
115 </ul>
116 */
117 class TextImager : public virtual MarkerOwner {
118 protected:
119 TextImager ();
120
121 public:
122 virtual ~TextImager ();
123
124 /*
125 * Note we can associate any textstore we want with this imager. One one
126 * can be associated at a time. And it is an error to call most TextImager
127 * methods (any that call GetTextStore()) while the textstore is set to
128 * nullptr. Always reset the textstore to nullptr before destroying the textstore
129 * pointed to. And note that switching textstores loses any information
130 * that might have been saved there, like current selection etc.
131 */
132 public:
133 virtual TextStore* PeekAtTextStore () const override; // from MarkerOwner
134 public:
135 nonvirtual void SpecifyTextStore (TextStore* useTextStore);
136
137 protected:
138 virtual void HookLosingTextStore ();
139 nonvirtual void HookLosingTextStore_ ();
140 virtual void HookGainedNewTextStore ();
141 nonvirtual void HookGainedNewTextStore_ ();
142
143 private:
144 TextStore* fTextStore;
145
146 public:
147 virtual void PurgeUnneededMemory ();
148
149 protected:
150 virtual void InvalidateAllCaches ();
151
152 /*
153 * Led's model of font application is slightly different than most
154 * other editors. Led defines a DefaultFont to be applied to all
155 * text. Then, this font can be overriden by the presence of
156 * special markers in the text. This overriding is done
157 * in subclasses of TextImager. So some textImagers will
158 * only support a single monolithic font for the entire text buffer
159 * and others will support refinements of the font specification
160 * on a character by character basis (using markers).
161 *
162 * DefaultFont defaults to GetStaticDefaultFont () (really - no puns intended :-))
163 */
164 public:
165 nonvirtual FontSpecification GetDefaultFont () const;
166 virtual void SetDefaultFont (const IncrementalFontSpecification& defaultFont);
167
168 protected:
169 nonvirtual void SetDefaultFont_ (const IncrementalFontSpecification& defaultFont);
170
171 private:
172 FontSpecification fDefaultFont;
173
174 public:
175 virtual FontSpecification GetDefaultSelectionFont () const;
176
177 public:
178 /*
179 @CLASS: TextImager::NotFullyInitialized
180 @DESCRIPTION: <p>Led requires a complex of objects to all be setup, and hooked together in order to
181 perform several operations correctly. E.G. to invalidate part of the screen, it must indirectly
182 call on code to check the @'TextStore', or the @'Partition'.</p>
183 <p>As part of code re-use, during the setup process, and part of the mechanism of providing default
184 objects for many of these pieces, we call SetThisOrThat() methods which invoke various InvalidateThisOrThat()
185 methods, which in turn may occasionally try to compute something (like the max# of rows in a line). These
186 sorts of errors are innocuous, but must be handled somehow. We could either write the code to be friendly of
187 such uninitiualized variables/values, or to throw like this. My inclination is that throwing will be a more
188 robust solution, since it will prevent an further use of a guessed value (and maybe prevent its being cached).</p>
189 */
190 class NotFullyInitialized {};
191
192 protected:
193 /*
194 @CLASS: TextImager::NoTabletAvailable
195 @BASES: @'TextImager::NotFullyInitialized'
196 @DESCRIPTION: <p>Thrown by @'TextImager::AcquireTablet' when no tablet available. See that method docs
197 for more details.</p>
198 */
199 class NoTabletAvailable : public NotFullyInitialized {};
200
201 protected:
202 /*
203 @METHOD: TextImager::AcquireTablet
204 @DESCRIPTION:
205 <p>By "Tablet" I mean on windows a CDC (@'Tablet*'). Something
206 we can draw into, and calculate text metrics with.</p>
207 <p>Sometimes Led is lucky enough to find itself in a position where it is
208 handed a tabet (drawing). But sometimes it isn't so luck. Conisider if someone
209 asks for a row number, and this causes a complex chain of events resulting
210 in an attempt to word-wrap, which, in turn, requires a tablet. We need some API
211 in the Imager to request a tablet. This API MUST return the appropriate tablet
212 for the view, appropriately prepared for drawing/text metric calculations.</p>
213 <p>Calls to @'TextImager::AcquireTablet' must ALWAYS be balanced with calls to @'TextImager::ReleaseTablet ()'
214 (including in the prescence of exceptions).</p>
215 <p>For windows, @'TextImager::AcquireTablet ()' must return the current PaintDC used in the PaintDC
216 call (if we are in the context of a paint).</p>
217 <p>@'TextImager::AcquireTablet ()' only fail by throwing - typically NoTabletAvailable (Led 2.1 and earlier
218 didn't allow failure).</p>
219 <p>Calls to @'TextImager::AcquireTablet ()'/@'TextImager::ReleaseTablet ()' can be nested.</p>
220 <p>Also, sometimes something happens which causes all text metrics to become</p>
221 invalid (say we are about to image to a print port). In this case, the imager
222 needs to be notified that all its text metrics are invalid. Call @'TextImager::TabletChangedMetrics ()'
223 in this case.</p>
224 */
225 virtual Tablet* AcquireTablet () const = 0;
226 /*
227 @METHOD: TextImager::ReleaseTablet
228 @DESCRIPTION: <p>Tablet API</p>
229 <p>See @'TextImager::AcquireTablet'. Generally don't call this directly - but instead use the
230 @'TextImager::Tablet_Acquirer' class.</p>
231 */
232 virtual void ReleaseTablet (Tablet* tablet) const = 0;
233
234 protected:
235 virtual void TabletChangedMetrics ();
236
237 public:
238 class Tablet_Acquirer;
239 friend class Tablet_Acquirer;
240
241 public:
242 //class TabStopList;
243 /*
244 @CLASS: TabStopList
245 @DESCRIPTION: <p>See @'TabStopList'</p>
246 */
247 class SimpleTabStopList;
248
249 public:
250 virtual const TabStopList& GetTabStopList (size_t containingPos) const;
251
252 /*
253 * Window/Scrolling support.
254 */
255 public:
256 nonvirtual Led_Rect GetWindowRect () const;
257 virtual void SetWindowRect (const Led_Rect& windowRect);
258
259 protected:
260 nonvirtual void SetWindowRect_ (const Led_Rect& windowRect);
261
262 private:
263 Led_Rect fWindowRect;
264
265 // Consider re-doing many of the below ROW APIs using this struct??? - LGP 2002-12-02
266 // Then we'd ahve void GetRowInfo (TransRowSpec tsr, size_t* rowStart, size_t* rowEnd) const = 0 declared here... and use that to get row info?
267 public:
268 struct TransientRowSpecification {
269 size_t fRowStartMarkerPos;
270 };
271
272 /*
273 * Vertical Scrolling:
274 */
275 public:
276 /*
277 @METHOD: TextImager::GetTopRowInWindow
278 @DESCRIPTION: <p>Returns the row number of the top row visible in the scrolled display window.
279 This is a generally ineffient routine to call. It is generally much better
280 to call @'TextImager::GetMarkerPositionOfStartOfWindow'</p>
281 */
282 virtual size_t GetTopRowInWindow () const = 0;
283 /*
284 @METHOD: TextImager::GetTotalRowsInWindow
285 @DESCRIPTION: <p></p>
286 */
287 virtual size_t GetTotalRowsInWindow () const = 0;
288 /*
289 @METHOD: TextImager::GetLastRowInWindow
290 @DESCRIPTION: <p>Returns the row number of the bottom row visible in the scrolled display window.
291 This is a generally ineffient routine to call. It is generally much better
292 to call @'TextImager::GetMarkerPositionOfEndOfWindow'</p>
293 */
294 virtual size_t GetLastRowInWindow () const = 0;
295 /*
296 @METHOD: TextImager::SetTopRowInWindow
297 @DESCRIPTION: <p></p>
298 */
299 virtual void SetTopRowInWindow (size_t newTopRow) = 0;
300 /*
301 @METHOD: TextImager::GetMarkerPositionOfStartOfWindow
302 @DESCRIPTION: <p>This function returns the marker position of the start of the display window. The
303 display window here refers to the area which is currently visible though scrolling.</p>
304 <p>This doc should be clarified!</p>
305 <p>See also @'TextImager::GetMarkerPositionOfStartOfWindow'</p>
306 */
307 virtual size_t GetMarkerPositionOfStartOfWindow () const = 0;
308 /*
309 @METHOD: TextImager::GetMarkerPositionOfEndOfWindow
310 @DESCRIPTION: <p>This function returns the marker position of the end of the display window. The
311 display window here refers to the area which is currently visible though scrolling.</p>
312 <p>This doc should be clarified!</p>
313 <p>See also @'TextImager::GetMarkerPositionOfStartOfWindow'</p>
314 */
315 virtual size_t GetMarkerPositionOfEndOfWindow () const = 0;
316 /*
317 @METHOD: TextImager::GetMarkerPositionOfStartOfLastRowOfWindow
318 @DESCRIPTION: <p></p>
319 */
320 virtual size_t GetMarkerPositionOfStartOfLastRowOfWindow () const = 0;
321
322 // Some helpful routines for the case where we don't have RowReferences available, but we don't want to be too slow...
323 public:
324 /*
325 @METHOD: TextImager::CalculateRowDeltaFromCharDeltaFromTopOfWindow
326 @DESCRIPTION: <p></p>
327 */
328 virtual ptrdiff_t CalculateRowDeltaFromCharDeltaFromTopOfWindow (long deltaChars) const = 0;
329 /*
330 @METHOD: TextImager::CalculateCharDeltaFromRowDeltaFromTopOfWindow
331 @DESCRIPTION: <p></p>
332 */
333 virtual ptrdiff_t CalculateCharDeltaFromRowDeltaFromTopOfWindow (ptrdiff_t deltaRows) const = 0;
334
335 // NB: Though you CAN ask for, or set the top row by number - this is VASTLY less efficient than
336 // doing it by relative position from the current window via ScrollByIfRoom ().
337 // This is because the former method requires computing the text metrics of all the text (and wrapping)
338 // above the row scrolled to (at), while the later - relative method - does not!!!
339 public:
340 /*
341 @METHOD: TextImager::ScrollByIfRoom
342 @DESCRIPTION: <p>If downBy negative then up
343 OK to ask to scroll further than allowed - return true
344 if any scrolling (not necesarily same amont requested) done</p>
345 */
346 virtual void ScrollByIfRoom (ptrdiff_t downByRows) = 0;
347
348 /*
349 @METHOD: TextImager::ScrollSoShowing
350 @DESCRIPTION: <p>If andTryToShowMarkerPos == 0, then just try to show the characters
351 after markerPos. If it is NOT zero, then try to show the characters
352 BETWEEN the two marker positions (note they might not be in ascending
353 order). The (argument) order of the two marker positions determines PREFERENCE.
354 That is - if there is no room to show both, be sure the first is showing.</p>
355 */
356 virtual void ScrollSoShowing (size_t markerPos, size_t andTryToShowMarkerPos = 0) = 0;
357
358 protected:
359 nonvirtual void ScrollSoShowingHHelper (size_t markerPos, size_t andTryToShowMarkerPos);
360
361 /*
362 * This attribute controls if we force the scroll position to be adjusted
363 * in InvalidateScrollBar() so that the entire window is being used. This is TRUE
364 * by default. But might sometimes be handy to disable this behavior.
365 */
366 public:
367 nonvirtual bool GetForceAllRowsShowing () const;
368 nonvirtual void SetForceAllRowsShowing (bool forceAllRowsShowing);
369
370 private:
371 bool fForceAllRowsShowing;
372
373 protected:
374 /*
375 @METHOD: TextImager::AssureWholeWindowUsedIfNeeded
376 @ACCESS: protected
377 @DESCRIPTION: <p></p>
378 */
379 virtual void AssureWholeWindowUsedIfNeeded () = 0;
380
381 public:
382 nonvirtual bool GetImageUsingOffscreenBitmaps () const;
383 nonvirtual void SetImageUsingOffscreenBitmaps (bool imageUsingOffscreenBitmaps);
384
385 private:
386 bool fImageUsingOffscreenBitmaps;
387
388 /*
389 * Horizontal Scrolling:
390 *
391 * The way horizontal scrolling works is that you specify a (horizontal) offset as to where
392 * drawing into the WindowRect() will be relative to. Typically, this will be ZERO, indicating
393 * the text is unscrolled. Typically (by default) the ComputeMaxHScrollPos () method will return
394 * zero. At any time, a UI can call ComputeMaxHScrollPos () in order to inform adjustments to
395 * a scrollbar. It is the TextImagers responsablity to assure that ComputeMaxHScrollPos () returns
396 * a correct, but efficiently computed (ie cached) value, where possible.
397 *
398 *
399 * Design Meanderings: - PRELIMINARY DESIGN THOUGHTS ON HOW TODO THIS - LGP 961231
400 * (what is connection between ComputeMaxHScrollPos () and GetLayoutWidth()??? Logicly there must
401 * be one. Really we must keep resetting Layout Width as people type text making a line longer. Then
402 * ComputeMaxHScrollPos () really just returns GetLayoutWidth() - WindowRect.GetWidth () (or zero, whichever
403 * is larger).
404 *
405 * Then calls to SetHScrollPos & SetLayoutWidth() must clip (auto-reset if necessary) HScrollPos.
406 *
407 */
408 public:
409 nonvirtual CoordinateType GetHScrollPos () const;
410 virtual void SetHScrollPos (CoordinateType hScrollPos);
411
412 protected:
413 nonvirtual void SetHScrollPos_ (CoordinateType hScrollPos);
414
415 public:
416 virtual DistanceType ComputeMaxHScrollPos () const;
417
418 private:
419 CoordinateType fHScrollPos;
420
421 /*
422 * Some utility methods, very handy for implementing horizontal scrolling. Can (and should be) overriden
423 * in certain subclasses for efficiency. But the default implementation will work.
424 */
425 public:
426 virtual DistanceType CalculateLongestRowInWindowPixelWidth () const;
427
428 public:
429 enum CursorMovementDirection {
430 eCursorBack,
431 eCursorForward,
432 eCursorToStart,
433 eCursorToEnd
434 };
435 enum CursorMovementUnit {
436 eCursorByChar,
437 eCursorByWord,
438 eCursorByRow,
439 eCursorByLine,
440 eCursorByWindow,
441 eCursorByBuffer
442 };
443 virtual size_t ComputeRelativePosition (size_t fromPos, CursorMovementDirection direction, CursorMovementUnit movementUnit);
444
445 public:
446 class GoalColumnRecomputerControlContext;
447 nonvirtual void RecomputeSelectionGoalColumn ();
448
449 private:
450 bool fSuppressGoalColumnRecompute;
451
452 public:
453 nonvirtual TWIPS GetSelectionGoalColumn () const;
454 nonvirtual void SetSelectionGoalColumn (TWIPS selectionGoalColumn);
455
456 private:
457 TWIPS fSelectionGoalColumn;
458
459 public:
460 /*
461 @METHOD: TextImager::GetStableTypingRegionContaingMarkerRange
462 @DESCRIPTION: <p>OK, this is a little obscure. But - believe me - it does make sense.
463 When doing updates to the text (say typing) the impact if the typing
464 (in terms of visual display) typically only extends over a narrow region
465 of the screen. If we are using a non-word-wrapped text editor, then this
466 is typically a line. If it is a word-wrapped text editor, then it is
467 typically a paragraph (or perhaps something less than that). This routine
468 allows communication from the imager to higher-level interactor
469 software about what areas of the text will need to be redrawn when
470 changes to localized regions of the text occur.</p>
471 */
472 virtual void GetStableTypingRegionContaingMarkerRange (size_t fromMarkerPos, size_t toMarkerPos, size_t* expandedFromMarkerPos,
473 size_t* expandedToMarkerPos) const = 0;
474
475 public:
476 nonvirtual Led_Rect GetTextWindowBoundingRect (size_t fromMarkerPos, size_t toMarkerPos) const;
477
478 public:
479 nonvirtual Led_Rect GetIntraRowTextWindowBoundingRect (size_t fromMarkerPos, size_t toMarkerPos) const;
480
481 public:
482 virtual vector<Led_Rect> GetRowHilightRects (const TextLayoutBlock& text, size_t rowStart, size_t rowEnd, size_t hilightStart,
483 size_t hilightEnd) const;
484
485 /*
486 * Utility routine to compute based on styleruns etc, all the fontinfo we can get
487 * at a particular point in the text. This can be helpful drawing additional adornments
488 * on the text in subclases.
489 */
490 public:
491 virtual FontMetrics GetFontMetricsAt (size_t charAfterPos) const;
492
493 // TextImagers don't really have a notion of a scrollbar. And yet for many different
494 // subclasses of TextImager, certain things might be detected that would force
495 // a subclass which DID have a scrollbar to update it.
496 // These things include:
497 // � Any edit operation (including font change)
498 // which increases/decreases number of rows
499 // � Any scroll operation
500 // � Resizing the LayoutWidth.
501 //
502 // Call InvalidateScrollBarParameters () whenever any such change happens.
503 protected:
504 virtual void InvalidateScrollBarParameters ();
505
506 public:
507 nonvirtual bool GetUseSelectEOLBOLRowHilightStyle () const;
508 nonvirtual void SetUseSelectEOLBOLRowHilightStyle (bool useEOLBOLRowHilightStyle);
509
510 private:
511 bool fUseEOLBOLRowHilightStyle;
512
513 /*
514 * Control if the caret is currently being displayed for this TE field. Typically it will
515 * be turned on when this widget gets the focus, and off when it loses it. But that is
516 * the responsability of higher level software (subclasses).
517 */
518 public:
519 nonvirtual bool GetSelectionShown () const;
520 virtual void SetSelectionShown (bool shown);
521
522 private:
523 bool fSelectionShown;
524
525 public:
526 nonvirtual size_t GetSelectionStart () const;
527 nonvirtual size_t GetSelectionEnd () const;
528 nonvirtual void GetSelection (size_t* start, size_t* end) const;
529 virtual void SetSelection (size_t start, size_t end);
530
531 protected:
532 nonvirtual void SetSelection_ (size_t start, size_t end);
533
534 protected:
535 class HilightMarker : public Marker {
536 public:
537 HilightMarker () = default;
538 };
539 HilightMarker* fHiliteMarker;
540 bool fWeAllocedHiliteMarker;
541
542 protected:
543 nonvirtual void SetHilightMarker (HilightMarker* newHilightMarker); // NB: Caller must free, and call SetHilightMarker (nullptr)
544
545 /*
546 * API to get postions of text.
547 */
548 public:
549 /*
550 @METHOD: TextImager::GetCharLocation
551 @DESCRIPTION: <p>Returns the (document relative) pixel location (rectangle) of the character after the given
552 marker position.</p>
553 <p>GetCharLocation () returns the rectangle bounding the character after marker-position
554 'afterPosition'. The position MUST be a valid one (0..GetEnd ()), but could
555 refer to the end of the buffer - and so there would be no character width in this case.</p>
556 <p>Note that this is also somewhat ineffecient for larger documents. You can often use
557 @'TextImager::GetCharLocationRowRelative' or @'TextImager::GetCharWindowLocation' instead.</p>
558 */
559 virtual Led_Rect GetCharLocation (size_t afterPosition) const = 0;
560 /*
561 @METHOD: TextImager::GetCharAtLocation
562 @DESCRIPTION: <p>Always returns a valid location, though it might not make a lot of
563 since if it is outside the ImageRect.</p>
564 <p>Note that this is also somewhat ineffecient for larger documents. You can often use
565 @'TextImager::GetRowRelativeCharAtLoc' or @'TextImager::GetCharAtWindowLocation' instead.</p>
566 */
567 virtual size_t GetCharAtLocation (const Led_Point& where) const = 0;
568 /*
569 @METHOD: TextImager::GetCharWindowLocation
570 @DESCRIPTION: <p>Like @'TextImager::GetCharLocation' but returns things relative to the start of the window.</p>
571 <p><em>NB:</em> if the location is outside of the current window, then the size/origin maybe pinned to some
572 arbitrary value, which is pixelwise above (or beleow according to before or after window start/end) the window.</p>
573 */
574 virtual Led_Rect GetCharWindowLocation (size_t afterPosition) const = 0;
575 /*
576 @METHOD: TextImager::GetCharAtWindowLocation
577 @DESCRIPTION: <p>Like @'TextImager::GetCharAtLocation' but assumes 'where' is presented window-relative.</p>
578 <p>See also @'TextInteractor::GetCharAtClickLocation'</p>
579 */
580 virtual size_t GetCharAtWindowLocation (const Led_Point& where) const = 0;
581
582 /*
583 @METHOD: TextImager::GetStartOfRow
584 @DESCRIPTION: <p>GetStartOfRow () returns the position BEFORE the first displayed character in the row.</p>
585 */
586 virtual size_t GetStartOfRow (size_t rowNumber) const = 0;
587 /*
588 @METHOD: TextImager::GetStartOfRowContainingPosition
589 @DESCRIPTION: <p></p>
590 */
591 virtual size_t GetStartOfRowContainingPosition (size_t charPosition) const = 0;
592 /*
593 @METHOD: TextImager::GetEndOfRow
594 @DESCRIPTION: <p>GetEndOfRow () returns the position AFTER the last displayed character in the row.
595 Note that this can differ from the end of row returned by @'TextImager::GetRealEndOfRow' because of characters removed by
596 @TextImager::RemoveMappedDisplayCharacters'.</p>
597 <p>See also @'TextImager::GetRealEndOfRow'.</p>
598 */
599 virtual size_t GetEndOfRow (size_t rowNumber) const = 0;
600 /*
601 @METHOD: TextImager::GetEndOfRowContainingPosition
602 @DESCRIPTION: <p></p>
603 */
604 virtual size_t GetEndOfRowContainingPosition (size_t charPosition) const = 0;
605 /*
606 @METHOD: TextImager::GetRealEndOfRow
607 @DESCRIPTION: <p>GetRealEndOfRow () returns the position AFTER the last character in the row. See
608 @'TextImager::GetEndOfRow'. Note - for the last row of the document, this could be include the
609 'bogus character'. So - this will always return a value &lt;= @'TextStore::GetLength' () + 1</p>
610 */
611 virtual size_t GetRealEndOfRow (size_t rowNumber) const = 0;
612 /*
613 @METHOD: TextImager::GetRealEndOfRowContainingPosition
614 @DESCRIPTION: <p></p>
615 */
616 virtual size_t GetRealEndOfRowContainingPosition (size_t charPosition) const = 0;
617 /*
618 @METHOD: TextImager::GetRowContainingPosition
619 @DESCRIPTION: <p></p>
620 */
621 virtual size_t GetRowContainingPosition (size_t charPosition) const = 0;
622 /*
623 @METHOD: TextImager::GetRowCount
624 @DESCRIPTION: <p></p>
625 */
626 virtual size_t GetRowCount () const = 0;
627
628 nonvirtual size_t GetRowLength (size_t rowNumber) const;
629
630 public:
631 /*
632 @METHOD: TextImager::GetCharLocationRowRelativeByPosition
633 @DESCRIPTION: <p></p>
634 */
635 virtual Led_Rect GetCharLocationRowRelativeByPosition (size_t afterPosition, size_t positionOfTopRow, size_t maxRowsToCheck) const = 0;
636
637 // Simple wrappers on GetStartOfRowContainingPosition () etc to allow row navigation without the COSTLY
638 // computation of row#s.
639 public:
640 nonvirtual size_t GetStartOfNextRowFromRowContainingPosition (size_t charPosition) const;
641 nonvirtual size_t GetStartOfPrevRowFromRowContainingPosition (size_t charPosition) const;
642
643 public:
644 /*
645 @METHOD: TextImager::GetRowHeight
646 @DESCRIPTION: <p>This API is redundent, but can be much more efficient to get at this information
647 than GetCharLocation() - especially in subclasses like MultiRowImager using RowRefernces.</p>
648 */
649 virtual DistanceType GetRowHeight (size_t rowNumber) const = 0;
650
651 public:
652 /*
653 @METHOD: TextImager::GetRowRelativeBaselineOfRowContainingPosition
654 @DESCRIPTION: <p>Returns the number of pixels from the top of the given row, to the baseline.
655 The 'charPosition' is a markerPosition just before any character in the row.</p>
656 */
657 virtual DistanceType GetRowRelativeBaselineOfRowContainingPosition (size_t charPosition) const = 0;
658
659 public:
660 /*
661 @METHOD: TextImager::GetTextDirection
662 @DESCRIPTION: <p>Returns the text direction (left to right or right to left) of the given character
663 (the one just after the given marker position).</p>
664 */
665 virtual TextDirection GetTextDirection (size_t charPosition) const = 0;
666
667 public:
668 virtual TextLayoutBlock_Copy GetTextLayoutBlock (size_t rowStart, size_t rowEnd) const;
669
670 /*
671 * Figure the region bounding the given segment of text. Useful for displaying
672 * some sort of text hilight, in addition (or apart from) the standard hilighting
673 * of text. Note we use a VAR parameter for the region rather than returing it
674 * cuz MFC's CRgn class doesn't support being copied.
675 */
676 public:
677 virtual vector<Led_Rect> GetSelectionWindowRects (size_t from, size_t to) const;
678 virtual void GetSelectionWindowRegion (Region* r, size_t from, size_t to) const;
679
680 public:
681 /*
682 @METHOD: TextImager::Draw
683 @DESCRIPTION: <p>This is the API called by the underlying window system (or wrappers) to get the imager
684 to draw its currently displayed window. The argument 'subsetToDraw' defines the subset (in the same coordinate system
685 the windowRect (@'TextImager::GetWindowRect') was specified in (so it will generally be a subset of the windowrect).</p>
686 */
687 virtual void Draw (const Led_Rect& subsetToDraw, bool printing) = 0;
688
689 // Misc default foreground/background color attributes
690 public:
691 /*
692 @METHOD: TextImager::DefaultColorIndex
693 @DESCRIPTION: <p>Enumerator of different color-table default names used by these routines:
694 <ul>
695 <li>@'TextImager::GetEffectiveDefaultTextColor'</li>
696 <li>@'TextImager::GetEffectiveDefaultTextColor'</li>
697 <li>@'TextImager::ClearDefaultTextColor'</li>
698 <li>@'TextImager::SetDefaultTextColor'</li>
699 </ul>
700 </p>
701 <p>The values are:
702 <table>
703 <tr>
704 <td>Name</td> <td>Default</td> <td>Description</td>
705 </tr>
706 <tr>
707 <td>eDefaultTextColor</td> <td>Led_GetTextColor&nbsp;()</td> <td>Color used for plain text. This color choice is frequently overriden elsewhere.</td>
708 </tr>
709 <tr>
710 <td>eDefaultBackgroundColor</td> <td>Led_GetTextBackgroundColor&nbsp;()</td> <td>The background color which the text is drawn against. This is mainly used in @'TextImager::EraseBackground'. NB: since TextImager::EraseBackground is frequently overriden - setting this value could have no effect.</td>
711 </tr>
712 <tr>
713 <td>eDefaultSelectedTextColor</td> <td>Led_GetSelectedTextColor&nbsp;()</td> <td>The color used to display selected text. This would typically by used by @'TextImager::HilightARectangle' - but depending on the style of hilight it uses - this color maybe ignonred.</td>
714 </tr>
715 <tr>
716 <td>eDefaultSelectedTextBackgroundColor</td><td>Led_GetSelectedTextBackgroundColor&nbsp;()</td> <td>The background color for hilighted (selected) text. Depending on the style of hilight it uses - this color maybe ignonred.</td>
717 </tr>
718 </table>
719 </p>
720 */
721 enum DefaultColorIndex {
722 eDefaultTextColor,
723 eDefaultBackgroundColor,
724 eDefaultSelectedTextColor,
725 eDefaultSelectedTextBackgroundColor,
726 eMaxDefaultColorIndex
727 };
728 nonvirtual Color* GetDefaultTextColor (DefaultColorIndex dci) const;
729 nonvirtual Color GetEffectiveDefaultTextColor (DefaultColorIndex dci) const;
730 nonvirtual void ClearDefaultTextColor (DefaultColorIndex dci);
731 nonvirtual void SetDefaultTextColor (DefaultColorIndex dci, const Color& textColor);
732
733 private:
734 Color* fDefaultColorIndex[eMaxDefaultColorIndex];
735
736 public:
737 virtual void EraseBackground (Tablet* tablet, const Led_Rect& subsetToDraw, bool printing);
738
739 public:
740 virtual void HilightArea (Tablet* tablet, Led_Rect hiliteArea);
741 virtual void HilightArea (Tablet* tablet, const Region& hiliteArea);
742
743 protected:
744 virtual void DrawRow (Tablet* tablet, const Led_Rect& currentRowRect, const Led_Rect& invalidRowRect, const TextLayoutBlock& text,
745 size_t rowStart, size_t rowEnd, bool printing);
746 virtual void DrawRowSegments (Tablet* tablet, const Led_Rect& currentRowRect, const Led_Rect& invalidRowRect,
747 const TextLayoutBlock& text, size_t rowStart, size_t rowEnd);
748 virtual void DrawRowHilight (Tablet* tablet, const Led_Rect& currentRowRect, const Led_Rect& invalidRowRect,
749 const TextLayoutBlock& text, size_t rowStart, size_t rowEnd);
750 virtual void DrawInterLineSpace (DistanceType interlineSpace, Tablet* tablet, CoordinateType vPosOfTopOfInterlineSpace,
751 bool segmentHilighted, bool printing);
752
753 protected:
754 virtual bool ContainsMappedDisplayCharacters (const Led_tChar* text, size_t nTChars) const;
755 virtual void ReplaceMappedDisplayCharacters (const Led_tChar* srcText, Led_tChar* copyText, size_t nTChars) const;
756 virtual size_t RemoveMappedDisplayCharacters (Led_tChar* copyText, size_t nTChars) const;
757 virtual void PatchWidthRemoveMappedDisplayCharacters (const Led_tChar* srcText, DistanceType* distanceResults, size_t nTChars) const;
758
759 protected:
760 static bool ContainsMappedDisplayCharacters_HelperForChar (const Led_tChar* text, size_t nTChars, Led_tChar charToMap);
761 static void ReplaceMappedDisplayCharacters_HelperForChar (Led_tChar* copyText, size_t nTChars, Led_tChar charToMap, Led_tChar charToMapTo);
762 static size_t RemoveMappedDisplayCharacters_HelperForChar (Led_tChar* copyText, size_t nTChars, Led_tChar charToRemove);
763 static void PatchWidthRemoveMappedDisplayCharacters_HelperForChar (const Led_tChar* srcText, DistanceType* distanceResults,
764 size_t nTChars, Led_tChar charToRemove);
765
766 protected:
767 virtual void DrawSegment (Tablet* tablet, size_t from, size_t to, const TextLayoutBlock& text, const Led_Rect& drawInto,
768 const Led_Rect& invalidRect, CoordinateType useBaseLine, DistanceType* pixelsDrawn);
769
770 public:
771 // Note we REQUIRE that useBaseLine be contained within drawInto
772 nonvirtual void DrawSegment_ (Tablet* tablet, const FontSpecification& fontSpec, size_t from, size_t to, const TextLayoutBlock& text,
773 const Led_Rect& drawInto, CoordinateType useBaseLine, DistanceType* pixelsDrawn) const;
774
775 protected:
776 // distanceResults must be an array of (to-from) elements - which is filled in with the widths
777 // of subsegments from 'from' up to 'i', where 'i' is the ith-from element of 'distanceResults'.
778 // For mbyte chars - splitting char results and looking at those values are UNDEFINED).
779 //
780 // In general - textwidth of text from from to b (where b MUST be contained in from/to)
781 // is distanceResults[b-from-1] - and of course ZERO if b == from
782 //
783 virtual void MeasureSegmentWidth (size_t from, size_t to, const Led_tChar* text, DistanceType* distanceResults) const;
784
785 public:
786 nonvirtual void MeasureSegmentWidth_ (const FontSpecification& fontSpec, size_t from, size_t to, const Led_tChar* text,
787 DistanceType* distanceResults) const;
788
789 protected:
790 virtual DistanceType MeasureSegmentHeight (size_t from, size_t to) const;
791
792 public:
793 nonvirtual DistanceType MeasureSegmentHeight_ (const FontSpecification& fontSpec, size_t from, size_t to) const;
794
795 protected:
796 virtual DistanceType MeasureSegmentBaseLine (size_t from, size_t to) const;
797
798 public:
799 nonvirtual DistanceType MeasureSegmentBaseLine_ (const FontSpecification& fontSpec, size_t from, size_t to) const;
800
801 public:
802 /*
803 @METHOD: TextImager::CalcSegmentSize
804 @DESCRIPTION: <p>Measures the distance in pixels from 'from' to 'to'.
805 This function requires that 'from' and 'to' are on the same row.</p>
806 <p>Note that for bidirectional text, the notion of 'from' and 'to' CAN be ambiguous. A given
807 marker-position between two characters in the buffer could refer to characters that are VISUALLY
808 not next to one another. Luckily - there is a convenient disambiguator in this API. Since we are always
809 measuring 'from' one position 'to' another - we assume the region in question is the one <em>after</em>
810 the 'from' and <em>before</em> the 'to'.</p>
811 <p>Note - because of this definition - and because of bidirectional characters - trying to find
812 the left and right side of a character with:
813 <CODE><PRE>
814 LHS = CalcSegmentSize (ROWSTART, i)
815 RHS = CalcSegmentSize (ROWSTART, i+1);
816 </PRE></CODE>
817 will NOT work. Use @'TextImager::GetRowRelativeCharLoc' to properly take into account the script
818 directionality runs.
819 </P>
820 */
821 virtual DistanceType CalcSegmentSize (size_t from, size_t to) const = 0;
822
823 public:
824 /*
825 @METHOD: TextImager::GetRowRelativeCharLoc
826 @DESCRIPTION: <p>Returns the row-relative bounds of the given character (the one from charLoc to charLoc + 1).
827 The left hand side is window-relative, so if you want window coordinates, add the window left.
828 This routine is more complex than just calling @'TextImager::CalcSegmentSize' () on the right and
829 left sides of the character, because of bidirectional editing.</p>
830 <p>Note that even though the character maybe RTL or LTR, we still return the *lhs <= *rhs for the
831 character cell. This routine calls @'TextImager::RemoveMappedDisplayCharacters' and if the given character is
832 removed, its proper LHS will still be returned, but the RHS will be EQUAL to its LHS.</p>
833 <p>Note that 'charLoc' must be a legal char location or just at the end of the buffer - 0 <= charLoc <= GetEnd ().
834 if charLoc==GetEnd() - you will get the same results as in the @'TextImager::RemoveMappedDisplayCharacters' case.</p>
835 <p>See also @'TextImager::GetRowRelativeCharLoc'</p>
836 */
837 virtual void GetRowRelativeCharLoc (size_t charLoc, DistanceType* lhs, DistanceType* rhs) const = 0;
838
839 public:
840 /*
841 @METHOD: TextImager::GetRowRelativeCharAtLoc
842 @DESCRIPTION: <p>Look in the row which begins at 'rowStart'. Look 'hOffset' pixels into the row, and return the character which
843 would be found at that point (assuming the vertical positioning is already correct). If its too far to the right, return ENDOFROW.</p>
844 <p>See also @'TextImager::GetRowRelativeCharLoc'</p>
845 <p>Returns the last character in the row (not a marker PAST that
846 character) if the point passed in is past the end of the last character in the row.
847 </p>
848 */
849 virtual size_t GetRowRelativeCharAtLoc (CoordinateType hOffset, size_t rowStart) const = 0;
850
851 // Font info caches...
852 private:
853 mutable FontSpecification fCachedFontSpec;
854 mutable FontMetrics fCachedFontInfo;
855#if qStroika_Platform_Windows
856 mutable FontObject* fCachedFont;
857#else
858 mutable bool fCachedFontValid;
859#endif
860
861 protected:
862 class FontCacheInfoUpdater {
863 public:
864 FontCacheInfoUpdater (const TextImager* imager, Tablet* tablet, const FontSpecification& fontSpec);
865 ~FontCacheInfoUpdater ();
866
867 public:
868 nonvirtual FontMetrics GetMetrics () const;
869
870 private:
871 const TextImager* fImager;
872
873#if qStroika_Platform_Windows
874 private:
875 Tablet* fTablet;
876 HGDIOBJ fRestoreObject;
877 HGDIOBJ fRestoreAttribObject;
878#endif
879 };
880 friend class FontCacheInfoUpdater;
881
882 private:
883 friend class GoalColumnRecomputerControlContext;
884 };
885
886 /*
887 @CLASS: TextImager::Tablet_Acquirer
888 @DESCRIPTION: <p>Stack-based resource allocation/deallocation. See @'TextImager::AcquireTablet'.</p>
889 */
890 class TextImager::Tablet_Acquirer {
891 public:
892 Tablet_Acquirer (const TextImager* textImager)
893 : fTextImager (textImager)
894 {
895 AssertNotNull (fTextImager);
896 fTablet = fTextImager->AcquireTablet ();
897 }
898 operator Tablet*()
899 {
900 AssertNotNull (fTablet);
901 return (fTablet);
902 }
903 Tablet* operator->()
904 {
905 return fTablet;
906 }
907 ~Tablet_Acquirer ()
908 {
909 AssertNotNull (fTextImager);
910 AssertNotNull (fTablet);
911 fTextImager->ReleaseTablet (fTablet);
912 }
913
914 private:
915 const TextImager* fTextImager;
916 Tablet* fTablet;
917 };
918
919 /*
920 @CLASS: TextImager::SimpleTabStopList
921 @BASES: @'TabStopList'
922 @DESCRIPTION: <p>A simple tabstop implementation in which all tabstops are equidistant from each other, and start
923 at position zero. This is commonly used as the default tabstop object.</p>
924 */
925 class TextImager::SimpleTabStopList : public TabStopList {
926 public:
927 SimpleTabStopList (TWIPS twipsPerTabStop);
928
929 public:
930 virtual TWIPS ComputeIthTab (size_t i) const override;
931 virtual TWIPS ComputeTabStopAfterPosition (TWIPS afterPos) const override;
932
933 public:
934 TWIPS fTWIPSPerTabStop;
935 };
936
937 /*
938 @CLASS: TextImager::GoalColumnRecomputerControlContext
939 @DESCRIPTION: <p></p>
940 */
941 class TextImager::GoalColumnRecomputerControlContext {
942 public:
943 GoalColumnRecomputerControlContext (TextImager& imager, bool suppressRecompute);
944 ~GoalColumnRecomputerControlContext ();
945
946 private:
947 TextImager& fTextImager;
948 bool fSavedSuppressRecompute;
949 };
950
951 /*
952 @CLASS: TrivialImager<TEXTSTORE,IMAGER>
953 @DESCRIPTION: <p>Handy little template, if you want to use the power of Led, but just to wrap a particular imager,
954 in a localized, one-time fasion, say todo printing, or some such. Not for interactors.</p>
955 <p>Depending on the IMAGER you wish to use, you may want to try @'TrivialImager_Interactor<TEXTSTORE,IMAGER>'
956 or @'TrivialWordWrappedImager<TEXTSTORE, IMAGER>' - both of which come with examples of their use.</p>
957 */
958 template <typename TEXTSTORE, typename IMAGER>
959 class TrivialImager : public IMAGER {
960 private:
961 using inherited = IMAGER;
962
963 protected:
964 TrivialImager (Tablet* t);
965
966 public:
967 TrivialImager (Tablet* t, Led_Rect bounds, const Led_tString& initialText = LED_TCHAR_OF (""));
968 ~TrivialImager ();
969
970 public:
971 nonvirtual void Draw (bool printing = false);
972 virtual void Draw (const Led_Rect& subsetToDraw, bool printing) override;
973
974 protected:
975 virtual Tablet* AcquireTablet () const override;
976 virtual void ReleaseTablet (Tablet* /*tablet*/) const override;
977
978 protected:
979 virtual void EraseBackground (Tablet* tablet, const Led_Rect& subsetToDraw, bool printing) override;
980
981 protected:
982 nonvirtual void SnagAttributesFromTablet ();
983
984 public:
985 nonvirtual Color GetBackgroundColor () const;
986 nonvirtual void SetBackgroundColor (Color c);
987 nonvirtual bool GetBackgroundTransparent () const;
988 nonvirtual void SetBackgroundTransparent (bool transparent);
989
990 private:
991 TEXTSTORE fTextStore;
992 Tablet* fTablet;
993 bool fBackgroundTransparent;
994 };
995#endif
996
997}
998
999/*
1000 ********************************************************************************
1001 ***************************** Implementation Details ***************************
1002 ********************************************************************************
1003 */
1004#include "TextImager.inl"
1005
1006#endif /*_Stroika_Frameworks_Led_TextImager_h_*/
#define AssertNotNull(p)
Definition Assertions.h:336