|
1 /* |
|
2 * Copyright (c) 1997-1999 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #if !defined(__EIKRTED_H__) |
|
20 #define __EIKRTED_H__ |
|
21 |
|
22 #if !defined(__EIKGTED_H__) |
|
23 #include <eikgted.h> |
|
24 #endif |
|
25 |
|
26 #if !defined(__APPARC_H__) |
|
27 #include <apparc.h> |
|
28 #endif |
|
29 |
|
30 #if !defined(__TXTMRTSR_H__) |
|
31 #include <txtmrtsr.h> |
|
32 #endif |
|
33 |
|
34 #if !defined(__GDI_H__) |
|
35 #include <gdi.h> |
|
36 #endif |
|
37 |
|
38 class CRichText; |
|
39 class TResourceReader; |
|
40 class TPictureHeader; |
|
41 class CEikRubberBand; |
|
42 class CBufStore; |
|
43 class CStreamStore; |
|
44 class CApaDoor; |
|
45 class CEikParserManager; |
|
46 class MEikRichTextEditorParserObserver; |
|
47 class MParser; |
|
48 |
|
49 /** |
|
50 * Rich text editor. |
|
51 * |
|
52 * This is an edit window that supports rich text, including embedded objects |
|
53 * represented either by icons or glass doors. |
|
54 * |
|
55 * @since Symbian 5.0 |
|
56 */ |
|
57 class CEikRichTextEditor : public CEikGlobalTextEditor, |
|
58 public MApaEmbeddedDocObserver, |
|
59 public MRichTextStoreResolver, |
|
60 public MPictureFactory |
|
61 { |
|
62 |
|
63 public: |
|
64 |
|
65 /** |
|
66 * Identifies how an embedded object should be represented. |
|
67 * |
|
68 * One of these values is specified when the object is inserted. |
|
69 */ |
|
70 enum TObjectFormat |
|
71 { |
|
72 /** The object is always represented by an icon */ |
|
73 EAlwaysIconic, |
|
74 |
|
75 /** |
|
76 * The object is represented by a glass door, if possible, or by icon, |
|
77 * if not. |
|
78 */ |
|
79 EGlassIfPossible |
|
80 }; |
|
81 |
|
82 /** |
|
83 * Edit window attribute flags specific to the rich text editor. |
|
84 * |
|
85 * These may be specified during construction in addition to the values |
|
86 * contained in the @c TFlags enum in class @c CEikEdwin. |
|
87 */ |
|
88 enum // user flag |
|
89 { |
|
90 /** |
|
91 * All embedded objects are represented by icon rather than |
|
92 * glass doors. |
|
93 */ |
|
94 EShowAllPicturesAsIconic =0x00100000, |
|
95 |
|
96 /** |
|
97 * The editor has no text parsers. |
|
98 * |
|
99 * Text parsers are used to recognise and tag special text strings, |
|
100 * e.g. URLs. |
|
101 */ |
|
102 ENoTextParsers =0x00200000, |
|
103 |
|
104 /** |
|
105 * When pasting text into the editor, the text is |
|
106 * stripped of all formatting. |
|
107 * |
|
108 * @since 3.2 |
|
109 */ |
|
110 EPasteAsPlainText =0x00400000 |
|
111 }; |
|
112 |
|
113 public: |
|
114 |
|
115 /** |
|
116 * C++ default constructor. |
|
117 */ |
|
118 IMPORT_C CEikRichTextEditor(); |
|
119 |
|
120 /** |
|
121 * C++ constructor. |
|
122 * |
|
123 * @param aBorder Border for the rich text editor. |
|
124 */ |
|
125 IMPORT_C CEikRichTextEditor(const TGulBorder& aBorder); |
|
126 |
|
127 /** |
|
128 * Destructor. |
|
129 */ |
|
130 IMPORT_C ~CEikRichTextEditor(); |
|
131 |
|
132 /** |
|
133 * By default Symbian 2nd phase constructor is private. |
|
134 * |
|
135 * Completes construction of the rich text editor. |
|
136 * |
|
137 * The editor's paragraph and character formatting are set to default |
|
138 * values, unless the @c CEikEdwin::EUserSuppliedText flag is specified |
|
139 * in @c aEdwinFlags. |
|
140 * |
|
141 * @param aParent If not NULL, the editor's parent control. |
|
142 * If NULL, the editor has no parent. |
|
143 * @param aNumberOfLines The number of lines visible in the editor. |
|
144 * This controls the editor's height. |
|
145 * @param aTextLimit The maximum number of characters that can be entered |
|
146 * into the editor. |
|
147 * @param aEdwinFlags Edit window attribute flags. |
|
148 * See @c CEikEdwin::TFlags(). |
|
149 * @param aFontControlFlags = EGulFontControlAll Flags that specify which |
|
150 * font-related controls should not appear in font dialogs launched |
|
151 * from the edit window. For instance @c EGulFontControlBold removes |
|
152 * the bold checkbox control. The default shows all. For possible |
|
153 * values, see @c gulftflg.hrh. |
|
154 * @param aFontNameFlags = EGulNoSymbolFonts The font flags. These control |
|
155 * whether symbol and monospace fonts should be displayed in font |
|
156 * dialogs launched from the edit window. For possible values, see |
|
157 * @c gulftflg.hrh. |
|
158 */ |
|
159 IMPORT_C void ConstructL(const CCoeControl* aParent, |
|
160 TInt aNumberOfLines, |
|
161 TInt aTextLimit, |
|
162 TInt aEdwinFlags, |
|
163 TInt aFontControlFlags=EGulFontControlAll, |
|
164 TInt aFontNameFlags=EGulNoSymbolFonts); |
|
165 |
|
166 /** |
|
167 * Gets a pointer to the rich text object owned by the editor. |
|
168 * |
|
169 * @return Pointer to the rich text object. |
|
170 */ |
|
171 IMPORT_C CRichText* RichText() const; |
|
172 |
|
173 /** |
|
174 * Launches an insert object dialog (@c CEikInsertObjectDialog), |
|
175 * and inserts a default document of the application type selected |
|
176 * by the user. |
|
177 * |
|
178 * The object can be displayed either as a glass door, if supported, |
|
179 * or as an icon, and the inserted object is opened for editing. |
|
180 * |
|
181 * Displays an info message and leaves if the editor's text limit |
|
182 * has been reached. |
|
183 * |
|
184 * @param aFormat Specifies whether the embedded document should be |
|
185 * displayed as an icon or as a glass door. |
|
186 */ |
|
187 IMPORT_C void InsertObjectL(TObjectFormat aFormat); |
|
188 |
|
189 /** |
|
190 * Launches an insert object dialog (@c CEikInsertObjectDialog), |
|
191 * and inserts a default document of the application type selected |
|
192 * by the user. |
|
193 * |
|
194 * The object is displayed as a glass door rather than as an icon, |
|
195 * if supported and the inserted object is opened for editing. |
|
196 * |
|
197 * Displays an info message and leaves if the editor's text limit |
|
198 * has been reached. |
|
199 * |
|
200 * Default is @c EGlassIfPossible. |
|
201 */ |
|
202 IMPORT_C void InsertObjectL(); // defaults to EGlassIfPossible |
|
203 |
|
204 /** |
|
205 * Creates and inserts a default document of the specified |
|
206 * application type. |
|
207 * |
|
208 * @c CApaProcess::AddNewDocumentL() is used to create the document. |
|
209 * |
|
210 * The object can be displayed either as a glass door, if supported, |
|
211 * or as an icon, and the inserted object is opened for editing. |
|
212 * |
|
213 * Displays an info message and leaves if no suitable application DLL |
|
214 * can be found, or if the editor's text limit has been reached. |
|
215 * |
|
216 * @since Symbian 7.0 |
|
217 * @param aAppDllName Filename of the application DLL. |
|
218 * @param aAppDllUid UID of the application. The default is @c KNullUid. |
|
219 * @param aFormat Specifies whether the embedded document should be |
|
220 * displayed as an icon or as a glass door. |
|
221 */ |
|
222 IMPORT_C void InsertObjectL(const TDesC& aAppDllName, |
|
223 TUid aAppDllUid, |
|
224 TObjectFormat aFormat); |
|
225 |
|
226 /** |
|
227 * Creates and inserts a new embedded object of the specified type. |
|
228 * |
|
229 * First, an attempt is made to find an extended picture factory that |
|
230 * supports the insertion of pictures of the specified type. If one is |
|
231 * not found, the function leaves; if one is found, the picture is inserted |
|
232 * at the cursor position. |
|
233 * |
|
234 * Displays an info message and leaves if the editor's text limit has |
|
235 * been reached. |
|
236 * |
|
237 * @since Symbian 6.1 |
|
238 * @param aPictureType The picture type. |
|
239 * @param aData The base address of the data. |
|
240 * @leave KErrNotSupported No picture factory which supports the specified |
|
241 * type is available in the control's @c Uikon |
|
242 * environment. |
|
243 */ |
|
244 IMPORT_C void InsertObjectL(TUid aPictureType, |
|
245 CBase* aData); |
|
246 |
|
247 /** |
|
248 * Re-edits the embedded object at the cursor position. |
|
249 * |
|
250 * If there is no embedded object at the cursor position, or if there is a |
|
251 * selection, an info message is displayed. |
|
252 * |
|
253 * If there is a valid object at the cursor position, it is opened for |
|
254 * editing (or for viewing if the editor is read-only). |
|
255 */ |
|
256 IMPORT_C void ReEditObjectL(); |
|
257 |
|
258 /** |
|
259 * Gets the document position and checks whether there is an embedded |
|
260 * object at the cursor position. |
|
261 * |
|
262 * If there is no embedded object at the cursor position, or if there |
|
263 * is a selection, an info message is displayed. |
|
264 * |
|
265 * @return The document position of the embedded object, or |
|
266 * @c KErrNotFound if there is no embedded object at the cursor |
|
267 * position, or if there is a selection |
|
268 */ |
|
269 IMPORT_C TInt ObjectCursorPos() const; |
|
270 |
|
271 /** |
|
272 * Tests whether there is an embedded object at the cursor position. |
|
273 * |
|
274 * If there is one, it is opened for editing (or for viewing if the editor |
|
275 * is read-only). |
|
276 * |
|
277 * @return @c ETrue if there is an embedded object at the cursor |
|
278 * position and it could be opened. @c EFalse if there is no |
|
279 * embedded object at the cursor position, or if the object has |
|
280 * a NULL UID. |
|
281 */ |
|
282 IMPORT_C TBool CheckForObjectL(); |
|
283 |
|
284 /** |
|
285 * Launches a format object dialog (@c CEikFormatObjectDialog) if there |
|
286 * is an embedded object at the cursor position, and the object supports |
|
287 * being displayed as a glass door. |
|
288 * |
|
289 * If the object does not support being displayed as a glass door, an |
|
290 * object information dialog (@c CEikObjectInfoDialog) is launched instead. |
|
291 * |
|
292 * If the embedded object's associated application cannot be found, an |
|
293 * info message is displayed and the function leaves. |
|
294 * |
|
295 * The function has no effect if there is no embedded object at the cursor |
|
296 * position. |
|
297 */ |
|
298 IMPORT_C void EditPictureFormatL(); |
|
299 |
|
300 /** |
|
301 * Handles a change to the format of an embedded object, by updating the |
|
302 * view, the scroll bars and reporting the event to its observers. |
|
303 * |
|
304 * There is no need to call this function after calling |
|
305 * @c EditPictureFormatL(). |
|
306 */ |
|
307 IMPORT_C void PictureFormatChangedL(); |
|
308 |
|
309 /** |
|
310 * Gets a pointer to the embedded object located at the specified position. |
|
311 * |
|
312 * If the object is not in memory, the function loads it. |
|
313 * |
|
314 * If the object's associated application cannot be found, an info message |
|
315 * is displayed and the function leaves. |
|
316 * |
|
317 * @param aDoor On return, the embedded document's wrapper object (icon or |
|
318 * glass door). |
|
319 * @param aDoc On return, the embedded document. |
|
320 * @param aDocPos The document position in the editor at which the embedded |
|
321 * object is located. |
|
322 */ |
|
323 IMPORT_C void GetEmbeddedAppL(CApaDoor*& aDoor, |
|
324 CApaDocument*& aDoc, |
|
325 TInt aDocPos); |
|
326 |
|
327 /** |
|
328 * Changes all embedded objects displayed as glass doors into temporarily |
|
329 * iconic. |
|
330 * |
|
331 * The function operates throughout the editor. |
|
332 * |
|
333 * Only needed when pictures are temporarily iconic. |
|
334 * |
|
335 * Has no effect if there are no embedded objects in the editor or if the |
|
336 * @c EShowAllPicturesAsIconic attribute flag was set during construction. |
|
337 */ |
|
338 IMPORT_C void UpdatePictureFormatL(); |
|
339 |
|
340 /** |
|
341 * Changes all embedded objects displayed as glass doors into temporarily |
|
342 * iconic. |
|
343 * |
|
344 * The function operates over a specified range of characters. |
|
345 * |
|
346 * Has no effect if there are no embedded objects in the editor or if the |
|
347 * @c EShowAllPicturesAsIconic attribute flag was set during construction. |
|
348 * |
|
349 * Only needed when pictures are temporarily iconic. |
|
350 * |
|
351 * @param aStartPos The start position. |
|
352 * @param aLength The number of characters, beginning at @c aStartPos over |
|
353 * which the function operates. |
|
354 */ |
|
355 IMPORT_C void UpdatePictureFormatL(TInt aStartPos,TInt aLength); |
|
356 |
|
357 /** |
|
358 * Changes the size of the icons used to represent embedded objects. |
|
359 * |
|
360 * Any existing iconic doors can be updated to the new size by calling |
|
361 * @c UpdatePictureSizeL(). |
|
362 * |
|
363 * @param aSize The new iconic door size in twips. |
|
364 */ |
|
365 IMPORT_C void SetDefaultIconicDoorSize(const TSize& aSize); |
|
366 |
|
367 /** |
|
368 * Gets the size of iconic doors. |
|
369 * |
|
370 * @return The size of iconic doors. |
|
371 */ |
|
372 IMPORT_C const TSize& DefaultIconicDoorSize() const; |
|
373 |
|
374 /** |
|
375 * Changes the size of all icons representing embedded objects to the |
|
376 * default iconic door size. |
|
377 * |
|
378 * Also updates any objects currently displayed as glass doors, so that |
|
379 * if displayed as icons, they will use the correct size. |
|
380 * |
|
381 * The function operates throughout the editor. |
|
382 */ |
|
383 IMPORT_C void UpdatePictureSizeL(); |
|
384 |
|
385 /** |
|
386 * Changes the size of all icons representing embedded objects to the |
|
387 * default iconic door size. |
|
388 * |
|
389 * Also updates any objects currently displayed as glass doors, so that |
|
390 * if displayed as icons, they will use the correct size. |
|
391 * |
|
392 * The function operates over a specified range of characters. |
|
393 * |
|
394 * @param aStartPos The start position. |
|
395 * @param aLength The number of characters, beginning at @c aStartPos over |
|
396 * which the function operates. |
|
397 */ |
|
398 IMPORT_C void UpdatePictureSizeL(TInt aStartPos,TInt aLength); |
|
399 |
|
400 /** |
|
401 * Sets a parser observer. |
|
402 * |
|
403 * If the @c CEikEdwin::ENoTextParsers attribute flag was specified on |
|
404 * construction, this function has no effect. |
|
405 * |
|
406 * Its @c HandleCursorOverParserL() function is called when the cursor is |
|
407 * positioned over text that has been tagged by the parser, for instance |
|
408 * a URL. |
|
409 * |
|
410 * @param aObserver The parser observer. |
|
411 */ |
|
412 IMPORT_C void SetParserObserver( |
|
413 MEikRichTextEditorParserObserver* aObserver); |
|
414 |
|
415 /** |
|
416 * Activate/Disable phone number grouping. |
|
417 * |
|
418 * @param aEnable @c ETrue if phone number grouping is to be activated, |
|
419 * @c EFalse otherwise. |
|
420 */ |
|
421 IMPORT_C void SetPhoneNumberGrouping( TBool aEnable ); |
|
422 |
|
423 public: // from CCoeControl |
|
424 |
|
425 /** |
|
426 * From @c CCoeControl. |
|
427 * |
|
428 * Handles key events. |
|
429 * |
|
430 * Has no effect (apart from returning @c EKeyWasConsumed) if the |
|
431 * @c CEikEdwin::EDisplayOnly attribute flag was specified on construction. |
|
432 * |
|
433 * Handles rich text-specific hot keys, for instance to insert an object; |
|
434 * otherwise calls @c CEikGlobalTextEditor::OfferKeyEventL(). |
|
435 * |
|
436 * @param aKeyEvent The key event. |
|
437 * @param aType The type of the event. The editor only consumes events of |
|
438 * type @c EEventKey. |
|
439 * @return Indicates whether or not the editor consumed the |
|
440 * key event. |
|
441 */ |
|
442 IMPORT_C TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType); |
|
443 |
|
444 /** |
|
445 * From @c CCoeControl. |
|
446 * |
|
447 * Handles pointer events inside the editor. |
|
448 * |
|
449 * Has no effect if the @c CEikEdwin::EDisplayOnly attribute flag was |
|
450 * specified on construction. |
|
451 * |
|
452 * @param aPointerEvent The pointer event to be handled. |
|
453 */ |
|
454 IMPORT_C void HandlePointerEventL(const TPointerEvent& aPointerEvent); |
|
455 |
|
456 /** |
|
457 * From @c CCoeControl. |
|
458 * |
|
459 * Completes the construction of the rich text editor from a resource file. |
|
460 * |
|
461 * The editor's paragraph and character formatting are set to default |
|
462 * values, unless the @c CEikEdwin::EUserSuppliedText flag is specified in |
|
463 * the resource. |
|
464 * |
|
465 * @param aReader A resource reader positioned for reading from an |
|
466 * RTXTED resource. |
|
467 */ |
|
468 IMPORT_C void ConstructFromResourceL(TResourceReader& aReader); |
|
469 |
|
470 /** |
|
471 * From @c CCoeControl. |
|
472 * |
|
473 * Activates the editor so that it is ready for use. |
|
474 * |
|
475 * For instance, the text view is created, the editor is set to observe its |
|
476 * rich text object, the editor's parser manager is set up, which handles |
|
477 * the changes that occur when the cursor is moved over tagged text |
|
478 * (for instance a URL), and all embedded objects are set to be displayed |
|
479 * as icons, of the default size. |
|
480 */ |
|
481 IMPORT_C void ActivateL(); |
|
482 |
|
483 private: |
|
484 |
|
485 /** |
|
486 * From CAknControl. |
|
487 */ |
|
488 IMPORT_C void* ExtensionInterface( TUid aInterface ); |
|
489 |
|
490 public: // from CEikEdwin |
|
491 |
|
492 /** |
|
493 * From @c CEikEdwin. |
|
494 * |
|
495 * Copies the contents of one text object into another. |
|
496 * |
|
497 * @param[in] aInText The rich text object to copy. |
|
498 * @param[out] aOutText On return, contains a copy of @c aInText. |
|
499 * @panic 26 In debug mode, if either @c aInText or @c aOutText is @c NULL. |
|
500 */ |
|
501 IMPORT_C void CopyDocumentContentL(CGlobalText& aInText,CGlobalText& aOutText); |
|
502 |
|
503 protected: |
|
504 |
|
505 /** |
|
506 * Internal flags used for indicating operations. |
|
507 */ |
|
508 enum // internal flags |
|
509 { |
|
510 /** Crop from left. */ |
|
511 ECropFromLeft =0x00010000, |
|
512 |
|
513 /** Crop from right. */ |
|
514 ECropFromRight =0x00020000, |
|
515 |
|
516 /** Crop from top. */ |
|
517 ECropFromTop =0x00040000, |
|
518 |
|
519 /** Crop from bottom. */ |
|
520 ECropFromBottom =0x00080000, |
|
521 |
|
522 /** Object is being re-edited. */ |
|
523 EReEditingObject =0x00100000 |
|
524 }; |
|
525 |
|
526 protected: // from CCoeControl |
|
527 |
|
528 /** |
|
529 * From @c CCoeControl. |
|
530 * |
|
531 * Writes the internal state to the specified stream. |
|
532 * |
|
533 * @param aWriteStream Target stream. |
|
534 */ |
|
535 IMPORT_C void WriteInternalStateL(RWriteStream& aWriteStream) const; |
|
536 |
|
537 protected: // from MEditObserver |
|
538 |
|
539 /** |
|
540 * From @c MEditObserver. |
|
541 * |
|
542 * This member is internal an not meant to be used. |
|
543 * |
|
544 * @param aStartEdit Start position for editing. |
|
545 * @param aEditLength The length of the edited object. |
|
546 */ |
|
547 IMPORT_C void EditObserver(TInt aStartEdit,TInt aEditLength); |
|
548 |
|
549 private: // from CoeControl |
|
550 |
|
551 IMPORT_C void Draw(const TRect& aRect) const; |
|
552 |
|
553 IMPORT_C void Reserved_2(); |
|
554 |
|
555 private: // from CEikEdwin |
|
556 |
|
557 IMPORT_C void HandleTextPastedL(TInt aStartPos,TInt& aLength); |
|
558 IMPORT_C void Reserved_3(); |
|
559 |
|
560 private: // from MApaEmbeddedDocObserver |
|
561 |
|
562 IMPORT_C void NotifyExit(TExitMode aMode); |
|
563 |
|
564 private: // from MRichTextStoreResolver |
|
565 |
|
566 IMPORT_C const CStreamStore& StreamStoreL(TInt aPos) const; |
|
567 |
|
568 private: // from MPictureFactory |
|
569 |
|
570 IMPORT_C void NewPictureL(TPictureHeader& aHdr,const CStreamStore& aDeferredPictureStore) const; |
|
571 |
|
572 private: |
|
573 |
|
574 void CommonConstructL(); |
|
575 |
|
576 static TInt InsertEmbeddedDocL(TAny *aThis); |
|
577 |
|
578 static TInt DeleteEmbeddedDoc(TAny *aThis); |
|
579 |
|
580 static TInt UpdateEmbeddedDocL(TAny* aThis); |
|
581 |
|
582 static TInt TryDeleteEmbeddedDocL(TAny *aThis); |
|
583 |
|
584 void InsertPictureL(const TPictureHeader& aPictureHeader); |
|
585 |
|
586 void DoInsertPictureL(TBool& aFormatHasChanged,const TPictureHeader& aPictureHeader); |
|
587 |
|
588 void DoReEditObjectL(TInt aDocPos); |
|
589 |
|
590 void RoomForObjectL(); |
|
591 |
|
592 void InsertObjectL(CApaDocument* aDoc,TObjectFormat aFormat); |
|
593 |
|
594 void SetTextObserver(CRichText& aText); |
|
595 |
|
596 inline void DoReportEdwinEventL(MEikEdwinObserver::TEdwinEvent aEventType); |
|
597 |
|
598 protected: |
|
599 |
|
600 /** |
|
601 * Default size of iconic door. |
|
602 */ |
|
603 TSize iDefaultIconicDoorSize; |
|
604 |
|
605 private: |
|
606 |
|
607 TPictureHeader iEmbeddedDoc; |
|
608 |
|
609 CIdle* iEmbeddedDocUpdate; |
|
610 |
|
611 CBufStore* iBufStore; |
|
612 |
|
613 CEikParserManager* iParserManager; |
|
614 |
|
615 private: |
|
616 |
|
617 friend class CEikParserManager; |
|
618 |
|
619 public: // new methods |
|
620 |
|
621 /** |
|
622 * Force everything to be parsed. |
|
623 */ |
|
624 IMPORT_C void RefreshParsersL(); |
|
625 |
|
626 }; |
|
627 |
|
628 class MEikRichTextEditorParserObserver |
|
629 { |
|
630 public: |
|
631 virtual void HandleCursorOverParserL(const TDesC& aDoItText)=0; |
|
632 }; |
|
633 |
|
634 inline void CEikRichTextEditor::DoReportEdwinEventL(MEikEdwinObserver::TEdwinEvent aEventType) |
|
635 {ReportEdwinEventL(aEventType);} |
|
636 |
|
637 #endif // __EIKRTED_H__ |
|
638 |
|
639 // End of file |