                               Using the Editor

                                  "An ass loaded with gold climbs to
                                   the top of the castle"
                                                  English Proverb

Introduction

          One of the most requested tools over the past few years has
     been a plug-in editor which you can easily incorporate into an
     application. Well here it is. Rather than implement a basic little
     line editor, we decide to endow Gold with a beefy "Arnold" editor
     that includes the following features:

          Automatic dynamic word wrap
          Clipboard support allowing for cut/copy and paste
          Full mouse support
          Support for optional headers and footers
          Hooks to allow customization


     Figure 15.1
     Edit Windows


Basic Principles

          Many of the principles used with the editing functions are
     identical with those used for listing and browsing data (discussed
     in the last chapter).

          Gold needs to know the main properties of the data and the
     characteristics of the window in which the data will be displayed.
     For example, before an edit window can be displayed, Gold needs to
     know some of the following information:

          What type of data is to be displayed, e.g. a single linked
               list, double linked list, array, etc.
          Where the data is stored, i.e. the name of the variable.
          The dimensions and style of the window.
          The window title.
          Any optional memo headers and footers.
          What colors are to be used.

          You define all these memo characteristics by creating a
     variable (of type MemoCfg, for memo configuration), initializing
     the variable, and then setting various components of the variable
     to define your specific needs.

          In other words, to display a memo in a window you need at
     least two variables: a list configuration variable, and a variable
     used to store the data.

          Having configured the MemoCfg variable and loaded the data the
     following function can be used to provide an edit window:

     RunMemo(var MemoDetails: MemoCfg; Tit:StrScreen);

     Displays the data identified in MemoDetails in an edit window.


Defining the Memo Configuration

          The key to displaying a memo window is to declare a variable
     of type MemoCfg, and then call Gold functions to set MemoCfg to
     meet your specific needs.

Initializing MemoCfg

          Always, always, always call InitMemoCfg to initialize the
     MemoCfg variable before calling the other memo procedures and
     functions.

     InitMemoCfg(var MemoDetails: MemoCfg);

     Initializes a memo configuration variable.

     Assigning the Data Source

          Having initialized the MemoCfg variable you then need to
     update the variable with information about the memo data source.
     Gold can display the contents of a SingleLL, DoubleLL, or a string
     array. Dependent upon your data source, you would call one of the
     following procedures to update the MemoCfg variable with the data
     source details:

     MemoAssignSLL(var MemoDetails: MemoCfg; var MemoSource: SingleLL);

          Sets the MemoCfg variable data source as a single linked list
     stored in the variable MemoSource.

     MemoAssignDLL(var MemoDetails: MemoCfg; var MemoSource: DoubleLL);

          Sets the MemoCfg variable data source as a double linked list
     stored in the variable MemoSource.

     MemoAssignArray(var MemoDetails: MemoCfg; var MemoSource;
                                        StrLen:Byte;ArrayElements:byte);

          Sets the MemoCfg variable data source as a string array stored
     in the variable MemoSource. The last two parameters define the
     length of each string in the array, and the number of elements in
     the array, respectively.

Setting Window Properties

          When the MemoCfg variable is initialized, the window
     properties (such as the window position and dimensions) are set to
     some standard defaults. If these defaults are not appropriate, you
     can customize them with the following two procedures

     MemoSetWin(var MemoDetails: MemoCfg; X1,Y1,X2,Y2:integer;
                                                         Style:byte);

          Defines the upper left and lower right corners of the window
     along with the window style. (Refer to chapter 4 for a discussion
     of window styles.)

     MemoSetGaps(var MemoDetails: MemoCfg;
                                LeftGap,RightGap,BotGap,TopGap: byte);

          This procedure allows you to set the gap or whitespace area
     around the memo body. The editable data does not need to fill the
     entire window.


          If you want to change the window defaults, update the
     following variables: MemoVars.X1, MemoVars.Y1, MemoVars.X2,
     MemoVars.Y2, and MemoVars.WiStyle.

Setting the Window Colors

          As usual, the default colors are defined in the TINT
     structure, and the following elements pertain to memo windows:

          MemoHi,
          MemoNorm,
          MemoBlock,
          MemoScrollbarHi,
          MemoScrollbarNorm,
          MemoBorder1,
          MemoBorder2,
          MemoBorderOff,
          MemoTitle,
          MemoHeaders,
          MemoIcons,

          You can modify the defaults (used by any new memo windows
     initialized after the modification) by using the standard
     GoldSetColor procedure.

          You can modify the colors of a specific memo window by using
     the following function:

     MemoSetColor(var MemoDetails: MemoCfg; A:TintElement;C:byte);

     Sets the color of one of the elements of a memo window.

          The following code is an extract from DEMMEM6.PAS which
     customizes the list display colors:

          procedure CustomizeColors;
          {}
          begin
             MemoSetColor(MemoSettings,MemoHi,
                          LightGrayonMagenta);
             MemoSetColor(MemoSettings,MemoBlock,YellowOnBlue);
             MemoSetColor(MemoSettings,MemoNorm,YellowOnMagenta);
             MemoSetColor(MemoSettings,MemoScrollbarHi,
                          WhiteOnMagenta);
             MemoSetColor(MemoSettings,MemoScrollbarNorm,
                          WhiteOnMagenta);
             MemoSetColor(MemoSettings,MemoBorder1,
                          YellowOnMagenta);
             MemoSetColor(MemoSettings,MemoBorder2,
                          YellowOnMagenta);
             MemoSetColor(MemoSettings,MemoBorderOff,
                          BlackOnMagenta);
             MemoSetColor(MemoSettings,MemoTitle,WhiteOnMagenta);
             MemoSetColor(MemoSettings,MemoHeaders,
                                         GreenOnMagenta);
             MemoSetColor(MemoSettings,MemoIcons,
                          LightCyanOnMagenta);
          end; { CustomizeColors }

Using Headers and Footers

          Memo windows support multi-line headers and footers. Out of
     the box, Gold limits the number of heading lines and footer lines
     to 4, but you can change the number by assigning a different value
     to the constants ListMaxHeaders and ListMaxFooters declared in
     GOLDLIST.

          To minimize memory usage, all headers and footers must be
     stored as string variables in your program. (Gold simply stores a
     pointer to the variable.)

          The following six procedures can be used to manage the headers
     and footers:

     MemoAssignHeader(var MemoDetails: MemoCfg; Line:byte; var
                                                       Heading:string);

          Sets the memo configuration variable to display a string as a
     header. A string variable must be passed -- a string literal will
     not be accepted. The line must be a value in the range 1 to
     ListMaxHeaders (which defaults to 4).

     MemoAssignFooter(var MemoDetails: MemoCfg; Line:byte; var
                                                   Footnote:string);

          Sets the memo configuration variable to display a string as a
     footer. A string variable must be passed -- a string literal will
     not be accepted. The line must be a value in the range 1 to
     ListMaxFooters (which defaults to 4).

     MemoRemoveHeader(var MemoDetails: MemoCfg; Line:byte);

          Removes a header line which was previously assigned with
     MemoAssignHeader.

     MemoRemoveFooter(var MemoDetails: MemoCfg; Line:byte);

          Removes a header line which was previously assigned with
     MemoAssignFooter.

     MemoScrollHeader(var MemoDetails: MemoCfg; On:boolean);

          Pass TRUE if the headings should be scrolled right and left
     when the user scrolls the memo body right and left, or FALSE if the
     header should remain fixed.

     MemoScrollFooter(var MemoDetails: MemoCfg; On:boolean);

          Pass TRUE if the headings should be scrolled right and left
     when the user scrolls the memo body right and left, or FALSE if the
     header should remain fixed

          You can automatically center a header or footer by prefixing
     the string with the ^ character.

          Refer to the demo file DEMMEM1.PAS for an example of a memo
     with a heading.

Controlling Word Wrap

          IMPORTANT NOTE: The GOLDFLAG.INC file includes a conditional
     compiler directive named WORDWRAP. This compiler directive must be
     enabled (i.e. there should be no spaces either side of the $ symbol
     in the {$DEFINE statement) if your program is to support word
     wrapping.

          Gold can be configured to edit with or without automatic word
     wrapping. When word wrapping is disabled, the editor functions like
     a normal text editor, e.g. just like DOS' EDIT program. By default,
     word wrapping is enabled, but the following procedure controls the
     word wrapping state:

     MemoSetWordWrap(var MemoDetails: MemoCfg; On:boolean);

     Pass TRUE to enable word wrapping and FALSE to disable it.

          Gold word wraps all text in the current paragraph. You
     indicate the end of a paragraph by inserting an end of paragraph
     character in the text stream. This character is defined in the
     GOLDMEMO unit by the variable MemoVars.EndofParaCode. The code will
     default to char(20) and is displayed as "". This same code will be
     inserted into the text during an edit session when the user presses
     the Enter key.

          Before the text is initially displayed at the beginning of a
     RunMemo session, Gold will automatically wrap the text. It is very
     important that the source text includes embedded end of paragraph
     codes where appropriate. Otherwise, Gold will assume the entire
     stream is one big happy paragraph!

          Listed below is an extract from DEMMEM1.PAS which shows the
     text stream being created. Notice the embedded end of paragraph
     codes inserted at strategic places in the text.:

          procedure BuildSLL;
          {}
          var I: integer;
          begin
             InitSLLStr(TextData);
             I := 0;
             SLLSetActiveList(TextData);
             inc(I,SLLAddStr('Turbo Pascal with Objects ...'));
             inc(I,SLLAddStr('programming system for DOS...'));
             inc(I,SLLAddStr('development.Features DOS...'));
             inc(I,SLLAddStr('application frameworks; wo...' ));
             inc(I,SLLAddStr('create DOS, Windows Dynamic ...'));
             inc(I,SLLAddStr('across platforms.Better ...'));
             inc(I,SLLAddStr('with the award-winning Tech....'));
             if I <> 0 then
             begin
                PromptOK(' ERROR ','Not enough memory !');
                halt;
             end;
          end; {BuildSLL}


          If you want to display an empty window with no initial text,
     you must (as a minimum) assign one end of paragraph marker to the
     text stream -- this is similar to many word processors where a new
     document always contains an end of document symbol.

          The following code is an extract from DEMMEM2.PAS which
     attempts to load the source text from a file. If the file load
     fails, a single end of paragraph marker is assigned to the text:

          procedure BuildSLL;
          {}
          begin
             InitSLLStr(TextData);
             SLLSetActiveList(TextData);
             if SLLLoadFromFile(TextFileName) <> 0 then {bad}
                if SLLAddStr(MemoVars.EndofParaCode) <> 0 then
                begin
                   PromptOK(' ERROR ','Not enough memory');
                   halt;
                end;
          end; {BuildSLL}

Using the Clipboard

          That's right, Gold edit session support an internal clipboard
     enabling cut and paste operations!

          IMPORTANT NOTE: The GOLDFLAG.INC file includes a conditional
     compiler directive named CUTANDPASTE. This compiler directive must
     be enabled (i.e. there should be no spaces either side of the $
     symbol in the {$DEFINE statement) if your program is to support cut
     and paste.

          Having said that there is support for cut and paste, there is
     not a lot left to say. Gold is initially configured to support
     standard key operations like Shift-Del and Ctrl-Ins. These cut and
     paste keystrokes are defined in the MemoVars global variable, and
     you may modify them to support custom keystrokes for copy and paste
     operations. List below is an extract from the initialization
     procedure which defines these keystrokes:

          procedure MemoDefaultSettings;
          {}
          begin
             with MemoVars do
             begin
                DeltoScrapKey := 263;   {Shift-Del}
                DelBlockKey := 339;     {Del}
                CopyToScrapkey := 260;  {Ctrl-Ins}
                InsfromScrapKey := 261; {Shift-Ins}
                MarkBlockStartKey := 321; {F7}
                MarkBlockEndKey := 322; {F8}
                HideBlockKey := 2;      {Ctrl-B}
             end;
          end;

Customizing General Edit Keystrokes

          In the last section you learned how to customize the
     keystrokes related to the cut and paste operations. List below are
     some other keystrokes which are defined in the global variable
     MemoVars. You may assign new values to any of these keystrokes.

          procedure MemoDefaultSettings;
          {}
          begin
             with MemoVars do
             begin
                WordRightKey := 372;    {Ctrl-Right}
                WordLeftKey := 371;     {Ctrl-Left}
                TopOfWindowKey := 375;  {Ctrl-Home}
                BotOfWindowKey := 373;  {Ctrl-End}
                TopofMemoKey := 388;    {Ctrl-PgUp}
                BotofMemoKey := 374;    {Ctrl-PgDn}
                EndEditKey := 324;      {F10}
                EscEditKey := 27;       {Esc}
             end;
          end;

Determining the Dirty State

          Each MemoCfg variable keeps track of whether the data has been
     edited. When the variable is initialized (using InitMemoCfg) the
     state is set to FALSE, i.e. the memo has not been edited. Gold will
     automatically set the dirty flag to true if the data is edited in
     any way.

          The following two procedures can be used to get and set the
     state of the dirty flag:

     MemoIsDirty(var MemoDetails: MemoCfg):boolean;

     Returns TRUE if the memo has been edited.

     MemoSetDirty(var MemoDetails: MemoCfg; On:boolean);

          Sets the dirty flag which indicates whether or not the data
     has been edited.

          The demo files DEMMEM2.PAS and DEMMEM5.PAS use these functions
     during file save operations.

Using Memo Hooks

          Gold provides two different hooks for customizing memo
     windows: the character hook and the hind hook.

The Character Hook

          A character hook is a procedure which is called every time a
     key is pressed or a mouse button is clicked while a memo window has
     focus. The hooked procedure is called before the key is processed
     by Gold. The hook is particularly useful for trapping special keys
     like F1 for help, or Ctrl-Y to delete a file, for example.

          All you have to do is create a procedure following some
     specific rules, and then call the MemoAssignCharHook procedure to
     instruct Gold to call your procedure every time a key is pressed.

          For a procedure to be eligible as a character hook it must
     adhere to the following rules:

          The procedure must be declared as a far procedure at the root
          level. Refer to the section Understanding Hooks in Chapter 3
          for further information.

          The procedure must be declared with four parameters. The first
          parameter is of type MemoCfgPtr, followed by one variable
          parameter of type word and two variable parameters of type
          byte. These parameters identify the MemoCfg variable used to
          display the memo along with the user input, i.e. the Key, X
          and Y.

     The following procedure declaration follows these rules:

          {$F+}
          procedure MyCharHook(MP: MemoCfgPtr; var Code:word;var
                                                          X,Y:byte);
          begin
             {some code}
          end; {MyCharHook}
          {$F-}

          The following procedure is then called to instruct Gold to
     call your procedure after each input:

     MemoAssignCharHook(var MemoDetails: MemoCfg; Proc:MemoCharHook);

          Instructs Gold to call the specified procedure before
     processing each user input to a memo window.

          If, subsequently, you want to remove the character hook,
     execute the following procedure:

     MemoRemoveCharHook(var MemoDetails: MemoCfg);

          Removes a list character which was previously assigned with
     MemoAssignCharHook.


          The demo file DEMMEM7.PAS illustrates a character hook
     providing Ctrl-Y line deletion support.

The Hind Hook

          A memo hook is similar to a character hook, except the hook is
     called after an input has been processed by Gold. If you want to
     write some additional information to the screen based on the
     cursor, you should take advantage of the hind hook.

          For a procedure to be eligible as a hind hook it must adhere
     to the following rules:

          The procedure must be declared as a far procedure at the root
          level. Refer to the section Understanding Hooks in Chapter 3
          for further information.

          The procedure must be declared with one parameter of type
          MemoCfgPtr.

     The following procedure declaration follows these rules:

          {$F+}
          procedure MyHineyHook(MemoDetailsPtr:MemoCfgPtr);
          begin
             {some code}
          end; {MyHineyHook}
          {$F-}

          The following procedure is then called to instruct Gold to
     call your procedure after each input:

     MemoAssignHindHook(var MemoDetails: MemoCfg; Proc:MemoHindHook));

          Instructs Gold to call the specified procedure after
     processing each user input to a list window, and once when the
     window is first displayed.

          The variable parameter which is passed to the procedure is
     actually a pointer to the MemoCfg variable which is being used by
     the memo. You can de-reference the pointer to access the variable,
     for example:

          Total := MemoDetails^.TotalNodes

          If, subsequently, you want to remove the hind hook, execute
     the following procedure:

     MemoRemoveHindHook(var MemoDetails:MemoCfg);

          Removes a memo hind which was previously assigned with
     MemoAssignHindHook.

          The demo file DEMMEM8.PAS shows a hind hook used to displayed
     the cursor position, and indicates when the file has been modified.

Launching Memos on the Desktop

          The desktop equivalent of RunMemo, is LaunchMemo which is
     described as follows:

     LaunchMemo(var MemoDetails: MemoCfg;Tit:StrScreen;
                                        CloseProc:MemoCloseProc): byte;

          Adds a memo window to the desktop. The function returns the
     number (or handle) of the newly created window.

          Like its Run counter part, the LaunchMemo function is passed a
     MemoCfg variable and a title. Additionally, LaunchMemo is passed a
     close function as a third parameter. This close function is called
     whenever the user tries to close the window on the desktop, and it
     provides you with an opportunity to do any housekeeping (such as
     disposing of linked lists) before the window is closed.

          For a function to be eligible as a memo close hook it must
     adhere to the following rules:

          The function must be declared as a far function at the root
          level. Refer to the section Understanding Hooks in Chapter 4
          for further information.

          The function must be declared with one parameter of type
          MemoCfgPtr, and one integer. The function must return a
          boolean value.

     The following procedure declaration follows these rules:

          {$F+}
          function GoodbyeList(var MCP: MemoCfgPtr; Handle:integer):
                                                               boolean;
          {}
          begin
             PopUpSetActive(ListMenu,201,true);
             SLLDestroy;
             GoodbyeList := true;
          end; { GoodbyeList }
          {$F-}

          If the function returns false, the window will not close. The
     first parameter passed to the function is a pointer to the MemoCfg
     variable used to display the list. You can de-reference this
     pointer to access the variable, for example:

          Total := MemoDetails^.ActiveNode


          The demo file DEMMEM5.PAS shows the LaunchMemo function in all
     its glory.

Saving Files in Hooks or on the Desktop

          IMPORTANT NOTE: Behind the scenes, a copy of the line
     containing the cursor is maintained by Gold. As edits are made to
     the memo, Gold simply updates the single line copy -- this offers
     improved performance. When the user moves lines, the copy is posted
     back to the original data and a copy of the new line is made. "Why
     do I care" I hear you ask. Read On

          If you plan to save the memo in a hook, or as part of the
     close function, you must instruct Gold to update the original data
     with the current line using the following function:

     MemoStoreActiveLine(var MemoDetails: MemoCfg);

          Instructs Gold to write back (to the original data source) all
     the edits performed on the active line, i.e. the line containing
     the cursor.

     This procedure is used in DEMMEM5.PAS.

Error Handling

          The RunMemo function has the potential to fail. For example,
     there may be insufficient memory to create the window. After
     calling RunMemo , always call the function, LastMemoError to see if
     the operation was successful.

     If LaunchMemo returns a zero, the window was not created.

