Sign in Join
Wiki copytemporary

RichMemo: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: RichMemo

OldNewLine
1+<figure>
2+<img src="richmemosample.png" title="RichMemo macOS screenshot (thanks to Dominique Louis)" />
3+<figcaption>RichMemo macOS screenshot (thanks to Dominique Louis)</figcaption>
4+</figure>
5+
6+## Introduction
7+
8+**RichMemo** is a package that includes a component to replace the Delphi TRichEdit component. It's designed in cross-platform way, so implementation is possible for the following platforms: Win32, MacOSX and Linux. Since, being cross-platform is the primary target, the native RichMemo API can be extended to make it Delphi's RichEdit compatible.
9+
10+Its main characteristics are
11+
12+- Text highlight
13+- System based Unicode edit support
14+
15+Planned: (patches are welcomed)
16+
17+- Adding images into text
18+- Embedding LCL Controls?
19+
20+The download contains the component, an installation package and a demo application, that illustrates the features of the component along with some instrumentation for evaluating the chart on a given system.
21+
22+Please add your bugreports, feature requests, pull request on [GitHub](https://github.com/skalogryz/richmemo/issues).
23+
24+## Licensing
25+
26+Author: Dmitry 'skalogryz' Boyarintsev
27+
28+License: [modified](http://svn.freepascal.org/svn/lazarus/trunk/COPYING.modifiedLGPL) [LGPL](http://svn.freepascal.org/svn/lazarus/trunk/COPYING.LGPL) (the same as the FPC RTL and the Lazarus LCL). You can contact the author if the modified LGPL doesn't work with your project licensing.
29+
30+## Download
31+
32+The easiest way to install the stable version of the component is via the [[Online Package Manager]] included in current Lazarus versions.
33+
34+The latest trunk is available on GitHub: <https://github.com/skalogryz/richmemo/> . The latest sources can be downloaded by [this GitHub link](https://github.com/skalogryz/richmemo/archive/refs/heads/master.zip).
35+
36+## Installation
37+
38+- Download the package set.
39+- Open **richmemo_design.lpk** package, and install it, rebuilding the IDE.
40+- TRichMemo is added to the 'Common Controls' component page.
41+
42+<figure>
43+<img src="RichMemoPalette.PNG" title="RichMemoPalette.PNG" />
44+<figcaption>RichMemoPalette.PNG</figcaption>
45+</figure>
46+
47+Originally **richmemopackage** was a single file, containing IDE specific and run-time needed classes. However, that meant that IDEIntf package would be a requirement for any project using RichMemo. In order to mitigate that the package was split into to separate packages:
48+
49+- richmemopackage - run-time rich memo component.
50+- richmemo_design - IDE extensions to support for RichMemo component.
51+
52+## Font Parameters
53+
54+Font parameters are generally represented by TFontParams record. The type is used to describe **rich** attributes of the font. Some attributes goes beyond the scope of TFont class, thus an extra type was necessary. However, most of the TFont related data types are reused (i.e. TFontStyles).
55+
56+- Name - name of the font (family).
57+- Size - size of the font in points (passing a negative value might produce unexpected results).
58+- Color - color of the font.
59+- Style - font styles, including Bold, italic, strike-through, underline.
60+- HasBkClr - boolean flag, if the text for the should have background color (if true, BkColor field is used, otherwise BkColor is ignored). Transparency is not supported.
61+- BkColor - background (aka highlight) color for the text.
62+- VScriptPos - vertical adjustment for the text ![](/wiki-images/c/c1/Richmemo_Subsuper.png).
63+ - vpNormal - no special position.
64+ - vpSubscript - the text is subscript.
65+ - vpSuperscript - the text is superscript.
66+
67+
68+The actual vertical offset of super/sub scripts depends on OS implementation and currently cannot be controlled.
69+
70+You can assign FontParams from TFont structure. For that you'll need to use `GetFontParams(afont: TFont)` function. Note, most of LCL controls have TFont set to "default" values. These are not actual values, but rather an indication that control's default font should be used (similar to crDefault for TColor).
71+
72+GetFontParams function resolves "Default" font name and returns the actual font family name.
73+
74+## Methods
75+
76+### SetTextAttributes
77+
78+```delphi
79+procedure SetTextAttributes(TextStart, TextLen: Integer; AFont: TFont);
80+```
81+
82+- TextStart : Integer - the first character to be modified
83+- TextLen : Integer - number of characters to be modified
84+- AFont : TFont - a font that should be applied to the part of the text
85+
86+```delphi
87+procedure SetTextAttributes(TextStart, TextLen: Integer; const TextParams: TFontParams);
88+```
89+
90+- TextStart : Integer - the first character to be modified
91+- TextLen : Integer - number of characters to be modified
92+- TextParams : TFontParams - font parameters to be set
93+
94+SetTextureAttributes methods are changing specified text range style. Font parameters is passed in both methods, by AFont parameter (LCL TFont object) or by TFontParams (declared at RichMemo).
95+
96+Setting text attributes does not change current selection. If it's necessary to modify the style of currently selected text, you should SelStart and SelLength as a text range values:
97+
98+```delphi
99+RichMemo1.SetTextAttributes(RichMemo1.SelStart, RichMemo1.SelLength, FontDialog1.Font);
100+```
101+
102+### GetTextAttributes
103+
104+```delphi
105+function GetTextAttributes(TextStart: Integer; var TextParams: TFontParams): Boolean; virtual;
106+```
107+
108+- TextStart : Integer - the character position to be queried for font parameters
109+- var TextParams : TFontParams - output value, filled with charachter's font attributes. If the method fails and returns false, record's field values is undefined.
110+
111+Fill font params of the character, at TextStart position. Method returns True if textstart is valid character position, and False overwise.
112+
113+### GetStyleRange
114+
115+```delphi
116+function GetStyleRange(CharPos: Integer; var RangeStart, RangeLen: Integer): Boolean; virtual;
117+```
118+
119+Returns a range of characters that share the same font parameters, i.e. all characters in the range has the same font name, size, color and styles.
120+
121+- CharPos : Integer - a character that belongs to the style range. It's not necessary for this position to be at the begining on the style range. It can be in the middle on in the end of the style range. The first character position is returned by RangeStart parameter.
122+- var RangeStart : Integer - the first character in the range
123+- var RangeLen : Integer - number of characthers in the range
124+
125+The method returns true if successed. the method returns false, if CharPos is incorrect value - more than available characters or some other error.
126+
127+### CharAtPos
128+
129+```delphi
130+function CharAtPos(x,y: Integer): Integer; virtual;
131+```
132+
133+Returns zero-based character offset (not UTF8 position). Returns -1 if fails.
134+
135+- x, y - is a point in the client area of the RichMemo control.
136+
137+The method is aware of scrolling state of the control. Thus two calls to CharAtPos(0,0) might return different values if scroll positions is changed between calls. See "examples/hittest" for the sample of usage.
138+
139+If specified x,y would be out of the content of RichMemo the return value is undefined. It is left up to widgetset to either return -1 or closes character available.
140+
141+### SetRangeColor
142+
143+```delphi
144+procedure SetRangeColor(TextStart, TextLength: Integer; FontColor: TColor);
145+```
146+
147+The method sets color of characters in the specified range to the FontColor. Other font parameters (name, size, styles) are left unchanged.
148+
149+- TextStart: Integer - the first character in the range
150+- TextLength: Integer - number of characters in the range
151+- FontColor: TColor - color that should be set
152+
153+### SetRangeParams
154+
155+```delphi
156+ procedure SetRangeParams(TextStart, TextLength: Integer; ModifyMask: TTextModifyMask;
157+ const fnt: TFontParams; AddFontStyle, RemoveFontStyle: TFontStyles); overload;
158+```
159+
160+The method changes font parameters in the specified range.
161+
162+- TextStart - the first character in the range
163+- TextLength - number of characters in the range
164+- ModifyMask - shows what exact font attributes must be updated The mask accepts any combination of the following values:
165+ - tmm_Color - font color would be modified (fnt.Color field must be populated)
166+ - tmm_Name - font name would be modified (fnt.Name field must be populated)
167+ - tmm_Size - font size would be modified (fnt.Size field must be populated)
168+ - tmm_Styles - font styles would be modified as specified by AddFontStyle, RemoveFontStyle parameters
169+ - tmm_BackColor - text highlight (background) color would be modified. (fnt.HasBkClr and fnt.BkColor fields must be populated )
170+ - Sending an empty mask will cause the method to return without making any changes.
171+- fnt - font parameters that are going to be used, depending on the ModifyMask values.
172+- AddFontStyle - set of styles that should be applied to the range (used only if tmm_Styles in ModifyMask, otherwise ignored)
173+- RemoveFontStyle - set of font style should should be removed from the range (used only if tmm_Styles in ModifyMask, otherwise ignored)
174+
175+```delphi
176+procedure SetRangeParams(TextStart, TextLength: Integer;
177+ ModifyMask: TTextModifyMask; const FontName: String;
178+ FontSize: Integer; FontColor: TColor;
179+ AddFontStyle, RemoveFontStyle: TFontStyles);
180+```
181+
182+The overloaded version of the method. It's just a wrapper around the parameter using TFontParams structure. The method doesn't support changing of background color, you should use the TFontParams version
183+
184+- FontName - font name that should set (used only if tmm_Name in ModifyMask, otherwise ignored)
185+- FontColor - font color that should set (used only if tmm_Color in ModifyMask, otherwise ignored)
186+
187+For example, if the following code is used
188+
189+```delphi
190+ RichMemo1.SetRangeParams (
191+ RichMemo1.SelStart, RichMemo1.SelLength,
192+ [tmm_Styles, tmm_Color], // changing Color and Styles only
193+ '', // this is font name - it's not used, thus we can leave it empty
194+ 0, // this is font size - it's font size, we can leave it empty
195+ clGreen, // making all the text in the selected region green color
196+ [fsBold, fsItalic], // adding Bold and Italic Styles
197+ []
198+ );
199+```
200+
201+### GetParaAlignment
202+
203+Gets paragraph alignment
204+
205+```delphi
206+function GetParaAlignment(TextStart: Integer; var AAlign: TParaAlignment): Boolean;
207+```
208+
209+- TextStart - the position of the character that's belongs to the paragraph
210+- AAlign - gets alignment of a paragraph.
211+
212+**Note**:
213+
214+- Win32 - full-width Justification doesn't work on Windows XP and earlier.
215+- OSX - due to Carbon limitation, this is not working in Carbon, but works for Cocoa widgetset.
216+
217+### SetParaAlignment
218+
219+Sets paragraph alignent
220+
221+```delphi
222+procedure SetParaAlignment(TextStart, TextLen: Integer; AAlign: TParaAlignment);
223+```
224+
225+**Note**:
226+
227+- Win32 - full-width Justification doesn't work on Windows XP and earlier.
228+- OSX - due to Carbon limitation, this is not working in Carbon, but works for Cocoa widgetset. It's also need to loaded that the correct alignment would be loaded from RTF file
229+
230+### GetParaMetric
231+
232+Returns paragraph metrics for a given paragraph ![](/wiki-images/b/be/Richmemo_parametric.PNG)
233+
234+- FirstLine - an offset for the first line (in points), of the paragraph from the beginning of control
235+- TailIndent - an offset for each line (in points), except for the first line, of the paragraph from the end of the control
236+- HeadIndent - an offset for each line (in points) of the paragraph from the end of the control
237+- SpaceBefore - additional space before paragraph (in points)
238+- SpaceAfter - additional space after paragraph (in points)
239+- LineSpacing - a factor used to calculate line spacing between lines in the paragraph. The factor is applied to Font size (tallest in the line), not the line height. Thus it's matching CSS's line-height property. Note that normal line-spacing is 1.2. I.e. font of 12 pt size, an actual lines spacing set to 1.2, would result in 14pt line height.
240+ - not supported in WinXP or earlier
241+ - most of the system would not allow if linespacing is set to less than 1.2. The value would be ignored and they'd default to 1.2
242+
243+**Note**
244+
245+- RichEdit paragraph settings are specified in pixels, not points, so you'll need to either convert these sizes yourself or use RichMemoHelper methods
246+- the notation of "Left"/"Right" offsets is avoided to prevent confusion for RTL.
247+
248+```delphi
249+function GetParaMetric(TextStart: Integer; var AMetric: TParaMetric): Boolean;
250+```
251+
252+procedure SetParaMetric(TextStart, TextLen: Integer; const AMetric: TParaMetric);
253+
254+</source>
255+
256+It's recommended to use InitParaMetric function in order to initialize TParaMetric structure. Otherwise zeroing structure out is by it size (i.e. FillChar(m, sizeof(TParaMetric), 0) is sufficient. If TParaMetric values are received from GetParaMetric call, then the structure is considered to be initialized properly.
257+
258+### GetParaRange
259+
260+Returns character range for a paragraph. The paragraph is identified by a character.
261+
262+```delphi
263+ function GetParaRange(CharOfs: Integer; var ParaRange: TParaRange): Boolean;
264+ function GetParaRange(CharOfs: Integer; var TextStart, TextLength: Integer): Boolean;
265+```
266+
267+CharOfs - is a character that belongs to a paragraph which range is returned. TParaRange - is a structure that returns 3 fields
268+
269+- start - the first character in the paragraph
270+- lengthNoBr - the length of the paragraph, excluding the line break character
271+- length - the length of the paragrpah, including the line break, if present the last line in the control doesn't contain a line break character, thus length = lengthNoBr value.
272+
273+### SetParaTabs
274+
275+Sets the set of tab stops to paragraphs in the specified length.
276+
277+```delphi
278+ procedure SetParaTabs(TextStart, TextLen: Integer; const AStopList: TTabStopList);
279+```
280+
281+- TextStart
282+- TextLen
283+- AStopList - the structure that contains an array of tab stops.
284+ - Count - specifies the number of initialized elements.
285+ - Tabs - array contains the description of each tab stop, consisting of an Offset and Alignment.
286+ - Offset - the tab point offset in points (from the left side of the control)
287+ - Align - the alignment of the tab stop (tabLeft - default one, tabCenter, tabRight, tabDecimal).
288+
289+**Notes**
290+
291+Renamefest
292+Prior to r4140 TTabAlignment enum values were taLeft, taCenter, taRight, taDecimal,but it did collide with Classes.TAlignment declarations. Thus the suffix was changed.
293+
294+win32
295+allows no more that 32 tabs
296+
297+gtk2
298+only tabLeft align is supported
299+
300+carbon
301+not implemented
302+
303+### GetParaTabs
304+
305+Gets the tab stops array. If no specific tabs were specified (widget default tab stops) AStopList count would be set to 0.
306+
307+```delphi
308+ function GetParaTabs(CharOfs: Integer; var AStopList: TTabStopList): Boolean;
309+```
310+
311+### SetParaNumbering
312+
313+Sets paragraphs automatic numbering and styles, such as bullets
314+
315+```delphi
316+procedure SetParaNumbering(TextStart, TextLen: Integer; const ANumber: TParaNumbering); virtual;
317+```
318+
319+- **TextStart** - the character of the the first paragraph to apply the style too
320+- **TextLength** - the number of characters that paragraph properties should be modified
321+- **TParaNumbering** - the structure describing the paragraph numbering information
322+
323+:* **style** - the style of numbering
324+
325+
326+
327+Note: for cross-platform compatibility, it's recommended to use either **pnNone**, **pnBullet**, **pnNumber** or **pnCustomChar**. RTF provides more support for numbering, yet not every platform rich-text widgetset supports them
328+
329+for initializing the structure you can use InitParaNumbering() or InitParaNumber() functions. Also using Default() would work too
330+
331+### SetRangeParaParams
332+
333+Sets paragraph metrics selectively
334+
335+```delphi
336+procedure SetRangeParaParams(TextStart, TextLength: Integer; ModifyMask: TParaModifyMask; const ParaMetric: TParaMetric);
337+```
338+
339+- TextStart - the character of the the first paragraph to apply the style too
340+- TextLength - the number of characters that paragraph properties should be modified
341+- ModifyMask - masks the identifies what para metric should be set for each paragraph within TextStart..TextLength block. Other characters will be left unmodified. Members of the set are
342+ - pmm_FirstLine - changes the first line indentation. FirstLine field of ParaMetric must be initialized
343+ - pmm_HeadIndent - changes the heading paragraph indentation. HeadIndent field of ParaMetric must be initialized
344+ - pmm_TailIndent - changes the trail paragraph indentation. TailIndent field of ParaMetric must be initialized
345+ - pmm_SpaceBefore - changes the space preceding . SpaceBefore field of ParaMetric must be initialized
346+ - pmm_SpaceAfter - changes the space after (below) the paragraph. SpaceAfter field of ParaMetric must be initialized
347+ - pmm_LineSpacing - line spacing within paragraph. LineSpace field of ParaMetric must be initialized
348+- ParaMetric - the structure containing the values to be set. Only fields specified by ModifyMask should be initialized
349+
350+### LoadRichText
351+
352+```delphi
353+function LoadRichText(Source: TStream): Boolean; virtual;
354+```
355+
356+- Source: TStream - a stream to read richtext data from
357+
358+The method loads RTF enocded data from the specified stream. Returns true if success, and false otherwise. If source is nil, the method returns false
359+
360+The content of TRichMemo is completely replaced by the content on the source stream. Current text selection is reset.
361+
362+### SaveRichText
363+
364+```delphi
365+function SaveRichText(Dest: TStream): Boolean; virtual;
366+```
367+
368+- Source: TStream - a stream to read richtext data from
369+
370+The method saves RTF enocded data to the specified stream. Returns true if success, and false otherwise. If source is nil, the method returns false
371+
372+Current state of TRichMemo is unchanged after the method returns
373+
374+### Search
375+
376+```delphi
377+function Search(const ANiddle: string; Start, Len: Integer; const SearchOpt: TSearchOptions): Integer;
378+```
379+
380+- ANiddle: string - a text to search for
381+- Start: Integer - a character position to start search from, if -1 or 0 search from the start
382+- Len: Integer - length (in characters, not bytes) of the text to search for the niddle.
383+- SearchOpt: TSearchOptions
384+ - soMatchCase - case sensitive search
385+ - soBackward - searching from the end of the document to the character identified by "Start" parameter
386+ - soWholeWord - searching for the whole word
387+
388+The method returns the character position of the found sub-string. The position is suitable for use in SelStart property. However, might not be usable for the direct Copy operation, if Unicode characters are present in the code. Instead UTF8Copy should be used.
389+
390+If the ANiddle was not found in the text, -1 is returned
391+
392+```delphi
393+function Search(const ANiddle: string; Start, Len: Integer; const SearchOpt: TSearchOptions;
394+ var TextStart, TextLength: Integer): Boolean;
395+```
396+
397+(The method is introduced with r5115)
398+
399+- ANiddle: string - a text to search for
400+- Start: Integer - a character position to start search from, if -1 or 0 search from the start
401+- Len: Integer - length (in characters, not bytes) of the text to search for the niddle.
402+- SearchOpt: TSearchOptions
403+ - soMatchCase - case sensitive search
404+ - soBackward - searching from the end of the document to the character identified by "Start" parameter
405+ - soWholeWord - searching for the whole word
406+- (output) ATextStart - a position of the text found (in characters)
407+- (output) ATextLength - length of the text found (in characters (cursor positions!))
408+
409+The method returns the true if searched sub-string is found, false otherwise.
410+
411+The returned ATextStart and ATextLength values are suitable for use in SelStart and SelLength properties.
412+
413+**Note**: for complex scripts, the found text might be exactly the same as the sub-string searched. The search behavior (on complex scripts) is widgetset specific. Thus searching for the same string on different widgetsets (i.e. win32 or gtk2) might produce different results. If you need the same results and don't get them, please create a bug report.
414+
415+If you need to extract the found text, you should use GetText() method (passing ATextStart and ATextLength values).
416+
417+### GetText, GetUText
418+
419+```delphi
420+ function GetText(TextStart, TextLength: Integer): String;
421+ function GetUText(TextStart, TextLength: Integer): UnicodeString;
422+```
423+
424+- TextStart: Integer - a character position to start extract from. (0 - is the first character in the text).
425+- TextLength: Integer - length (in characters, not bytes) of the text to extract
426+
427+GetText() returns UTF8 sub-string
428+
429+GetUText() returns UTF16 sub-string
430+
431+Current selection would not be affected by the operation. (If you see the selection affected, please report as an issue).
432+
433+You should not consider the efficiency of either method. For example, WinAPI internally operates with UTF16 characters, thus GetUText() **might** be more efficient for it. While Gtk2 operates UTF8 and calling GetText() **might** be more efficient for it. Instead of thinking about underlying system, you should be considering the needs of your task.
434+
435+### Redo
436+
437+```delphi
438+ procedure Redo;
439+```
440+
441+The method redos the last change [undone](http://lazarus-ccr.sourceforge.net/docs/lcl/stdctrls/tcustomedit.undo.html) previously.
442+
443+You can always call CanRedo to check, if there're any actions can be redone.
444+
445+## Properties
446+
447+### ZoomFactor
448+
449+```delphi
450+property ZoomFactor: double
451+```
452+
453+Read/Write property. Controls zooming of the RichMemo content. 1.0 - is no-zoom. Less than \< 1.0 - decrease zoom, more than 1.0 - increasing zooming. If 0 is set - value defaults to 1.0 zoom factor (no zoom).
454+
455+### HideSelection
456+
457+```delphi
458+property HideSelection: Boolean default false
459+```
460+
461+Read/Write property. If True RichMemo selection is hidden if the control is not focused. If False, the selection is shown all the time.
462+
463+### CanRedo
464+
465+```delphi
466+property CanRedo: Boolean
467+```
468+
469+If True, there're actions in Undo queue that can be redone. If False, there are no actions in Undo queue that can be redone. Calling to Redo won't have any effect.
470+
471+### Transparent
472+
473+<figure>
474+<img src="richmemo_transparent.png" title="richmemo_transparent.png" width="400" />
475+<figcaption>richmemo_transparent.png</figcaption>
476+</figure>
477+
478+```delphi
479+property Transparent: Boolean
480+```
481+
482+If True, the background of richmemo is not filled with any color, leaving any underlying controls or windows to be visible. (Suggested to be used with TImage that would provide the background) If False, (default) the background is filled with a solid color
483+
484+Note that the property might not affect "border" style of the the richmemo. You need to set BorderStyle to bsNone in order to disable it. For macOS this also hides the focus ring (when the control is focused)
485+
486+- Supported by Win32, Qt5 and Cocoa
487+
488+### Rtf
489+
490+```delphi
491+property Rtf: String
492+```
493+
494+<figure>
495+<img src="RtfEditor.PNG" title="RtfEditor.PNG" width="400" />
496+<figcaption>RtfEditor.PNG</figcaption>
497+</figure>
498+
499+Read/Write property that allows to read or write Rich-Text Formatted (RTF) text to the whole control. The intent of the property is to have an ability to set Rich-Text in design time, however the property would work in run-time as well.
500+
501+If then property value is an empty string then Lines property is used as a plain text to initialize control value in load-time.
502+
503+**Note:** The property is available in TRichMemo class only, it's not available (or even implemented in anyway) in TCustomRichMemo class. It is recommended to use LoadRichText/SaveRichText in order to modify the content of the page, just because they could consume less memory, than copying the whole rich-text into the memory.
504+
505+**Cross-platform and Forward Compatibility Note:** - currently the property is implemented based on the widgetset providede Rich-text read/write routines. It is very likely that in near future it would be changed to used RichMemo read/write RTF stream code only. The issue is with older systems might not support the latest version of RTF.
506+
507+So right now, you might run into an issue, If you create a project in Gtk2 and save the richmemo is design time containing Unicode characters. Then if you try to load it on the XP machine and would use widgetset native loader, you might see characters missing or rendered improperly. Changing to the usage of RichMemo RTF loader/saver will prevent this issue from happening. Currently you can also avoid it by registering RichMemo loaders (call RegisterRTFLoader, RegisterRTFSaver procedures in project initialization, before any RichMemo is loaded)
508+
509+## Events
510+
511+### OnSelectionChange
512+
513+```delphi
514+property OnSelectionChange: TNotifyEvent
515+TNotifyEvent = procedure (Sender: TObject) of object;
516+```
517+
518+The event is fired whenever, selection is changed within RichMemo, either programmatically or due to the user action.
519+
520+## TRichEditForMemo
521+
522+The class helper that implements RichEdit programmatic interface. The helper is declared at RichEditHelpers unit, so you have to add it to the uses section. Helpers are available in FPC 2.6.0 or later.
523+
524+### Methods
525+
526+#### FindText
527+
528+Searches a given range in the text for a target string
529+
530+### Properties
531+
532+#### SelAttributes
533+
534+Reads/Modifies character attributes of the current selection.
535+
536+### Paragraph
537+
538+Reads/Modifies paragraph attributes of the current selection.
539+
540+### RichMemoUtils
541+
542+The unit is introduced to add some useful OS-specific features for handling working with RichMemo
543+
544+#### InsertImageFromFile
545+
546+```delphi
547+function InsertImageFromFile (const ARichMemo: TCustomRichMemo; APos: Integer;
548+ const FileNameUTF8: string;
549+ const AImgSize: TSize
550+ ): Boolean = nil;
551+```
552+
553+*Disclaimer: the function would insert an image file into RichMemo (if implemented by the widgetset) But in a very inefficient way. The image would be read again and the memory would be re-allocated for the image every time. So please, don't use it for smileys in your chat instant messaging. A better API (with data caching) is considered. (That's why this method is not part of TCustomRichMemo class).*
554+
555+- APos - position in the text
556+- AImgSize - size to be inserted in POINTS, not pixels!. If both cx and cy are 0, the image would not be resized at all. If only one cx, cy is zero results are undefined.
557+
558+There's also an utility function **InsertImageFromFileNoResize** that calls InsertImageFromFile, passing size as 0.
559+
560+An example of use
561+
562+```delphi
563+ procedure TForm1.Button1Click(Sender: TObject);
564+ begin
565+ // running a dialog to select a picture to insert
566+ if OpenDialog1.Execute then
567+ // inserting the picture at the current RichMemo cursor position
568+ InsertImageFromFileNoResize(RichMemo1, RichMemo1.SelStart, OpenDialog1.FileName);
569+ end;
570+```
571+
572+### InsertStyledText,InsertColorStyledText,InsertFontText
573+
574+The set of functions that simplify appending and replacing of styled text. The specified style of the text is applied on the insertion. The common rule for all functions, if InsPos (insertion position) is negative - the text is appended to the end. In order to insert the text in the beginning of the test InsPos should set to 0.
575+
576+The inserted text is not forced to be on a new line or add a new line. You might want to add LineFeed character to TestUTF8 parameter, if you want the inserted text to cause a new line to be started.
577+
578+```delphi
579+procedure InsertStyledText(
580+ const ARichMemo: TCustomRichMemo;
581+ const TextUTF8: String;
582+ AStyle: TFontStyles;
583+ InsPos : Integer = -1 )
584+```
585+
586+InsertStyledText inserts the styled text at the specified position.
587+
588+```delphi
589+procedure InsertColorStyledText(
590+ const ARichMemo: TCustomRichMemo;
591+ const TextUTF8: String;
592+ AColor: TColor;
593+ AStyle: TFontStyles;
594+ InsPos : Integer = -1 )
595+```
596+
597+InsertColorStyledText inserts the text with specified style and color at the specified position. to the end of the RichMemo
598+
599+```delphi
600+procedure InsertFontText(
601+ const ARichMemo: TCustomRichMemo;
602+ const TextUTF8: String;
603+ const prms: TFontParams;
604+ InsPos : Integer = -1 )
605+```
606+
607+InsertFontText inserts the text and applies specified FontParams to it.
608+
609+You might want to create a class helper to implement these functions as methods for RichMemo. Beware, if you're using Delphi-compatibility helper - you might right into a conflict.
610+
611+## Frequently Asked Questions
612+
613+### Using RichMemo in Shared Libraries
614+
615+[Issue \#17412](http://bugs.freepascal.org/view.php?id=17412) If you need to use the component in a shared library, you might need to add -fPIC key to the compiler option of "the package" and the "project".
616+
617+## (Delphi) RichEdit like interface
618+
619+[Issue \#14632](http://bugs.freepascal.org/view.php?id=14632) A typical problem is porting an existing code that's using RichEdit from Delphi. RichMemo interface doesn't match RichEdit in many ways. But there're two ways to handle that:
620+
621+- you can either create a sub-class from TCustomRichMemo (or RichMemo) and implement Delphi RichEdit methods;
622+- you can use RichMemoHelpers unit (fpc 2.6.0 or later required) and use methods provided by class Helpers that should; Currently SelAttributes and Paragraph properties are implemented.
623+
624+`uses ... RichMemo, RichMemoHelpers;`\
625+\
626+`TForm = class`\
627+`  RichMemo1 : TRichMemo;`\
628+`   `\
629+`  // SelAttributes property is not available in the base class`\
630+`  // but added by a helper defined at RichMemoHelpers unit`\
631+`RichMemo1.SelAttributes.Name := 'Courier New';`
632+
633+## Append mixed color text at the end of the RichMemo
634+
635+If you just need simple coloring then here is an example that will each time add a new line with random color (tested on Windows):
636+
637+```delphi
638+ procedure TForm1.Button1Click(Sender: TObject);
639+ var
640+ i, j: integer;
641+ str: string;
642+ begin
643+ with Richmemo1 do
644+ begin
645+ str := 'Newline text';
646+ i := Length(Lines.Text) - Lines.Count; // cr as #10#13 is counted only once so subtract it once
647+ j := Length(str) + 1; // +1 to make the cr the same format
648+ Lines.Add(str);
649+ SetRangeColor(i, j, Round(random * $FFFFFF));
650+ end;
651+ end;
652+```
653+
654+` `<span style="color:#0140FF">`Newline text`</span>\
655+` `<span style="color:#507FAF">`NewLine text`</span>\
656+` `<span style="color:#C04A90">`NewLine text`</span>
657+
658+Alternative example for simple coloring (tested on Windows):
659+
660+```delphi
661+ procedure TForm1.Button2Click(Sender: TObject);
662+ begin
663+ with RichMemo1 do
664+ begin
665+ Lines.Add('Line in red');
666+ SetRangeColor(Length(Lines.Text) - Length(Lines[Lines.Count - 1]) - Lines.Count - 1, Length(Lines[Lines.Count - 1]), clRed);
667+
668+ Lines.Add('Line in blue');
669+ SetRangeColor(Length(Lines.Text) - Length(Lines[Lines.Count - 1]) - Lines.Count - 1, Length(Lines[Lines.Count - 1]), clBlue);
670+
671+ Lines.Add('Normal line.');
672+ Lines.Add('Normal line.');
673+
674+ Lines.Add('Line in green ');
675+ SetRangeColor(Length(Lines.Text) - Length(Lines[Lines.Count - 1]) - Lines.Count - 1, Length(Lines[Lines.Count - 1]), clGreen);
676+ end;
677+ end;
678+```
679+
680+` `<span style="color:#FF0000">`Line in red`</span>\
681+` `<span style="color:#0000FF">`Line in blue`</span>\
682+` `<span style="color:#000000">`Normal line.`</span>\
683+` `<span style="color:#000000">`Normal line.`</span>\
684+` `<span style="color:#008000">`Line in green`</span>
685+
686+If you need mixed coloring then this is an example that will add a new line with several different colored words (tested on Windows):
687+
688+```delphi
689+procedure TForm1.Button3Click(Sender: TObject);
690+ procedure AddColorStr(s: string; const col: TColor = clBlack; const NewLine: boolean = true);
691+ begin
692+ with RichMemo1 do
693+ begin
694+ if NewLine then
695+ begin
696+ Lines.Add('');
697+ Lines.Delete(Lines.Count - 1); // avoid double line spacing
698+ end;
699+
700+ SelStart := Length(Text);
701+ SelText := s;
702+ SelLength := Length(s);
703+ SetRangeColor(SelStart, SelLength, col);
704+
705+ // deselect inserted string and position cursor at the end of the text
706+ SelStart := Length(Text);
707+ SelText := '';
708+ end;
709+ end;
710+ begin
711+ AddColorStr('Black, ');
712+ AddColorStr('Green, ', clGReen, false);
713+ AddColorStr('Blue, ', clBlue, false);
714+ AddColorStr('Red', clRed, false);
715+ end;
716+```
717+
718+` `<span style="color:#000000">`Black, `</span><span style="color:#008000">`Green, `</span><span style="color:#0000FF">`Blue, `</span><span style="color:#FF0000">`Red`</span>\
719+` `<span style="color:#000000">`Black, `</span><span style="color:#008000">`Green, `</span><span style="color:#0000FF">`Blue, `</span><span style="color:#FF0000">`Red`</span>\
720+` `<span style="color:#000000">`Black, `</span><span style="color:#008000">`Green, `</span><span style="color:#0000FF">`Blue, `</span><span style="color:#FF0000">`Red`</span>
721+
722+## Markup Language Parsing
723+
724+![mlparse example](/wiki-images/8/8c/Mlparse.png) The native rich-edit controls do not support a markup-language parsing.
725+
726+You'll need to write it yourself as shown in mlparse example
727+
728+## Internals
729+
730+### Platform Support Matrix
731+
732+Values in the matrix are not expected to be static (except for "carbon" column), each system is planned to support all features declared by RichMemo.
733+
734+Note that the table contains more features than published above. That means that the APIs are already available in RichMemo, but since they're unstable (on some platforms) it's considered "experimental" rather than promised.
735+
736+| Feature | Win32 | Gtk2 | Qt | Cocoa | Carbon |
737+|--------------------------------|-------|------------|-----|-------|-------------------|
738+| Font color and style selection | Yes | Yes | Yes | Yes | Yes |
739+| Font background color | Yes | Yes | Yes | Yes | No |
740+| Subscript,Superscript | Yes | Yes | No | No | No |
741+| GetStyleRange | Yes | Yes | No | Yes | Yes |
742+| Paragraph Alignment | Yes | Yes | Yes | Yes | Almost Impossible |
743+| Paragraph Metrics | Yes | Yes | No | Yes | Almost Impossible |
744+| Paragraph Tabs | Yes | Yes | No | Yes | No |
745+| Zoom | Yes | Incomplete | No | Yes | No |
746+| Printing | Yes | No | No | No | No |
747+| RTF Load/Save | OS | RichMemo | No | OS | OS |
748+| Insertables | Yes | Yes | No | No | No |
749+| Transparent | Yes | No | Yes | Yes | No |
750+
751+### Win32
752+
753+![RichMemo without Theme patch](/wiki-images/e/e9/Rm_win32_notheme.PNG) ![RichMemo with Theme](/wiki-images/1/19/Rm_win32_theme.PNG)
754+
755+- [RichEdit](https://msdn.microsoft.com/en-us/library/windows/desktop/bb787605%28v=vs.85%29.aspx) is used as system widgetset. The latest known .dll is loaded and initialized on start. Please note that RichEdit 1.0 would not support most of the features.
756+- The internal wrapper of RichEditManager is provided in order to be compatible with Win 9x. However, it was never tried or tested. It's also expected that TOM object could be wrapped as one of the RichEditManager implementation. However, having the "manager" in place could be removed completely.
757+- According to the internet, RichEdit control was not updated by Microsoft to support Theme drawing (since XP and up to Windows 8.1). Thus, RichEdit might always look like an old Win9x 3d-framed control. Based on the patch provided at the [issue](http://bugs.freepascal.org/view.php?id=25111%7Creported) a way to override [WM_NCPAINT](https://msdn.microsoft.com/en-us/library/windows/desktop/dd145212%28v=vs.85%29.aspx) method was introduced in r4153.
758+
759+Win32RichMemo provides a global variable NCPaint. It's a handler of non-client area drawing. By default, it attempts to draw themed border (see Win32RichMemo.ThemedNCPaint for implementation). It provides good results on Windows XP themes, but later themes (where animations are used), the results are not so great and should be updated.
760+
761+In order to let system do NCPaint only (i.e. LCL implementation is causing issues or new windows updated RichEdit to draw borders properly), you can change NCPaint value at runtime, resetting it to nil
762+
763+`uses`\
764+`  RichMemo, ..{$ifdef WINDOWS}Win32RichMemo{$endif}`\
765+\
766+`initialization`\
767+`  {$ifdef WINDOWS}`\
768+`  Win32RichMemo.NCPaint:=nil;`\
769+`  {$endif}`\
770+
771+You can also provide your own implementation of NCPaint. However, if you implement proper animated theme drawing, please provide patch.
772+
773+The behavior is Windows implementation specific and should not (and will not be) part of RichMemo interface.
774+
775+There's additional information about Windows RICHEDIT that can be found at:
776+
777+- MSDN blog by "Murray Sargent: Math in Office" (https://docs.microsoft.com/en-us/archive/blogs/murrays/).
778+
779+### Gtk2
780+
781+- [GtkTextView](https://developer.gnome.org/gtk2/stable/GtkTextView.html) is used as system widgetset
782+- Sub and Superscript are not supported natively, an extra code is implemented in gtk2richmemo in order to implement them.
783+- Bulletin and numbered lists are emulated.
784+
785+#### 2.1.0 (trunk/later)
786+
787+Gtk2 internal functions got changes. Specifically function GetWidgetInfo was modified, making RichMemo to fail on compilation.
788+
789+in order to adapt the change **RMLCLTRUNK** must be defined. It can be easily specified using **richmemopackage** compiler options.
790+
791+<figure>
792+<img src="rmlcltrunkdefine.png" title="rmlcltrunkdefine.png" />
793+<figcaption>rmlcltrunkdefine.png</figcaption>
794+</figure>
795+
796+### Gtk3
797+
798+The APIs between Gtk2 and [Gtk3](https://developer.gnome.org/gtk3/stable/GtkTextView.html) are not really different. The major difference is in painting. For Gtk3 Cairo canvas is heavily used. The only area it affects is "Internals". IF any one wants to contribute Gtk2 implementation - please create separate gtk3richmemoXXX units. No {\$ifdefs} should be created in gtk2 units.
799+
800+### Cocoa
801+
802+- [NSTextView](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSTextView_Class/index.html) is used for Cocoa as system widgetset
803+- Not every font-family provides *italic* font. Thus even if you pass fsItalic as part of Style for a font, it might be ignored.
804+
805+#### Lazarus 1.8 and earlier
806+
807+You need to add a define RMLCL18 for the package compilation.
808+
809+### Qt
810+
811+- [QTextEdit](http://doc.qt.io/qt-4.8/qtextedit.html) widget is used. Current C-mappings for Qt are missing a lot of RichText APIs, thus full implementation is currently impossible.
812+- The following classes need to be mapped to C-functions:
813+ - [QTextBlockFormatH](http://doc.qt.io/qt-4.8/qtextblockformat.html) needed for paragraphs alignments (such as paragraph indents, line spacing and tabs). QTextEdit only exposes paragraph alignment.
814+ - [QTextCharFormatH](http://doc.qt.io/qt-4.8/qtextcharformat.html) needed for additional character formatting (i.e. vertical alignment, links support). QTextEdit only exposes font styles.
815+
816+## See also
817+
818+- [[RichMemo/Features]] - Comments on some features in progress.
819+- [[RichMemo/WorkArounds]] - Some notes about features not yet complete.
820+- [[RichMemo/Defines]] - Additional package switches that allow to resolve compiling/runtime issues.
821+- [[RichMemo/Samples]] - More examples.
822+- [[RichMemo/FAQ]] - Frequently (Forum) Asked Questions
823+- [MyNotex](https://sites.google.com/site/mynotex/) - Application which uses modified by Massimo Nardello RichMemo package on Linux-Gtk2.
824+- [[TMemo]]
825+- [[KControls|KMemo control]]
826+
827+---
828+*From the Free Pascal wiki, page "RichMemo", as it stood on 2022-09-29 after a change by Skalogryz, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history