/*----------------------------------------------------------------------------*/
/*                                                                            */
/* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved.             */
/* Copyright (c) 2005-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.               */
/*                                                                            */
/*----------------------------------------------------------------------------*/

/**
 *   Name: usewmgr.rex
 *   Type: Example ooRexx program
 *
 *   Description:  Demonstrates the use of the WindowsManager, the WindowObject,
 *                 and the WindowsClipboard classes.  Has some examples of
 *                 ooDialog usage.
 *
 *                 This example uses some of the public functions provided by
 *                 the windowsSystem.frm package. That framework contains
 *                 functions shared with other example programs shipped with
 *                 ooRexx that use winsystm.cls.
 *
 *   Note:         Because of language dependencies this example will not run
 *                 correctly on all language versions of Windows.  It assumes
 *                 the window titles are in English.  To run on a different
 *                 language version, change the window titles for the Calculator
 *                 and Notepad applications to the correct title for
 *                 the specific language version of Windows.
 */

-- First, give the user a clue as to what is happening.
msg = "This ooRexx example program demonstrates controlling a"     || .endOfLine           || -
      "Windows application using the winsystm.cls classes."        || .endOfLine~copies(2) || -
      "Information will be displayed using message boxes like"     || .endOfLine           || -
      "this one.  Push the ok button in them to continue the"      || .endOfLine           || -
      "demonstration."                                             || .endOfLine~copies(2) || -
      "You will see the Calculator and Notepad programs appear"    || .endOfLine           || -
      "on your screen, one at a time.  There are some short"       || .endOfLine           || -
      "pauses between actions.  Be patient during them."           || .endOfLine~copies(2) || -
      "On the other hand, if the pauses are too short for you,"    || .endOfLine           || -
      "you can increase the pauses by editing the code.  This"     || .endOfLine           || -
      "will provide a good excuse to explore the code, which"      || .endOfLine           || -
      "is an excellent way to learn.  Simply search for 'call"     || .endOfLine           || -
      "SysSleep' in the code and increase the sleep amount for"    || .endOfLine           || -
      "any pause that is is too short."                            || .endOfLine~copies(2) || -
      "In particular watch the Calculator program to see the"      || .endOfLine           || -
      "changes as it is remotely controlled.  The applications"    || .endOfLine           || -
      "will be closed automatically"

j = infoDialog(msg)

-- A simple demonstration of the Windows Manager.  Get an instance of the
-- WindowsManager class.  Then, display the title and coordinates of the current
-- foreground window.
winMgr = .WindowsManager~new

fg = winMgr~ForegroundWindow
if fg \= .nil then do
  msg = "Current foreground window" || '0d0a0d0a'x || -
        "  Window title:   " fg~Title || '0d0a'x || -
        "  Window location:" fg~Coordinates
  j = infoDialog(msg)
end
else do
  msg = "The WindowsManager object failed to find the foreground window."
  j = errorDialog(msg)
end

-- A more involved demonstration using the Windows Calculator application.  The
-- real work is done in the workWithCalc() routine.
calcPrg = getPathToSystemExe("calc.exe")
if calcPrg \== .nil then do
  ret = workWithCalc(calcPrg)
end
else do
  msg = "Faild to locate a definitive path to the Windows" || '0d0a'x || -
        "Calculator application.  Going to skip this part" || '0d0a'x || -
        "of the demonstration."
  j = errorDialog(msg)
end

-- An example similar to the Windows Calculator, but using Windows Notepad.
notepad = getPathToSystemExe("notepad.exe")
if notepad \== .nil then do
  ret = workWithNotepad(notepad)
end
else do
  msg = "Faild to locate a definitive path to the Windows" || '0d0a'x || -
        "Notepad application.  Going to skip this part of" || '0d0a'x || -
        "the demonstration."
  j = errorDialog(msg)
end

return 0


/* windowsSystem.frm contains public helper routines and also requires
 * winsystm.cls and oodplain.cls.  Requiring windowsSystem.frm gives us access
 * to the class definitions for WindowsManager, WindowsClipboard, and
 * WindowObject. oodplain.cls has the class definitions for the message dialogs.
 */
::requires "windowsSystem.frm"

::routine workWithCalc
  use strict arg calcPrg

  -- Start the Calculator program.
  "start" calcPrg

  -- Find the Calculator Window, if we don't find it, then end this portion of
  -- the demonstration.
  calc = findTheWindow("Calculator")
  if calc == .nil then return windowFailure("Calculator")

  -- Make the calculator the top-most window.
  calc~ToForeground
  call SysSleep 2

  -- Switch to the scientific view.
  calc~ProcessMenuCommand("&View","&Scientific")
  call SysSleep 2

  /* When the view of the Calculator is changed, it gets a new window handle.
   * Because of this, after each view change, the calculator must be found
   * again.  This is an oddity of the Calculator application.  Most applications
   * maintain the same window handle through out their execution.
   */
  calc = findTheWindow("Calculator")
  if calc == .nil then return windowFailure("Calculator")
  calc~ToForeground

  /* Each button in a dialog has a resource ID.  When the button is pushed or
   * clicked, the button sends a command (WM_COMMAND) notification to its
   * parent window.  The command notification contains the resource ID of the
   * button that was pushed.  This is how the dialog knows what button(s) the
   * user pushes or clicks.
   *
   * We can simulate a user pushing buttons by sending commands to the dialog
   * using the resource ID of the button we want pushed.  Here we define
   * variables with the IDs of some of the buttons in the calculator
   * application.  This makes the code easier to read.  You can verify that
   * these are the correct ID numbers by using the displayWindowTree.rex program
   * included with this distribution of ooRexx.
   */

  divide = 90
  multi  = 91
  plus   = 92
  minus  = 93
  point  = 85
  equals = 112
  one    = 125
  three  = 127
  five   = 129
  seven  = 131

  /* Create a clipboard object */
  cb = .WindowsClipboard~new

  /* Switch between Scientific and Standard view using the calculator's menu */
  calc = findTheWindow("Calculator")
  if calc == .nil then return windowFailure("Calculator")

  calc~ProcessMenuCommand("&View","S&tandard")
  call SysSleep 2

  calc = findTheWindow("Calculator")
  if calc == .nil then return windowFailure("Calculator")

  calc~ProcessMenuCommand("&View","&Scientific")
  call SysSleep 2

  calc = findTheWindow("Calculator")
  if calc == .nil then return windowFailure("Calculator")

  calc~Title = "Calculator - Open Object Rexx is in charge!"

  -- Do a few calculations and retrieve the results via the clipboard
  calc~sendCommand(seven)
  calc~sendCommand(plus)
  calc~sendCommand(five)
  calc~sendCommand(equals)

  -- Empty clipboard so that we can wait until data is available
  cb~empty

  /* Copy result to clipboard, wait until the data is available from the
   * clipboard and then use paste to display the result.
   */
  calc~processMenuCommand("&Edit", "&Copy")
  do while cb~IsDataAvailable = 0; nop; end
  msg = "Calculator says: 7+5="cb~Paste
  j = infoDialog(msg)

  call SysSleep 2
  calc~sendCommand(divide)
  calc~sendCommand(one)
  calc~sendCommand(Point)
  calc~sendCommand(five)
  calc~sendCommand(equals)
  cb~Empty

  calc~processMenuCommand("&Edit", "&Copy")
  do while cb~IsDataAvailable = 0; nop; end
  msg = "Calculator says: 7+5/1.5="cb~Paste
  j = infoDialog(msg)

  calc~sendCommand(multi)
  calc~sendCommand(three)
  calc~sendCommand(equals)
  cb~Empty

  calc~processMenuCommand("&Edit","&Copy")
  do while cb~IsDataAvailable = 0; nop; end
  msg = "Calculator says: 7+5/1.5*3="cb~Paste
  j = infoDialog(msg)

  call SysSleep 2

  -- Switch to hexadecimal mode.
  calc~pushButton("Hex")
  cb~empty
  calc~processMenuCommand("&Edit","&Copy")
  do while cb~isDataAvailable = 0; nop; end
  msg = "Calculator says: 7+5/1.5="cb~Paste " (HEX)"
  j = infoDialog(msg)

  call SysSleep 2

  -- Switch back to decimal mode.
  calc~pushButton("Dec")

  -- Copy a number to the clipboard and from there to the calculator to
  -- demonstrate another way to enter numbers.
  cb~copy("123456789")
  calc~processMenuCommand("&Edit", "&Paste")
  call SysSleep 2

  -- Simulate a left mouse button click on the "Grads" radio button.
  gradsRB = findDescendent(calc, "Grads")
  if gradsRB \== .nil then do
    gradsRB~sendMouseClick("LEFT", "DOWN", 2, 2)
    gradsRB~sendMouseClick("LEFT", "UP", 2, 2)
  end

  call SysSleep 2

  -- Close the Calculator application by pressing Close in the system menu.
  calc~sendSysCommand("Close")

return 0

::routine workWithNotepad
  use strict arg notePrg

  -- Start the Notepad program.
  "start" notePrg

  np = findTheWindow("Untitled - Notepad")
  if np == .nil then return windowFailure("Notepad")

  -- Move Notepad to foreground.
  np~toForeground

  -- The edit window is the first (and perhaps only) child window of Notepad.
  npe = np~firstChild

  -- The sendText() method sets the text of a window.
  npe~sendText("Hello,")

  -- The sendKey() method sends a key press to a window.  This can be used to
  -- send virtual key pressess to the window.
  npe~sendKey("RETURN")

  -- Because we are sending messages to an application running in a separate
  -- process, on today's fast, multi-threaded, multi-cored, systems, it is
  -- possible for the messages to arrive in the Notepad application in a
  -- different order than they are being sent here.  This was probably not
  -- possible on Windows 3.0 when this example was first developed.
  --
  -- To avoid this out of order messaging, we use a helper function from the
  -- windowsSystem.frm framework.
  j = sendTextWithPause(npe, "This is a sample that mainly demonstrates the use")
  j = sendKeyWithPause(npe, "RETURN")
  j = sendTextWithPause(npe, "of the WindowsManager class.")
  j = sendKeyWithPause(npe, "RETURN")
  j = sendKeyWithPause(npe, "RETURN")

  j = sendTextWithPause(npe, "It also introduces the WindowObject, the MenuObject,")
  j = sendKeyWithPause(npe, "RETURN")
  j = sendTextWithPause(npe, "and the WindowsClipboard classes.")
  j = sendKeyWithPause(npe, "RETURN")
  j = sendKeyWithPause(npe, "RETURN")

  npe~sendText("In 1 second intervals I'm going to minimize, maximize, restore,")
  j = sendKeyWithPause(npe, "RETURN")
  npe~sendText("disable, and reenable Notepad.")
  j = sendKeyWithPause(npe, "RETURN")

  call SysSleep 3
  np~minimize
  call SysSleep 1
  np~maximize
  call SysSleep 1
  np~restore
  call SysSleep 1

  -- Change the size of the Notepad window.
  np~resize(650, 280)

  -- Make the main window (including the menu) inaccessible.
  np~disable

  j = sendKeyWithPause(npe, "RETURN")
  j = sendKeyWithPause(npe, "RETURN")
  j = sendTextWithPause(npe, "Select a menu item. It won't be possible.")
  call SysSleep 5

  -- Re-enable the main window.
  np~enable

  j = sendKeyWithPause(npe, "RETURN")
  j = sendTextWithPause(npe, "Now the menu is enabled again.")
  call SysSleep 1

  j = sendKeyWithPause(npe, "RETURN")
  j = sendTextWithPause(npe, "You can do the same with the editor window.")

  -- We disable the edit window so it gets grayed.
  npe~disable
  call SysSleep 4

  npe~enable
  j = sendKeyWithPause(npe, "RETURN")
  npe~sendText("Now let's hide the editor window.")
  call SysSleep 2

  -- Let's hide the edit window (not the main window) for a moment.e */
  npe~hide
  np~restore
  call SysSleep 2

  npe~restore
  npe~sendKey("RETURN")
  npe~sendKey("RETURN")
  call SysSleep 3
  npe~sendKey("RETURN")
  npe~sendKey("RETURN")
  npe~sendText("The show is over. Good bye in 3 seconds!")

  -- Get a WindowsManager object, we'll use it below to cancel the Save dialog.
  winMgr = .WindowsManager~new

  -- On Vista, the interface to Notepad is slightly changed.
  if isVistaOrLater() then buttonName = "Do&n't Save"
  else buttonName = "&No"
  call SysSleep 2

  -- Close Notepad by pressing the Close menu item in the system menu.
  np~sendSysCommand("Close")

  -- Wait a second for the dialog that asks whether to save the changes.
  call SysSleep 1
  fg = winMgr~foregroundWindow
  if fg \== .nil, fg~title = "Notepad" then do
    call SysSleep .1

    -- Get the No or Don't Save button.  Then set the focus to the button and
    -- send the space key to it.  This simulates a user pushing the button and
    -- has the effect of closing Notepad automatically.
    button = findDescendent(fg, buttonName)
    if button \== .nil then do
      fg~focusItem(button)
      button~SendKey("SPACE")
    end
    else do
      msg = 'Could not locate the' buttonName~changestr('&', "") 'button used to ' || '0d0a'x || -
            'discard the text changes.  findDescendent() failed' || '0d0a0d0a'x || -
            'You will have to close the Notepad windows manually.'
      j = errorDialog(msg)
    end
  end
  else do
    msg = 'Failed to connect with the Save Dialog for Notepad' || '0d0a0d0a'x || -
          'You may have to close the Notepad windows manually.'
    j = errorDialog(msg)
  end

return 0

/** windowFailure()
 * A simple helper function to put up an error message box.
 */
::routine windowFailure
  use strict arg name

  msg = "Failed to locate the" name "application window." || '0d0a0d0a'x || -
        "Ending the" name "demonstration prematurely."
  j = errorDialog(msg)

return .false

