DESIGN NOTES

This file provides some design notes for the Rich Text Display & Editor package for FLTK.

If you have questions, please email Jeffrey Ellis at: jeffellis1@gmail.com. 

==========================================================================================

CLASS ORGANIZATION

Fl_RichTextDisplay and Fl_RichTextEditor are new text widget classes, NOT derived from 
the existing Fl_Text_Display or Fl_Text_Editor classes. Fl_RichTextDisplay is derived 
from Fl_Group, and Fl_RichTextEditor is derived from Fl_RichTextDisplay.

Fl_RichTextDisplay displays the text, provides methods for setting styles, colors, etc., 
and handlers for selecting and copying text. Fl_RichTextEditor adds handlers for editing 
the text, as well as undo capability. Some Fl_RichTextDisplay methods for setting styles
are overridden by Fl_RichTextEditor, so the style operations can be made undo-able.

I used a Character class to store each individual character in the text and its attributes
(style, font, size, color, etc.). Character is a doubly linked list with pointers to the 
previous and next Characters.

Fl_RichTextDisplay maintains a pointer to the first Character (mpFirstChar) and current 
insertion point Character (mpCurrentChar).

I used a Hyperlink class to store each url linked to. Individual Characters that are 
hyperlinked each have a pointer to the relevant Hyperlink object. Fl_RichTextDisplay 
maintains the list of all Hyperlinks.

Fl_RichTextEditor maintains a list of Undo objects to use in performing undo operations. 
Specific types of undo (UndoStyle, UndoDelete, UndoInsert, etc.) are derived from the base 
Undo class.

EditorContent is a class that can be used to package up all of the content in an editor
associated with a particular "document" including the current list of Characters, the
current selection range, hyperlinks, the undo stack, etc. -- all things related to
the document currently displayed. Fl_RichTextEditor provides methods to let you rapidly
swap "documents" being displayed by providing the corresponding EditorContent object.

The files demo.fl, demo.h, demo.cpp, and stockText.h are for the demo program, 
textdemo.exe.

==========================================================================================

TEXT CURSOR PLACEMENT

The mpCurrentChar member of Fl_RichTextDisplay represents the current insertion point.
Any text added by the user or via the AddText() method will be inserted immediately after
mpCurrentChar.

Following are notes to myself to keep straight how cursor placement should work as I
developed all this. I'm including here in case anyone else trying to understand the code
finds it useful.

CONVENTIONS ON CURSOR PLACEMENT

CURSOR AT END OF LINE:

If cursor is supposed to be at end of line, and it is a word-wrapped line (not a newline
terminated line), the cursor should show up AFTER the ending space. Any new text 
entered should immediately go to front of next line.
	
If cursor is supposed to be at end of line, and it is a newline-terminated line, the 
cursor should be BEFORE the newline and AFTER the character preceding the newline. Any 
new text inserted will be at the end of that line, before the newline.

CURSOR AT BEGINNING OF LINE:
   
If cursor is supposed to be at the beginning of a line, then it should appear at the 
beginning of the line. This means that the very next character after the cursor is 
actually the first character on that line, and the character that precedes the first 
character on the line should be the "Current" character (i.e., mpCurrentChar).
   
CONVENTIONS FOR CURRENT INSERTION CHARACTER:
   
   CONVENTION #0: 
   mpCurrentChar is the character RIGHT BEFORE the cursor.
   
   CONVENTION #1:
   If mpCurrentChar == NULL it means the cursor is AT THE VERY FRONT, BEFORE ALL TEXT.
  
   CONVENTION #2:
   If mpCurrentChar is a newline, the cursor should be drawn at the FRONT OF THE NEXT
   LINE.
   
   CONVENTION #3:
   If mpCurrentChar is the last non-newline character in a line (e.g., if 
   mpCurrentChar->mpNext is on the next line, or if mpCurrentChar->mpNext->mC == '\n'), 
   the cursor should be at the VERY END OF THAT LINE, but BEFORE ANY NEWLINE IF THERE 
   IS A NEWLINE ENDING THAT LINE.

   PROBLEM WITH THE ABOVE:
   Because of convention #3, when the user hits the HOME key, instead of appearing at
   the beginning of the line, the cursor will appear at the end of the preceding line.
   
   CONVENTION #5:
   Use a flag to indicate whether we are in HOME or END behavior mode. If in END 
   behavior mode, convention #4 works as described above. If in HOME mode, cursor 
   appears at beginning of line following mpCurrentChar rather than as described in 
   convention #4 above. (The flag is the mCursorMode member of Fl_RichTextDisplay,
   and is of enumeration type CursorMode { MODE_NONE, MODE_END, MODE_HOME }.
   
   PUT ANOTHER WAY * * * * * * * *
   
   CURSOR SHOULD APPEAR AT BEGINNING OF LINE WHEN ONE OF FOLLOWING IS TRUE:
   (1) mpCurrentChar is NULL
   (2) mpCurrentChar is newline
   (3) mpCurrentChar is last non-newline char in a wrapped line and mode is MODE_HOME
   
   CURSOR SHOULD APPEAR AT END OF LINE WHEN ONE OF FOLLOWING IS TRUE:
   (1) mpCurrentChar is last non-newline char in a wrapped line and mode is not 
       MODE_HOME (note that cursor will be before a newline if there is one, so 
       technically not at the *very* end)
       
==========================================================================================

UNDO CAPABILITY

Types of operations to be undo-able are:
   - text insertion (whether by keystroke, drag-and-drop, copy-paste, cut-paste, etc.)
   - text deletion (whether by backspace, delete, insertion over selected text, etc.)
   - text styling (bold, italic, underline, highlighting)
   - hyperlinking
   - font and size selection
   
Approach:
   - Base Undo class
   - Specific undo classes for different types of operations are derived from Undo (e.g.,
     UndoInsert, UndoDelete, UndoStyle, UndoHyperlink, etc.).
   - UndoInsert also contains an UndoDelete object, since you can insert text over top
     of (and thereby deleting) selected text. So the UndoInsert will undo the insertion
     and restore the deleted selected text.
   - UndoStack is a class that maintains a stack of Undo objects. Fl_RichTextEditor has
     an mUndoStack member that is the UndoStack. Fl_RichTextEditor provides a
     PerformUndo() method that pops the next Undo off the stack and invokes the Undo's
     PerformUndo() method.
   - Currently Fl_RichTextEditor has a hardwired stack size for the undo stack (it is
     the constant DefaultNumUndo, at the top of the Fl_RichTextEditor.cpp file).
   - I implemented the UndoInsert capability a single character at a time rather than 
     undo'ing all subsequent insertion at once (e.g., like MS-Word does). Mostly because
     it's easiest, and also because if I needed to check the top Undo on the stack to see
     if it's an UndoInsert (so I could add more inserted text to it), Liskov would hate me
     with the flames of a thousand white hot suns.
   
==========================================================================================

A FEW KNOWN BUGS/SHORTCOMINGS:

(1) If the first word on a line is edited, it might be made short enough that it should
    really wrap-back to the end of the preceding line. But currently not checking for this.
    
(2) When cursor mode (mCursorMode) is in MODE_END, you can't click at the beginning of a
    line to move the cursor there if it is a word-wrapped line. Works fine in MODE_HOME
    mode, though.
    
(3) When you right-arrow past the end of a word-wrapped line, cursor should skip over
    remaining whitespace and go to beginning of next line, but does not; it stays at 
    end of line when insertion point is at front of next line. (Editing works fine, it
    just does not draw the cursor at the beginning of the next line like it should.)
    
(4) Currently if you right-arrow past the very end of text, the cursor goes to the very
    beginning of text. Should really stay at the end of text.
    

