/*----------------------------------------------------------------------------*/
/*                                                                            */
/* Copyright (c) 2011-2014 Rexx Language Association. All rights reserved.    */
/*                                                                            */
/* This program and the accompanying materials are made available under       */
/* the terms of the Common Public License v1.0 which accompanies this         */
/* distribution. A copy is also available at the following address:           */
/* https://www.oorexx.org/license.html                                        */
/*                                                                            */
/* Redistribution and use in source and binary forms, with or                 */
/* without modification, are permitted provided that the following            */
/* conditions are met:                                                        */
/*                                                                            */
/* Redistributions of source code must retain the above copyright             */
/* notice, this list of conditions and the following disclaimer.              */
/* Redistributions in binary form must reproduce the above copyright          */
/* notice, this list of conditions and the following disclaimer in            */
/* the documentation and/or other materials provided with the distribution.   */
/*                                                                            */
/* Neither the name of Rexx Language Association nor the names                */
/* of its contributors may be used to endorse or promote products             */
/* derived from this software without specific prior written permission.      */
/*                                                                            */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS        */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT          */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS          */
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT   */
/* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,      */
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED   */
/* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,        */
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING    */
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS         */
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.               */
/*                                                                            */
/*----------------------------------------------------------------------------*/

/**
 *  An example program showing how to work with the mouse wheel.
 *
 *  The ooDialog programmer can capture mouse wheel event by using the
 *  connectEvent() method of the .Mouse class.  The event handler for the
 *  connection will be invoked each time the user turns the scroll wheel on the
 *  mouse.  The event handler can then use the arguments passed to it to do
 *  custom scrolling.
 *
 *  Read throught the code comments to understand how this works.
 *
 *  The dialog for this program contains a large multi-line edit control with a
 *  number of lines in it.  For each turn of the mouse scroll wheel, the text in
 *  the edit control is scrolled 1 line. If the shift key is held down while
 *  turning the mouse scroll wheel, the text is scrolled 3 lines instead of 1.
 *  If the control key is down, the text is scrolled 1 page and if both the
 *  shift and control keys are down, the text is scrolled 3 pages.
 *
 *  The Help button in the dialog displays text explaining the dialog.  Use the
 *  help button to see the expected behavior of the dialog and compare the
 *  behavior with the implementing code in this file.
 */

  -- First we locate the direcotry our source code is in.  This allows us to
  -- build complete path names to our resource files, which in turn allows this
  -- program to be run from anywhwere on the computer.  For instance the program
  -- file can be dragged and dropped on ooDialog.exe and it will run correctly.
  srcDir = locate()

  -- When programmers wish to use symbolic resource IDs in their programs, as
  -- this program does, it is much easier to use the global constant directory,
  -- (.constDir,) rather than the constDir attribute of a dialog.  Using the
  -- .constDir Only is by far the most efficient way to use symbolic IDs.
  --
  -- The use of the global constant directory is controlled by the .Application
  -- object.  The .Application object is also used to set other application-wide
  -- defaults or values.  In this program, we also want to make the default for
  -- automatic data detection to be off.  Both of these requirements can be done
  -- with one method call to the .Application object.  As a convenience, the
  -- setDefaults() method will also populate the .constDir with symbols.
  --
  -- In this invokcation, the application is set to use the .constDir only, "O",
  -- symbols are loaded into the .constDir from the file: ContextMenu.h, and the
  -- default for automatic data detection is set to off (.false.)
.application~setDefaults('O', srcDir'mouseWheel.h', .false)

dlg = .MouseDemo~new(srcDir"mouseWheel.rc", IDD_MOUSE_DLG)

if dlg~initCode <> 0 then do
  msg = "Error initializing the Mouse Wheel Demonstration dialog." || '0d0a0d0a'x || -
        "This example will have to quit."
  j = MessageDialog(msg, 0, "Application Error", "OK", "ERROR")
  return 99
end

dlg~execute("SHOWTOP")
return 0

::requires 'ooDialog.cls'

::class 'MouseDemo' subclass RcDialog

/** initDialog()
 *
 * The ooDialog framework automatically invokes the initDialog() method
 * immediately after the underlying Windows dialog is created.  This makes it
 * the correct place to do any initialization that requires the underlying
 * dialog and dialog controls to exist.
 *
 * This is a typical initDialog(), but take note of these points.  The mouse
 * wheel event notification is sent by the operating system to the window with
 * the current input focus.  If the window does not process the message, then
 * the OS sends the message to the parent window of that window.  This continues
 * on up the parent / child window chain until either a window does process the
 * notification or the top of the chain is reached.
 *
 * Multi-line edit controls *do* process the notification and normally handle
 * their own scrolling.  Because of this, we need to get two mouse objects.  One
 * for this dialog and one for the edit control.  We then connect the mouse
 * wheel event in both mouse objects to a method in this dialog.
 */
::method initDialog
  expose eData pbOk eCommand process

  -- Connect the help key event to our onHelp() method.
  self~connectHelp(onHelp)

  eData = self~newEdit(IDC_EDIT_DATA)

  -- Get a mouse object for the edit control and connect the mouse wheel event.
  -- There are several options of how to handle this.  We could, using options
  -- to connectEvent(), either have the edit control's mouse wheel events
  -- connect to the same method as we connect the the dialog's mouse wheel
  -- events to, or have them connect to a separate method.
  --
  -- In this case we choose the later, and specify the onEditMouseWheel()
  -- method.
  --
  -- Note that onEditMouseWheel() method will only be invoked when the
  -- IDC_EDIT_DATA edit control has the focus.
  mouse = .Mouse~new(eData)
  mouse~connectEvent('MOUSEWHEEL', onEditMouseWheel)

  -- Connect all mouse wheel events that reach the dialog to a method in this
  -- dialog.  The default method name is onMouseWheel, so we omit the method
  -- name arugment and name our method in this dialog: 'onMousWheel'.
  --
  -- Note that this onMouseWheel() method will never be invoked if the
  -- IDC_EDIT_DATA edit control has the focus.
  mouse = .Mouse~new(self)
  mouse~connectEvent('MOUSEWHEEL')

  -- Set up the radio buttons, connect a click event on any of them to our
  -- single onRbSelect() method.
  self~connectButtonEvent(IDC_RB_ANYWHERE        , 'CLICKED', onRbSelect)
  self~connectButtonEvent(IDC_RB_DIALOG          , 'CLICKED', onRbSelect)
  self~connectButtonEvent(IDC_RB_ON_EDIT_OK      , 'CLICKED', onRbSelect)
  self~connectButtonEvent(IDC_RB_FOCUSED_ANYWHERE, 'CLICKED', onRbSelect)
  self~connectButtonEvent(IDC_RB_FOCUSED_DIALOG  , 'CLICKED', onRbSelect)
  self~connectButtonEvent(IDC_RB_FOCUSED_EDIT    , 'CLICKED', onRbSelect)

  -- Set the initial checked state to the 'Mouse is anywhere' radio button.
  self~newRadioButton(IDC_RB_ANYWHERE)~check
  process = "ANYWHERE"

  -- Save a reference to the ok push button and the single-line edit control.
  pbOk = self~newPushButton(IDOK)
  eCommand = self~newEdit(IDC_EDIT_COMMAND)

  -- Fill the edit control with data so we can see the scrolling.
  text = ''
  do i = 1 to 300
    text ||= 'Line number' || '09'x || i || '09'x || 'Mouse Wheel Demonstation' || .endOfLine
  end
  eData~setText(text)


/** onMouseWheel()
 *
 * This is an event handler for the mouse wheel event.  Three arguments are
 * sent to the event handler by the ooDialog framework.
 *
 * @param  state  A list of keywords that indicate the keyboard and mouse button
 *                modifiers.  The list of possible words is exactly:
 *
 *                None Control lButton mButton rButton Shift xButton1 xButton2
 *
 *                The none keyword will always be by itself, otherwise, the list
 *                can consist of one or more of the keywords.
 *
 * @param  pos    A .Point object that represents the position of the mouse
 *                pointer on the screen.  This is in screen co-ordinates, in
 *                pixels.
 *
 * @param  mouse  The mouse object that connected this event.  For our purposes
 *                we don't need the mouse so we just ignore this argument.
 *
 * @param  delta  A whole number that represents the amount the scroll wheel was
 *                turned.  The number will always be a multiple of 120 because
 *                that is what the operating system sends.  If the number is
 *                positive, the user turned the wheel away from herself and
 *                indicates to scroll up.  When negative the user turned the
 *                wheel towards himself, and indicates to scroll down.
 *
 *                At this point in time (c. 2011) the OS sends the event
 *                notification for each notch of the scroll wheel and so delta
 *                will always be 120 or -120.
 *
 * Because of the 2 mouse~connectEvent() calls in initDialog() the method will
 * only be called when the dialog is the active window and the multi-line edit
 * control does *not* have the focus.  We simply delegate to the maybeScroll()
 * method, passing on 3 of the arguments and .false to indicate the edit control
 * does not have the focus.
 */
::method onMouseWheel unguarded
  use arg state, pos, mouse, delta

  return self~maybeScroll(delta, state, pos, .false)


/** onEditMouseWheel
 *
 * This is an event handler for the mouse wheel event.  The arguments are
 * explained in the comment header for onMouseWheel().
 *
 * Because of the 2 mouse~connectEvent() calls in initDialog() this method will
 * *only* be called when the edit control has the focus.
 *
 * We simply delegate to the maybeScroll() method, passing on the 3 arguments
 * and .true to indicate the edit control has the focus.
 */
::method onEditMouseWheel unguarded
  use arg state, pos, mouse, delta

  return self~maybeScroll(delta, state, pos, .true)


/** maybeScroll
 * Based on the arguments, we determine whether to scroll or not.
 *
 * In all cases .true is returned, which is eventually returned by the
 * event handler.  This indicates that the event message was processed.
 */
::method maybeScroll private
  expose process eData
  use strict arg delta, state, pos, focused

  -- Eliminate some easy stuff
  if process~abbrev("FOCUSED"), \ focused then return 0

  select
    when process~right(8) == "ANYWHERE" then return self~scroll(delta, state)
    when process~right(6) == "DIALOG" then return self~scrollOnDialog(delta, state, pos)
    when process == "ON_EDIT_OK" then return self~scrollOnControls(delta, state, pos)
    when process == "FOCUSED_EDIT", focused then return self~scrollOnEdit(delta, state, pos)
    otherwise do
      return .true
    end
  end
  -- End select


/** scrollOnDialog()
 *
 * Determines if the position of the cursor (pos) is over the dialog window.  If
 * it is, we scroll, otherwise we do not scroll.
 *
 * In all cases .true is returned, which is eventually returned by the
 * event handler.  This indicates that the event message was processed.
 */
::method scrollOnDialog private
  use strict arg delta, state, pos

  if pos~inRect(self~windowRect) then return self~scroll(delta, state)

  return .true

/** scrollOnEdit()
 *
 * Determines if the position of the cursor (pos) is over the multi-line edit
 * control.  If it is, we scroll, otherwise we do not scroll.
 *
 * In all cases .true is returned, which is eventually returned by the
 * event handler.  This indicates that the event message was processed.
 */
::method scrollOnEdit private
  expose eData
  use strict arg delta, state, pos

  if pos~inRect(eData~windowRect) then return self~scroll(delta, state)

  return .true


/** scrollOnControls()
 *
 * Determines if the position of the cursor (pos) is over one of the edit
 * controls, or over the Ok button.  If it is, we scroll, otherwise we do not
 * scroll.
 *
 * In all cases .true is returned, which is eventually returned by the
 * event handler.  This indicates that the event message was processed.
 */
::method scrollOnControls private
  expose eData pbOk eCommand
  use strict arg delta, state, pos

  if pos~inRect(eData~windowRect) | pos~inRect(eCommand~windowRect) | -
     pos~inRect(pbOk~windowRect) then do
       return self~scroll(delta, state)
  end

  return .true


/** scroll()
 *
 * This private method is where the scrolling is actually done.  It is called
 * by other methods that have determined the mouse is in the correct place for
 * scrolling to happen.  delta and state are the orignal arguments sent to the
 * mouse wheel event handler.  See onMouseWheel() and onEditMouseWheel() for
 * the explanation of the arguments.
 *
 * Based on the arguments, we have the edit control scroll the appropriate
 * amount.  In all cases we return .true which is eventually returned by the
 * event handler.  This indicates that the event message was processed.
 */
::method scroll private
  expose eData
  use strict arg delta, state

  if delta < 0 then do
    select
      when state == 'None' then eData~scrollCommand("DOWN")

      when state~wordPos('Control') <> 0, state~wordPos('Shift') <> 0 then do
        -- Scroll 3 pages down.
        eData~scrollCommand('PAGEDOWN', 3)
      end

      when state~wordPos('Control') <> 0 then do
        -- Scroll page down.
        eData~scrollCommand('PAGEDOWN')
      end

      when state~wordPos('Shift') <> 0 then do
        -- Scroll 3 lines down.
        eData~scrollCommand('DOWN', 3)
      end

      otherwise do
        -- Ignore all others and scroll 1 line.
        eData~scrollCommand("DOWN")
      end
    end
    -- End select
  end
  else do
    -- Same as above, but scroll up.
    select
      when state == 'None' then eData~scrollCommand("UP")

      when state~wordPos('Control') <> 0, state~wordPos('Shift') <> 0 then do
        -- Scroll 3 pages down.
        eData~scrollCommand('PAGEUP', 3)
      end

      when state~wordPos('Control') <> 0 then do
        -- Scroll page down.
        eData~scrollCommand('PAGEUP')
      end

      when state~wordPos('Shift') <> 0 then do
        -- Scroll 3 lines down.
        eData~scrollCommand('UP', 3)
      end

      otherwise do
        -- Ignore all others and scroll 1 line.
        eData~scrollCommand("UP")
      end
    end
    -- End select
  end

  return .true


/** onRbSelect()
 *
 * This is the event handler for a button click on one of the radio buttons.
 *
 * With auto radio buttons, (which the radio buttons in this program are,) when
 * a radio button is clicked, it is automatically set to the selected state.
 *
 * Each time a radio button becomes the selected button, we update the process
 * variable.  That way 'process' always reflects which radio button is currently
 * selected.
 *
 * The event handler is sent five arguments.  However the first two are left
 * over from the old IBM ooDialog implementation and we ignore them.  We only
 * need the third 'id' argument, so we ignore the last two args also.
 */
::method onRbSelect
  expose process
  use arg , , id

  select
    when id == .ConstDir[IDC_RB_ANYWHERE        ] then process = "ANYWHERE"
    when id == .ConstDir[IDC_RB_DIALOG          ] then process = "DIALOG"
    when id == .ConstDir[IDC_RB_ON_EDIT_OK      ] then process = "ON_EDIT_OK"
    when id == .ConstDir[IDC_RB_FOCUSED_ANYWHERE] then process = "FOCUSED_ANYWHERE"
    when id == .ConstDir[IDC_RB_FOCUSED_DIALOG  ] then process = "FOCUSED_DIALOG"
    when id == .ConstDir[IDC_RB_FOCUSED_EDIT    ] then process = "FOCUSED_EDIT"
    otherwise
      nop
  end
  -- End select


/** help()
 *
 * This is the event handler for the IDHELP command.  Any button or menu item
 * with the resource ID of IDHELP will generate the HELP command event.  The
 * ooDialog framework automatically connects the IDOK, IDCANCEL, and IDHELP
 * commands to the ok(), cancel(), and help() methods in the dialog object.
 *
 * The default help() method does nothing.  To do something the programmer over-
 * rides the method in her subclass.  Here we simply invoke the onHelp() method
 * to do the work.
 *
 * Do not confuse the HELP command event with the help key (F1) event.
 */
::method help
  self~onHelp

/** onHelp()
 *
 * This is the event handler for the help key (F1) event.  The event is
 * generated when the user presses the F1 key.
 *
 * The programmer connects this event to a method in the Rexx dialog by using
 * the connectHelp() method.  Here in this event handler, we show a modal dialog
 * with some help text.
 *
 * Do not confuse the help key (F1) event with the HELP command event.
 */
::method onHelp
  .MouseDemoHelp~new(.application~srcDir'mouseWheel.rc', IDD_HELP)~execute("SHOWTOP")


/** class::MouseDemoHelp
 *
 * This is a very simple dialog subclass whose purpose is to display the help
 * text for the Mouse Wheel Demonstration program.
 *
 * Note that we set up our own handlers for the mouse wheel event for the help
 * text simply to provide more example code.  There is really not much need to
 * do this.  The edit control by default handles the scrolling for the mouse
 * wheel itself.  There is not a compelling reason in this dialog to not simply
 * let the edit control handle the scrolling for us.  (Other than the reason
 * mentioned, to provide more code example.)
 */
::class 'MouseDemoHelp' subclass RcDialog

::method initDialog
  expose newFont e visibleLines

  -- Connect the mouse wheel event to a method in this dialog.  We don't specify
  -- a method name, we'll just accept the default name of onMouseWheel.
  .Mouse~new(self)~connectEvent('MOUSEWHEEL')

  e = self~newEdit(IDC_HELP_TEXT)

  -- In this case we have the edit control not process the mouse wheel, but
  -- rather pass the message on to the dialog.  The fourth optional parameter of
  -- SENDTODLG is a keyword that causes the mouse wheel notfication to be
  -- passed on up the parent / child window chain and not sent to the edit
  -- control.
  --
  -- The effect of this is that the edit control never even sees the
  -- notification, allowing our onMouseWheel() method to handle all mouse wheel
  -- notifications, not matter where the mouse is over our dialog.
  .Mouse~new(e)~connectEvent('MOUSEWHEEL', 'NOOP', , "SENDTODLG")

  -- Create a mono-spaced font for the edit control that displays the help text.
  newFont = self~createFontEx('Courier New', 9)

  -- Set the font of the edit control to our custom font, set the text of the
  -- edit control to our help text.
  e~setFont(newFont)
  e~setText(getHelpText())

  -- visibleLines is the number of visible lines in the edit control.  It is
  -- only used if the user scrolls the help text using the mouse wheel.  So, we
  -- use lazy evaluation.  It is only calculated if needed.
  visibleLines = 0


/** onMouseWheel
 *
 * This is the event handler for the mouse wheel event.  We connected the event
 * to this method through the Mouse connectEvent() method in initDialog().
 *
 * The comment header for the onMouseWheel() event handler in the maid dialog
 * explains the arguments in detail.
 *
 * The system-wide wheelScrollLines parameter is used to indicate how much to
 * scroll for 1 notch of a turn of the mouse wheel.  The system default is 3,
 * but the user can set this to what they want.
 *
 * Microsoft says this about the wheel scroll lines parameter: it is the
 * suggested number of lines to scroll when there are no modifier keys; if the
 * value is 0, no scrolling should be done; if the value is greater than the
 * number of visible lines, or its value is WHEEL_PAGESCROLL, it should be
 * interpreted as clicking once in the page down or page up area of the scroll
 * bar.
 */
::method onMouseWheel unguarded
  expose e visibleLines
  use arg state, pos, mouse, delta

  if visibleLines == 0 then visibleLines = self~calcVisibleLines(e)

  if delta > 0 then direction = 'up'
  else direction = 'down'

  scrollLines = .SPI~wheelScrollLines

  -- scrollLines is only meant to indicate the number of lines to scroll when
  -- there are no modifier keys or mouse buttons.
  if state \== 'None' then do
    select
      when state~wordPos("Shift") <> 0, state~wordPos('Control') <> 0 then do
        if direction == 'up' then e~scrollCommand('PAGEUP', 3)
        else e~scrollCommand('PAGEDOWN', 3)
        return .true
      end

      when state~wordPos('Shift') then do
        if direction == 'up' then e~scrollCommand('UP', 3)
        else e~scrollCommand('DOWN', 3)
        return .true
      end

      when state~wordPos('Control') then do
        if direction == 'up' then e~scrollCommand('PAGEUP')
        else e~scrollCommand('PAGEDOWN')
        return .true
      end

      otherwise do
        -- Some other modifier, key or mouse, is active.  There are a lot of
        -- things we could do, but for simplicity we are going to just ignore
        -- this.
        return .false
      end
    end
    -- End select
  end

  -- No modifiers, we use the system scroll lines value to decide how much to
  -- scroll.
  select
    when scrollLines == 0 then do
      -- Indicates to not scroll
    end

    when scrollLines == .SPI~WHEEL_PAGESCROLL then do
      -- This setting indicates to scroll 1 page.
      if direction == 'up' then e~scrollCommand('PAGEUP')
      else e~scrollCommand('PAGEDOWN')
    end

    when scrollLines > visibleLines then do
      -- Scroll 1 page.
      if direction == 'up' then e~scrollCommand('PAGEUP')
      else e~scrollCommand('PAGEDOWN')
    end

    otherwise do
      -- Scroll the number of lines specified by  wheel scroll lines parameter.
      if direction == 'up' then e~scrollCommand('UP', scrollLines)
      else e~scrollCommand('DOWN', scrollLines)
    end
  end
  -- End select

  return .true


/** caclVisibleLines()
 *
 * Calculates the number of visible lines in the edit control.  This is done
 * by gettting the size of the edit control formatting rectangle in pixels and
 * the size of a string in the edit controls in pixels.  The height of the
 * formatting rectangle is then divided by the height of a line of text.
 *
 * When getting the height of a line in the edit control, any string can be used
 * because we only need the height of the string.  The length does not matter.
 *
 * On my first try at this, I used the client rect of the edit control for the
 * calculation.  But, it was off by 1.  The proper way to calculate the height
 * is to use the formatting rectangle.  The edit control does all its drawing
 * within that rectangle.
 */
::method calcVisibleLines private
  use strict arg editCtrl

  fRect = editCtrl~getRect

  height     = fRect~bottom - fRect~top
  lineHeight = editCtrl~getTextSizePX("Any string will do for this")~height

  -- Note that we return a whole number here, not a fraction.  The number is
  -- exactly the number of *whole* lines in the edit control.  Quite often in a
  -- dialog, a multi-line edit control will contain a partial line, (as does
  -- this dialog.)
  return height % lineHeight

/** leaving()
 *
 * The leaving method is invoked automatically by the ooDialog framework
 * immediately before the dialog is destroyed.
 *
 * At this point, the underlying Windows dialog still exists.  This makes the
 * leaving method the proper place to do clean up.  Especially if some of the
 * clean up, as does deleteFont(), requires the underlying dialog to exist.
 *
 * Here we delete the mono-spaced font created for the help text display.
 */
::method leaving
  expose newFont

  self~deleteFont(newFont)


/** routine::getHelpText()
 *
 * This is convenience routine that returns the help text for the Mouse Wheel
 * demonstration program.  The help text is constructed here as a single string.
 *
 * The text could be contained in a file and read in when needed.  To reduce the
 * nubmer of files needed for the program, the text was just typed into the
 * program file.
 */
::routine getHelpText

  txt = -
''                                                                              || .endOfLine || -
''                                                                              || .endOfLine || -
'                      The Mouse Wheel Demonstration'                           || .endOfLine || -
'                      ============================='                           || .endOfLine || -
''                                                                              || .endOfLine || -
'   This demonstration program allows you to scroll the lines of text in the'   || .endOfLine || -
'   main edit control using the mouse wheel.'                                   || .endOfLine || -
''                                                                              || .endOfLine || -
''                                                                              || .endOfLine || -
'   Scrolling'                                                                  || .endOfLine || -
'   ========='                                                                  || .endOfLine || -
''                                                                              || .endOfLine || -
'   When the mouse wheel is rotated one notch, the text is scrolled one line.'  || .endOfLine || -
'   Rotating the mouse wheel away from yourself scrolls the text up, while'     || .endOfLine || -
'   rotating the wheel towards yourself scrolls the text down.  When the text'  || .endOfLine || -
'   is scrolled to line 1, scrolling up stops.  Likewise when the lines of'     || .endOfLine || -
'   text are scrolled all the way to the bottom, scrolling down stops.'         || .endOfLine || -
''                                                                              || .endOfLine || -
'   Holding down the control and or the shift keys while rotating the mouse'    || .endOfLine || -
'   wheel changes the amount of text scrolled by rotating one notch of the'     || .endOfLine || -
'   mouse wheel.  This is summarized in the following table:'                   || .endOfLine || -
''                                                                              || .endOfLine || -
'   Notches       Key        Lines'                                             || .endOfLine || -
'   Rotated       Down       Scrolled'                                          || .endOfLine || -
'   ======================================='                                    || .endOfLine || -
'     1           None          1'                                              || .endOfLine || -
'   ---------------------------------------'                                    || .endOfLine || -
'     1          Shift          3'                                              || .endOfLine || -
'   ---------------------------------------'                                    || .endOfLine || -
'     1          Control      1 page'                                           || .endOfLine || -
'   ---------------------------------------'                                    || .endOfLine || -
'     1          Control'                                                       || .endOfLine || -
'                   &         3 pages'                                          || .endOfLine || -
'                Shift'                                                         || .endOfLine || -
'   ---------------------------------------'                                    || .endOfLine || -
''                                                                              || .endOfLine || -
''                                                                              || .endOfLine || -
'   Radio Buttons'                                                              || .endOfLine || -
'   ============='                                                              || .endOfLine || -
''                                                                              || .endOfLine || -
'   The radio buttons at the bottom of the dialog change how the position of'   || .endOfLine || -
'   the mouse pointer affects whether the text is scrolled, or not.'            || .endOfLine || -
''                                                                              || .endOfLine || -
'   Text is never scrolled unless the dialog has the input focus.  This'        || .endOfLine || -
'   essentially means when the dialog is the active window.'                    || .endOfLine || -
''                                                                              || .endOfLine || -
'   The following table assigns a number to each radio button according to'     || .endOfLine || -
'   the text of the radio button.  The next table uses the radio button'        || .endOfLine || -
'   number to describe its function.'                                           || .endOfLine || -
''                                                                              || .endOfLine || -
'   Radio     Radio'                                                            || .endOfLine || -
'   Button    Button'                                                           || .endOfLine || -
'     #        Text'                                                            || .endOfLine || -
'   ========================================================================='  || .endOfLine || -
'     1       Mouse is anywhere'                                                || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
'     2       Mouse is over dialog'                                             || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
'     3       Mouse over edit controls / Ok button'                             || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
'     4       Multi-line edit focused / mouse anywhere'                         || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
'     5       Multi-line edit focused / mouse over dialog'                      || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
'     6       Multi-line edit focused / mouse over edit control'                || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
''                                                                              || .endOfLine || -
''                                                                              || .endOfLine || -
'   Radio     Text is'                                                          || .endOfLine || -
'   Button    scrolled'                                                         || .endOfLine || -
'     #       when:'                                                            || .endOfLine || -
'   ========================================================================='  || .endOfLine || -
'     1       The mouse pointer is anywhere on the screen.  It does not have'   || .endOfLine || -
'             to be over the dialog at all.'                                    || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
'     2       Only when the mouse pointer is over dialog.  If the pointer is'   || .endOfLine || -
'             moved outside of the dialog, the scrolling stops.'                || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
'     3       Only when the mouse pointer is directly over one of the two'      || .endOfLine || -
'             edit controls, or directly over the Ok button.'                   || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
'     4       First the multi-line edit control must have the focus.  If any'   || .endOfLine || -
'             other control has the focus, text is not scrolled.  The mouse'    || .endOfLine || -
'             pointer can be anywhere on the screen.'                           || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
'     5       The multi-line edit control must have the focus.  The mouse'      || .endOfLine || -
'             pointer must be directly over the dialog.'                        || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
'     6       The multi-line edit control must have the focus.  The mouse'      || .endOfLine || -
'             pointer must be directly over the multi-line edit control.'       || .endOfLine || -
'   -------------------------------------------------------------------------'  || .endOfLine || -
''                                                                              || .endOfLine || -
''                                                                              || .endOfLine || -
'   Edit Controls'                                                              || .endOfLine || -
'   ============='                                                              || .endOfLine || -
''                                                                              || .endOfLine || -
'   Large, top, edit control:'                                                  || .endOfLine || -
'   -------------------------'                                                  || .endOfLine || -
'   This contains the text that is scrolled.  You can type into this control'   || .endOfLine || -
'   to add or remove lines.'                                                    || .endOfLine || -
''                                                                              || .endOfLine || -
'   Small, bottom, edit control labeled Command:'                               || .endOfLine || -
'   --------------------------------------------'                               || .endOfLine || -
'   This control serves no purpose, other than to take up real estate in the'   || .endOfLine || -
'   dialog.  You can typ in this control, but it does nothing.'                 || .endOfLine || -
''                                                                              || .endOfLine || -
''                                                                              || .endOfLine || -
'   Push Buttons'                                                               || .endOfLine || -
'   ============'                                                               || .endOfLine || -
''                                                                              || .endOfLine || -
'   Ok button    ->  Closes the dialog'                                         || .endOfLine || -
''                                                                              || .endOfLine || -
'   Help button  ->  Shows this help.'                                          || .endOfLine

  return txt
