Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
TextImager.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. 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 the mac a grafport, or 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>For Mac, we must not only return the grafPort, but be sure it is prepared.</p>
218 <p>@'TextImager::AcquireTablet ()' only fail by throwing - typically NoTabletAvailable (Led 2.1 and earlier
219 didn't allow failure).</p>
220 <p>Calls to @'TextImager::AcquireTablet ()'/@'TextImager::ReleaseTablet ()' can be nested.</p>
221 <p>Also, sometimes something happens which causes all text metrics to become</p>
222 invalid (say we are about to image to a print port). In this case, the imager
223 needs to be notified that all its text metrics are invalid. Call @'TextImager::TabletChangedMetrics ()'
224 in this case.</p>
225 */
226 virtual Tablet* AcquireTablet () const = 0;
227 /*
228 @METHOD: TextImager::ReleaseTablet
229 @DESCRIPTION: <p>Tablet API</p>
230 <p>See @'TextImager::AcquireTablet'. Generally don't call this directly - but instead use the
231 @'TextImager::Tablet_Acquirer' class.</p>
232 */
233 virtual void ReleaseTablet (Tablet* tablet) const = 0;
234
235 protected:
236 virtual void TabletChangedMetrics ();
237
238 public:
239 class Tablet_Acquirer;
240 friend class Tablet_Acquirer;
241
242 public:
243 //class TabStopList;
244 /*
245 @CLASS: TabStopList
246 @DESCRIPTION: <p>See @'TabStopList'</p>
247 */
248 class SimpleTabStopList;
249
250 public:
251 virtual const TabStopList& GetTabStopList (size_t containingPos) const;
252
253 /*
254 * Window/Scrolling support.
255 */
256 public:
257 nonvirtual Led_Rect GetWindowRect () const;
258 virtual void SetWindowRect (const Led_Rect& windowRect);
259
260 protected:
261 nonvirtual void SetWindowRect_ (const Led_Rect& windowRect);
262
263 private:
264 Led_Rect fWindowRect;
265
266 // Consider re-doing many of the below ROW APIs using this struct??? - LGP 2002-12-02
267 // 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?
268 public:
269 struct TransientRowSpecification {
270 size_t fRowStartMarkerPos;
271 };
272
273 /*
274 * Vertical Scrolling:
275 */
276 public:
277 /*
278 @METHOD: TextImager::GetTopRowInWindow
279 @DESCRIPTION: <p>Returns the row number of the top row visible in the scrolled display window.
280 This is a generally ineffient routine to call. It is generally much better
281 to call @'TextImager::GetMarkerPositionOfStartOfWindow'</p>
282 */
283 virtual size_t GetTopRowInWindow () const = 0;
284 /*
285 @METHOD: TextImager::GetTotalRowsInWindow
286 @DESCRIPTION: <p></p>
287 */
288 virtual size_t GetTotalRowsInWindow () const = 0;
289 /*
290 @METHOD: TextImager::GetLastRowInWindow
291 @DESCRIPTION: <p>Returns the row number of the bottom row visible in the scrolled display window.
292 This is a generally ineffient routine to call. It is generally much better
293 to call @'TextImager::GetMarkerPositionOfEndOfWindow'</p>
294 */
295 virtual size_t GetLastRowInWindow () const = 0;
296 /*
297 @METHOD: TextImager::SetTopRowInWindow
298 @DESCRIPTION: <p></p>
299 */
300 virtual void SetTopRowInWindow (size_t newTopRow) = 0;
301 /*
302 @METHOD: TextImager::GetMarkerPositionOfStartOfWindow
303 @DESCRIPTION: <p>This function returns the marker position of the start of the display window. The
304 display window here refers to the area which is currently visible though scrolling.</p>
305 <p>This doc should be clarified!</p>
306 <p>See also @'TextImager::GetMarkerPositionOfStartOfWindow'</p>
307 */
308 virtual size_t GetMarkerPositionOfStartOfWindow () const = 0;
309 /*
310 @METHOD: TextImager::GetMarkerPositionOfEndOfWindow
311 @DESCRIPTION: <p>This function returns the marker position of the end of the display window. The
312 display window here refers to the area which is currently visible though scrolling.</p>
313 <p>This doc should be clarified!</p>
314 <p>See also @'TextImager::GetMarkerPositionOfStartOfWindow'</p>
315 */
316 virtual size_t GetMarkerPositionOfEndOfWindow () const = 0;
317 /*
318 @METHOD: TextImager::GetMarkerPositionOfStartOfLastRowOfWindow
319 @DESCRIPTION: <p></p>
320 */
321 virtual size_t GetMarkerPositionOfStartOfLastRowOfWindow () const = 0;
322
323 // Some helpful routines for the case where we don't have RowReferences available, but we don't want to be too slow...
324 public:
325 /*
326 @METHOD: TextImager::CalculateRowDeltaFromCharDeltaFromTopOfWindow
327 @DESCRIPTION: <p></p>
328 */
329 virtual ptrdiff_t CalculateRowDeltaFromCharDeltaFromTopOfWindow (long deltaChars) const = 0;
330 /*
331 @METHOD: TextImager::CalculateCharDeltaFromRowDeltaFromTopOfWindow
332 @DESCRIPTION: <p></p>
333 */
334 virtual ptrdiff_t CalculateCharDeltaFromRowDeltaFromTopOfWindow (ptrdiff_t deltaRows) const = 0;
335
336 // NB: Though you CAN ask for, or set the top row by number - this is VASTLY less efficient than
337 // doing it by relative position from the current window via ScrollByIfRoom ().
338 // This is because the former method requires computing the text metrics of all the text (and wrapping)
339 // above the row scrolled to (at), while the later - relative method - does not!!!
340 public:
341 /*
342 @METHOD: TextImager::ScrollByIfRoom
343 @DESCRIPTION: <p>If downBy negative then up
344 OK to ask to scroll further than allowed - return true
345 if any scrolling (not necesarily same amont requested) done</p>
346 */
347 virtual void ScrollByIfRoom (ptrdiff_t downByRows) = 0;
348
349 /*
350 @METHOD: TextImager::ScrollSoShowing
351 @DESCRIPTION: <p>If andTryToShowMarkerPos == 0, then just try to show the characters
352 after markerPos. If it is NOT zero, then try to show the characters
353 BETWEEN the two marker positions (note they might not be in ascending
354 order). The (argument) order of the two marker positions determines PREFERENCE.
355 That is - if there is no room to show both, be sure the first is showing.</p>
356 */
357 virtual void ScrollSoShowing (size_t markerPos, size_t andTryToShowMarkerPos = 0) = 0;
358
359 protected:
360 nonvirtual void ScrollSoShowingHHelper (size_t markerPos, size_t andTryToShowMarkerPos);
361
362 /*
363 * This attribute controls if we force the scroll position to be adjusted
364 * in InvalidateScrollBar() so that the entire window is being used. This is TRUE
365 * by default. But might sometimes be handy to disable this behavior.
366 */
367 public:
368 nonvirtual bool GetForceAllRowsShowing () const;
369 nonvirtual void SetForceAllRowsShowing (bool forceAllRowsShowing);
370
371 private:
372 bool fForceAllRowsShowing;
373
374 protected:
375 /*
376 @METHOD: TextImager::AssureWholeWindowUsedIfNeeded
377 @ACCESS: protected
378 @DESCRIPTION: <p></p>
379 */
380 virtual void AssureWholeWindowUsedIfNeeded () = 0;
381
382 public:
383 nonvirtual bool GetImageUsingOffscreenBitmaps () const;
384 nonvirtual void SetImageUsingOffscreenBitmaps (bool imageUsingOffscreenBitmaps);
385
386 private:
387 bool fImageUsingOffscreenBitmaps;
388
389 /*
390 * Horizontal Scrolling:
391 *
392 * The way horizontal scrolling works is that you specify a (horizontal) offset as to where
393 * drawing into the WindowRect() will be relative to. Typically, this will be ZERO, indicating
394 * the text is unscrolled. Typically (by default) the ComputeMaxHScrollPos () method will return
395 * zero. At any time, a UI can call ComputeMaxHScrollPos () in order to inform adjustments to
396 * a scrollbar. It is the TextImagers responsablity to assure that ComputeMaxHScrollPos () returns
397 * a correct, but efficiently computed (ie cached) value, where possible.
398 *
399 *
400 * Design Meanderings: - PRELIMINARY DESIGN THOUGHTS ON HOW TODO THIS - LGP 961231
401 * (what is connection between ComputeMaxHScrollPos () and GetLayoutWidth()??? Logicly there must
402 * be one. Really we must keep resetting Layout Width as people type text making a line longer. Then
403 * ComputeMaxHScrollPos () really just returns GetLayoutWidth() - WindowRect.GetWidth () (or zero, whichever
404 * is larger).
405 *
406 * Then calls to SetHScrollPos & SetLayoutWidth() must clip (auto-reset if necessary) HScrollPos.
407 *
408 */
409 public:
410 nonvirtual CoordinateType GetHScrollPos () const;
411 virtual void SetHScrollPos (CoordinateType hScrollPos);
412
413 protected:
414 nonvirtual void SetHScrollPos_ (CoordinateType hScrollPos);
415
416 public:
417 virtual DistanceType ComputeMaxHScrollPos () const;
418
419 private:
420 CoordinateType fHScrollPos;
421
422 /*
423 * Some utility methods, very handy for implementing horizontal scrolling. Can (and should be) overriden
424 * in certain subclasses for efficiency. But the default implementation will work.
425 */
426 public:
427 virtual DistanceType CalculateLongestRowInWindowPixelWidth () const;
428
429 public:
430 enum CursorMovementDirection {
431 eCursorBack,
432 eCursorForward,
433 eCursorToStart,
434 eCursorToEnd
435 };
436 enum CursorMovementUnit {
437 eCursorByChar,
438 eCursorByWord,
439 eCursorByRow,
440 eCursorByLine,
441 eCursorByWindow,
442 eCursorByBuffer
443 };
444 virtual size_t ComputeRelativePosition (size_t fromPos, CursorMovementDirection direction, CursorMovementUnit movementUnit);
445
446 public:
447 class GoalColumnRecomputerControlContext;
448 nonvirtual void RecomputeSelectionGoalColumn ();
449
450 private:
451 bool fSuppressGoalColumnRecompute;
452
453 public:
454 nonvirtual TWIPS GetSelectionGoalColumn () const;
455 nonvirtual void SetSelectionGoalColumn (TWIPS selectionGoalColumn);
456
457 private:
458 TWIPS fSelectionGoalColumn;
459
460 public:
461 /*
462 @METHOD: TextImager::GetStableTypingRegionContaingMarkerRange
463 @DESCRIPTION: <p>OK, this is a little obscure. But - believe me - it does make sense.
464 When doing updates to the text (say typing) the impact if the typing
465 (in terms of visual display) typically only extends over a narrow region
466 of the screen. If we are using a non-word-wrapped text editor, then this
467 is typically a line. If it is a word-wrapped text editor, then it is
468 typically a paragraph (or perhaps something less than that). This routine
469 allows communication from the imager to higher-level interactor
470 software about what areas of the text will need to be redrawn when
471 changes to localized regions of the text occur.</p>
472 */
473 virtual void GetStableTypingRegionContaingMarkerRange (size_t fromMarkerPos, size_t toMarkerPos, size_t* expandedFromMarkerPos,
474 size_t* expandedToMarkerPos) const = 0;
475
476 public:
477 nonvirtual Led_Rect GetTextWindowBoundingRect (size_t fromMarkerPos, size_t toMarkerPos) const;
478
479 public:
480 nonvirtual Led_Rect GetIntraRowTextWindowBoundingRect (size_t fromMarkerPos, size_t toMarkerPos) const;
481
482 public:
483 virtual vector<Led_Rect> GetRowHilightRects (const TextLayoutBlock& text, size_t rowStart, size_t rowEnd, size_t hilightStart,
484 size_t hilightEnd) const;
485
486 /*
487 * Utility routine to compute based on styleruns etc, all the fontinfo we can get
488 * at a particular point in the text. This can be helpful drawing additional adornments
489 * on the text in subclases.
490 */
491 public:
492 virtual FontMetrics GetFontMetricsAt (size_t charAfterPos) const;
493
494 // TextImagers don't really have a notion of a scrollbar. And yet for many different
495 // subclasses of TextImager, certain things might be detected that would force
496 // a subclass which DID have a scrollbar to update it.
497 // These things include:
498 // � Any edit operation (including font change)
499 // which increases/decreases number of rows
500 // � Any scroll operation
501 // � Resizing the LayoutWidth.
502 //
503 // Call InvalidateScrollBarParameters () whenever any such change happens.
504 protected:
505 virtual void InvalidateScrollBarParameters ();
506
507 public:
508 nonvirtual bool GetUseSelectEOLBOLRowHilightStyle () const;
509 nonvirtual void SetUseSelectEOLBOLRowHilightStyle (bool useEOLBOLRowHilightStyle);
510
511 private:
512 bool fUseEOLBOLRowHilightStyle;
513
514 /*
515 * Control if the caret is currently being displayed for this TE field. Typically it will
516 * be turned on when this widget gets the focus, and off when it loses it. But that is
517 * the responsability of higher level software (subclasses).
518 */
519 public:
520 nonvirtual bool GetSelectionShown () const;
521 virtual void SetSelectionShown (bool shown);
522
523 private:
524 bool fSelectionShown;
525
526 public:
527 nonvirtual size_t GetSelectionStart () const;
528 nonvirtual size_t GetSelectionEnd () const;
529 nonvirtual void GetSelection (size_t* start, size_t* end) const;
530 virtual void SetSelection (size_t start, size_t end);
531
532 protected:
533 nonvirtual void SetSelection_ (size_t start, size_t end);
534
535 protected:
536 class HilightMarker : public Marker {
537 public:
538 HilightMarker () = default;
539 };
540 HilightMarker* fHiliteMarker;
541 bool fWeAllocedHiliteMarker;
542
543 protected:
544 nonvirtual void SetHilightMarker (HilightMarker* newHilightMarker); // NB: Caller must free, and call SetHilightMarker (nullptr)
545
546 /*
547 * API to get postions of text.
548 */
549 public:
550 /*
551 @METHOD: TextImager::GetCharLocation
552 @DESCRIPTION: <p>Returns the (document relative) pixel location (rectangle) of the character after the given
553 marker position.</p>
554 <p>GetCharLocation () returns the rectangle bounding the character after marker-position
555 'afterPosition'. The position MUST be a valid one (0..GetEnd ()), but could
556 refer to the end of the buffer - and so there would be no character width in this case.</p>
557 <p>Note that this is also somewhat ineffecient for larger documents. You can often use
558 @'TextImager::GetCharLocationRowRelative' or @'TextImager::GetCharWindowLocation' instead.</p>
559 */
560 virtual Led_Rect GetCharLocation (size_t afterPosition) const = 0;
561 /*
562 @METHOD: TextImager::GetCharAtLocation
563 @DESCRIPTION: <p>Always returns a valid location, though it might not make a lot of
564 since if it is outside the ImageRect.</p>
565 <p>Note that this is also somewhat ineffecient for larger documents. You can often use
566 @'TextImager::GetRowRelativeCharAtLoc' or @'TextImager::GetCharAtWindowLocation' instead.</p>
567 */
568 virtual size_t GetCharAtLocation (const Led_Point& where) const = 0;
569 /*
570 @METHOD: TextImager::GetCharWindowLocation
571 @DESCRIPTION: <p>Like @'TextImager::GetCharLocation' but returns things relative to the start of the window.</p>
572 <p><em>NB:</em> if the location is outside of the current window, then the size/origin maybe pinned to some
573 arbitrary value, which is pixelwise above (or beleow according to before or after window start/end) the window.</p>
574 */
575 virtual Led_Rect GetCharWindowLocation (size_t afterPosition) const = 0;
576 /*
577 @METHOD: TextImager::GetCharAtWindowLocation
578 @DESCRIPTION: <p>Like @'TextImager::GetCharAtLocation' but assumes 'where' is presented window-relative.</p>
579 <p>See also @'TextInteractor::GetCharAtClickLocation'</p>
580 */
581 virtual size_t GetCharAtWindowLocation (const Led_Point& where) const = 0;
582
583 /*
584 @METHOD: TextImager::GetStartOfRow
585 @DESCRIPTION: <p>GetStartOfRow () returns the position BEFORE the first displayed character in the row.</p>
586 */
587 virtual size_t GetStartOfRow (size_t rowNumber) const = 0;
588 /*
589 @METHOD: TextImager::GetStartOfRowContainingPosition
590 @DESCRIPTION: <p></p>
591 */
592 virtual size_t GetStartOfRowContainingPosition (size_t charPosition) const = 0;
593 /*
594 @METHOD: TextImager::GetEndOfRow
595 @DESCRIPTION: <p>GetEndOfRow () returns the position AFTER the last displayed character in the row.
596 Note that this can differ from the end of row returned by @'TextImager::GetRealEndOfRow' because of characters removed by
597 @TextImager::RemoveMappedDisplayCharacters'.</p>
598 <p>See also @'TextImager::GetRealEndOfRow'.</p>
599 */
600 virtual size_t GetEndOfRow (size_t rowNumber) const = 0;
601 /*
602 @METHOD: TextImager::GetEndOfRowContainingPosition
603 @DESCRIPTION: <p></p>
604 */
605 virtual size_t GetEndOfRowContainingPosition (size_t charPosition) const = 0;
606 /*
607 @METHOD: TextImager::GetRealEndOfRow
608 @DESCRIPTION: <p>GetRealEndOfRow () returns the position AFTER the last character in the row. See
609 @'TextImager::GetEndOfRow'. Note - for the last row of the document, this could be include the
610 'bogus character'. So - this will always return a value &lt;= @'TextStore::GetLength' () + 1</p>
611 */
612 virtual size_t GetRealEndOfRow (size_t rowNumber) const = 0;
613 /*
614 @METHOD: TextImager::GetRealEndOfRowContainingPosition
615 @DESCRIPTION: <p></p>
616 */
617 virtual size_t GetRealEndOfRowContainingPosition (size_t charPosition) const = 0;
618 /*
619 @METHOD: TextImager::GetRowContainingPosition
620 @DESCRIPTION: <p></p>
621 */
622 virtual size_t GetRowContainingPosition (size_t charPosition) const = 0;
623 /*
624 @METHOD: TextImager::GetRowCount
625 @DESCRIPTION: <p></p>
626 */
627 virtual size_t GetRowCount () const = 0;
628
629 nonvirtual size_t GetRowLength (size_t rowNumber) const;
630
631 public:
632 /*
633 @METHOD: TextImager::GetCharLocationRowRelativeByPosition
634 @DESCRIPTION: <p></p>
635 */
636 virtual Led_Rect GetCharLocationRowRelativeByPosition (size_t afterPosition, size_t positionOfTopRow, size_t maxRowsToCheck) const = 0;
637
638 // Simple wrappers on GetStartOfRowContainingPosition () etc to allow row navigation without the COSTLY
639 // computation of row#s.
640 public:
641 nonvirtual size_t GetStartOfNextRowFromRowContainingPosition (size_t charPosition) const;
642 nonvirtual size_t GetStartOfPrevRowFromRowContainingPosition (size_t charPosition) const;
643
644 public:
645 /*
646 @METHOD: TextImager::GetRowHeight
647 @DESCRIPTION: <p>This API is redundent, but can be much more efficient to get at this information
648 than GetCharLocation() - especially in subclasses like MultiRowImager using RowRefernces.</p>
649 */
650 virtual DistanceType GetRowHeight (size_t rowNumber) const = 0;
651
652 public:
653 /*
654 @METHOD: TextImager::GetRowRelativeBaselineOfRowContainingPosition
655 @DESCRIPTION: <p>Returns the number of pixels from the top of the given row, to the baseline.
656 The 'charPosition' is a markerPosition just before any character in the row.</p>
657 */
658 virtual DistanceType GetRowRelativeBaselineOfRowContainingPosition (size_t charPosition) const = 0;
659
660 public:
661 /*
662 @METHOD: TextImager::GetTextDirection
663 @DESCRIPTION: <p>Returns the text direction (left to right or right to left) of the given character
664 (the one just after the given marker position).</p>
665 */
666 virtual TextDirection GetTextDirection (size_t charPosition) const = 0;
667
668 public:
669 virtual TextLayoutBlock_Copy GetTextLayoutBlock (size_t rowStart, size_t rowEnd) const;
670
671 /*
672 * Figure the region bounding the given segment of text. Useful for displaying
673 * some sort of text hilight, in addition (or apart from) the standard hilighting
674 * of text. Note we use a VAR parameter for the region rather than returing it
675 * cuz MFC's CRgn class doesn't support being copied.
676 */
677 public:
678 virtual vector<Led_Rect> GetSelectionWindowRects (size_t from, size_t to) const;
679 virtual void GetSelectionWindowRegion (Region* r, size_t from, size_t to) const;
680
681 public:
682 /*
683 @METHOD: TextImager::Draw
684 @DESCRIPTION: <p>This is the API called by the underlying window system (or wrappers) to get the imager
685 to draw its currently displayed window. The argument 'subsetToDraw' defines the subset (in the same coordinate system
686 the windowRect (@'TextImager::GetWindowRect') was specified in (so it will generally be a subset of the windowrect).</p>
687 */
688 virtual void Draw (const Led_Rect& subsetToDraw, bool printing) = 0;
689
690 // Misc default foreground/background color attributes
691 public:
692 /*
693 @METHOD: TextImager::DefaultColorIndex
694 @DESCRIPTION: <p>Enumerator of different color-table default names used by these routines:
695 <ul>
696 <li>@'TextImager::GetEffectiveDefaultTextColor'</li>
697 <li>@'TextImager::GetEffectiveDefaultTextColor'</li>
698 <li>@'TextImager::ClearDefaultTextColor'</li>
699 <li>@'TextImager::SetDefaultTextColor'</li>
700 </ul>
701 </p>
702 <p>The values are:
703 <table>
704 <tr>
705 <td>Name</td> <td>Default</td> <td>Description</td>
706 </tr>
707 <tr>
708 <td>eDefaultTextColor</td> <td>Led_GetTextColor&nbsp;()</td> <td>Color used for plain text. This color choice is frequently overriden elsewhere.</td>
709 </tr>
710 <tr>
711 <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>
712 </tr>
713 <tr>
714 <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>
715 </tr>
716 <tr>
717 <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>
718 </tr>
719 </table>
720 </p>
721 */
722 enum DefaultColorIndex {
723 eDefaultTextColor,
724 eDefaultBackgroundColor,
725 eDefaultSelectedTextColor,
726 eDefaultSelectedTextBackgroundColor,
727 eMaxDefaultColorIndex
728 };
729 nonvirtual Color* GetDefaultTextColor (DefaultColorIndex dci) const;
730 nonvirtual Color GetEffectiveDefaultTextColor (DefaultColorIndex dci) const;
731 nonvirtual void ClearDefaultTextColor (DefaultColorIndex dci);
732 nonvirtual void SetDefaultTextColor (DefaultColorIndex dci, const Color& textColor);
733
734 private:
735 Color* fDefaultColorIndex[eMaxDefaultColorIndex];
736
737 public:
738 virtual void EraseBackground (Tablet* tablet, const Led_Rect& subsetToDraw, bool printing);
739
740 public:
741 virtual void HilightArea (Tablet* tablet, Led_Rect hiliteArea);
742 virtual void HilightArea (Tablet* tablet, const Region& hiliteArea);
743
744 protected:
745 virtual void DrawRow (Tablet* tablet, const Led_Rect& currentRowRect, const Led_Rect& invalidRowRect, const TextLayoutBlock& text,
746 size_t rowStart, size_t rowEnd, bool printing);
747 virtual void DrawRowSegments (Tablet* tablet, const Led_Rect& currentRowRect, const Led_Rect& invalidRowRect,
748 const TextLayoutBlock& text, size_t rowStart, size_t rowEnd);
749 virtual void DrawRowHilight (Tablet* tablet, const Led_Rect& currentRowRect, const Led_Rect& invalidRowRect,
750 const TextLayoutBlock& text, size_t rowStart, size_t rowEnd);
751 virtual void DrawInterLineSpace (DistanceType interlineSpace, Tablet* tablet, CoordinateType vPosOfTopOfInterlineSpace,
752 bool segmentHilighted, bool printing);
753
754 protected:
755 virtual bool ContainsMappedDisplayCharacters (const Led_tChar* text, size_t nTChars) const;
756 virtual void ReplaceMappedDisplayCharacters (const Led_tChar* srcText, Led_tChar* copyText, size_t nTChars) const;
757 virtual size_t RemoveMappedDisplayCharacters (Led_tChar* copyText, size_t nTChars) const;
758 virtual void PatchWidthRemoveMappedDisplayCharacters (const Led_tChar* srcText, DistanceType* distanceResults, size_t nTChars) const;
759
760 protected:
761 static bool ContainsMappedDisplayCharacters_HelperForChar (const Led_tChar* text, size_t nTChars, Led_tChar charToMap);
762 static void ReplaceMappedDisplayCharacters_HelperForChar (Led_tChar* copyText, size_t nTChars, Led_tChar charToMap, Led_tChar charToMapTo);
763 static size_t RemoveMappedDisplayCharacters_HelperForChar (Led_tChar* copyText, size_t nTChars, Led_tChar charToRemove);
764 static void PatchWidthRemoveMappedDisplayCharacters_HelperForChar (const Led_tChar* srcText, DistanceType* distanceResults,
765 size_t nTChars, Led_tChar charToRemove);
766
767 protected:
768 virtual void DrawSegment (Tablet* tablet, size_t from, size_t to, const TextLayoutBlock& text, const Led_Rect& drawInto,
769 const Led_Rect& invalidRect, CoordinateType useBaseLine, DistanceType* pixelsDrawn);
770
771 public:
772 // Note we REQUIRE that useBaseLine be contained within drawInto
773 nonvirtual void DrawSegment_ (Tablet* tablet, const FontSpecification& fontSpec, size_t from, size_t to, const TextLayoutBlock& text,
774 const Led_Rect& drawInto, CoordinateType useBaseLine, DistanceType* pixelsDrawn) const;
775
776 protected:
777 // distanceResults must be an array of (to-from) elements - which is filled in with the widths
778 // of subsegments from 'from' up to 'i', where 'i' is the ith-from element of 'distanceResults'.
779 // For mbyte chars - splitting char results and looking at those values are UNDEFINED).
780 //
781 // In general - textwidth of text from from to b (where b MUST be contained in from/to)
782 // is distanceResults[b-from-1] - and of course ZERO if b == from
783 //
784 virtual void MeasureSegmentWidth (size_t from, size_t to, const Led_tChar* text, DistanceType* distanceResults) const;
785
786 public:
787 nonvirtual void MeasureSegmentWidth_ (const FontSpecification& fontSpec, size_t from, size_t to, const Led_tChar* text,
788 DistanceType* distanceResults) const;
789
790 protected:
791 virtual DistanceType MeasureSegmentHeight (size_t from, size_t to) const;
792
793 public:
794 nonvirtual DistanceType MeasureSegmentHeight_ (const FontSpecification& fontSpec, size_t from, size_t to) const;
795
796 protected:
797 virtual DistanceType MeasureSegmentBaseLine (size_t from, size_t to) const;
798
799 public:
800 nonvirtual DistanceType MeasureSegmentBaseLine_ (const FontSpecification& fontSpec, size_t from, size_t to) const;
801
802 public:
803 /*
804 @METHOD: TextImager::CalcSegmentSize
805 @DESCRIPTION: <p>Measures the distance in pixels from 'from' to 'to'.
806 This function requires that 'from' and 'to' are on the same row.</p>
807 <p>Note that for bidirectional text, the notion of 'from' and 'to' CAN be ambiguous. A given
808 marker-position between two characters in the buffer could refer to characters that are VISUALLY
809 not next to one another. Luckily - there is a convenient disambiguator in this API. Since we are always
810 measuring 'from' one position 'to' another - we assume the region in question is the one <em>after</em>
811 the 'from' and <em>before</em> the 'to'.</p>
812 <p>Note - because of this definition - and because of bidirectional characters - trying to find
813 the left and right side of a character with:
814 <CODE><PRE>
815 LHS = CalcSegmentSize (ROWSTART, i)
816 RHS = CalcSegmentSize (ROWSTART, i+1);
817 </PRE></CODE>
818 will NOT work. Use @'TextImager::GetRowRelativeCharLoc' to properly take into account the script
819 directionality runs.
820 </P>
821 */
822 virtual DistanceType CalcSegmentSize (size_t from, size_t to) const = 0;
823
824 public:
825 /*
826 @METHOD: TextImager::GetRowRelativeCharLoc
827 @DESCRIPTION: <p>Returns the row-relative bounds of the given character (the one from charLoc to charLoc + 1).
828 The left hand side is window-relative, so if you want window coordinates, add the window left.
829 This routine is more complex than just calling @'TextImager::CalcSegmentSize' () on the right and
830 left sides of the character, because of bidirectional editing.</p>
831 <p>Note that even though the character maybe RTL or LTR, we still return the *lhs <= *rhs for the
832 character cell. This routine calls @'TextImager::RemoveMappedDisplayCharacters' and if the given character is
833 removed, its proper LHS will still be returned, but the RHS will be EQUAL to its LHS.</p>
834 <p>Note that 'charLoc' must be a legal char location or just at the end of the buffer - 0 <= charLoc <= GetEnd ().
835 if charLoc==GetEnd() - you will get the same results as in the @'TextImager::RemoveMappedDisplayCharacters' case.</p>
836 <p>See also @'TextImager::GetRowRelativeCharLoc'</p>
837 */
838 virtual void GetRowRelativeCharLoc (size_t charLoc, DistanceType* lhs, DistanceType* rhs) const = 0;
839
840 public:
841 /*
842 @METHOD: TextImager::GetRowRelativeCharAtLoc
843 @DESCRIPTION: <p>Look in the row which begins at 'rowStart'. Look 'hOffset' pixels into the row, and return the character which
844 would be found at that point (assuming the vertical positioning is already correct). If its too far to the right, return ENDOFROW.</p>
845 <p>See also @'TextImager::GetRowRelativeCharLoc'</p>
846 <p>Returns the last character in the row (not a marker PAST that
847 character) if the point passed in is past the end of the last character in the row.
848 </p>
849 */
850 virtual size_t GetRowRelativeCharAtLoc (CoordinateType hOffset, size_t rowStart) const = 0;
851
852 // Font info caches...
853 private:
854 mutable FontSpecification fCachedFontSpec;
855 mutable FontMetrics fCachedFontInfo;
856#if qStroika_Foundation_Common_Platform_Windows
857 mutable FontObject* fCachedFont;
858#else
859 mutable bool fCachedFontValid;
860#endif
861
862 protected:
863 class FontCacheInfoUpdater {
864 public:
865 FontCacheInfoUpdater (const TextImager* imager, Tablet* tablet, const FontSpecification& fontSpec);
866 ~FontCacheInfoUpdater ();
867
868 public:
869 nonvirtual FontMetrics GetMetrics () const;
870
871 private:
872 const TextImager* fImager;
873
874#if qStroika_Foundation_Common_Platform_Windows
875 private:
876 Tablet* fTablet;
877 HGDIOBJ fRestoreObject;
878 HGDIOBJ fRestoreAttribObject;
879#endif
880 };
881 friend class FontCacheInfoUpdater;
882
883 private:
884 friend class GoalColumnRecomputerControlContext;
885 };
886
887 /*
888 @CLASS: TextImager::Tablet_Acquirer
889 @DESCRIPTION: <p>Stack-based resource allocation/deallocation. See @'TextImager::AcquireTablet'.</p>
890 */
891 class TextImager::Tablet_Acquirer {
892 public:
893 Tablet_Acquirer (const TextImager* textImager)
894 : fTextImager (textImager)
895 {
896 AssertNotNull (fTextImager);
897 fTablet = fTextImager->AcquireTablet ();
898 }
899 operator Tablet* ()
900 {
901 AssertNotNull (fTablet);
902 return (fTablet);
903 }
904 Tablet* operator->()
905 {
906 return fTablet;
907 }
908 ~Tablet_Acquirer ()
909 {
910 AssertNotNull (fTextImager);
911 AssertNotNull (fTablet);
912 fTextImager->ReleaseTablet (fTablet);
913 }
914
915 private:
916 const TextImager* fTextImager;
917 Tablet* fTablet;
918 };
919
920 /*
921 @CLASS: TextImager::SimpleTabStopList
922 @BASES: @'TabStopList'
923 @DESCRIPTION: <p>A simple tabstop implementation in which all tabstops are equidistant from each other, and start
924 at position zero. This is commonly used as the default tabstop object.</p>
925 */
926 class TextImager::SimpleTabStopList : public TabStopList {
927 public:
928 SimpleTabStopList (TWIPS twipsPerTabStop);
929
930 public:
931 virtual TWIPS ComputeIthTab (size_t i) const override;
932 virtual TWIPS ComputeTabStopAfterPosition (TWIPS afterPos) const override;
933
934 public:
935 TWIPS fTWIPSPerTabStop;
936 };
937
938 /*
939 @CLASS: TextImager::GoalColumnRecomputerControlContext
940 @DESCRIPTION: <p></p>
941 */
942 class TextImager::GoalColumnRecomputerControlContext {
943 public:
944 GoalColumnRecomputerControlContext (TextImager& imager, bool suppressRecompute);
945 ~GoalColumnRecomputerControlContext ();
946
947 private:
948 TextImager& fTextImager;
949 bool fSavedSuppressRecompute;
950 };
951
952 DISABLE_COMPILER_MSC_WARNING_START (4250) // inherits via dominance warning
953
954 /*
955 @CLASS: TrivialImager<TEXTSTORE,IMAGER>
956 @DESCRIPTION: <p>Handy little template, if you want to use the power of Led, but just to wrap a particular imager,
957 in a localized, one-time fasion, say todo printing, or some such. Not for interactors.</p>
958 <p>Depending on the IMAGER you wish to use, you may want to try @'TrivialImager_Interactor<TEXTSTORE,IMAGER>'
959 or @'TrivialWordWrappedImager<TEXTSTORE, IMAGER>' - both of which come with examples of their use.</p>
960 */
961 template <typename TEXTSTORE, typename IMAGER>
962 class TrivialImager : public IMAGER {
963 private:
964 using inherited = IMAGER;
965
966 protected:
967 TrivialImager (Tablet* t);
968
969 public:
970 TrivialImager (Tablet* t, Led_Rect bounds, const Led_tString& initialText = LED_TCHAR_OF (""));
971 ~TrivialImager ();
972
973 public:
974 nonvirtual void Draw (bool printing = false);
975 virtual void Draw (const Led_Rect& subsetToDraw, bool printing) override;
976
977 protected:
978 virtual Tablet* AcquireTablet () const override;
979 virtual void ReleaseTablet (Tablet* /*tablet*/) const override;
980
981 protected:
982 virtual void EraseBackground (Tablet* tablet, const Led_Rect& subsetToDraw, bool printing) override;
983
984 protected:
985 nonvirtual void SnagAttributesFromTablet ();
986
987 public:
988 nonvirtual Color GetBackgroundColor () const;
989 nonvirtual void SetBackgroundColor (Color c);
990 nonvirtual bool GetBackgroundTransparent () const;
991 nonvirtual void SetBackgroundTransparent (bool transparent);
992
993 private:
994 TEXTSTORE fTextStore;
995 Tablet* fTablet;
996 bool fBackgroundTransparent;
997 };
998 DISABLE_COMPILER_MSC_WARNING_END (4250) // inherits via dominance warning
999#endif
1000
1001}
1002
1003/*
1004 ********************************************************************************
1005 ***************************** Implementation Details ***************************
1006 ********************************************************************************
1007 */
1008#include "TextImager.inl"
1009
1010#endif /*_Stroika_Frameworks_Led_TextImager_h_*/
#define AssertNotNull(p)
Definition Assertions.h:333