rexx-things/modules/windows/oodialog/winsystem/windowsSystem.frm
2025-03-12 20:50:48 +00:00

918 lines
30 KiB
Plaintext

/*----------------------------------------------------------------------------*/
/* */
/* Copyright (c) 2008-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: windowsSystem.frm
* Type: Framework
*
* Description: A collection of public routines and classes to help work with
* the winsystm.cls package.
*
* This is an example of how to extract common function into a
* package, and the use the package to help in writting similar
* programs. A number of the sample programs that use
* winsystm.cls make use of this framework.
*/
::requires 'winsystm.cls'
::requires "ooDialog.cls"
/** findTheWindow()
* Uses the WindowsManager to create a WindowObject representing a top-level
* window currently running on the system.
*
* When an application is started up, it takes some finite amount of time before
* the window for the application is created by the operating system. The
* WindowsManager can not 'find' the aplication window until it has been
* created. This function loops a number of times trying to find the window,
* then eventually gives up
*
* How long to wait for to find the window before giving up can be adjusted by
* the caller.
*
* @param title The title of the window being sought.
* @param loops Optional. The number of loops to perform looking for the
* window. The default is 20.
* @param pause Optional. The time to sleep during each loop. The default
* is .2 of a second.
*
* @return A WindowObject object representing the desired window on
* success. If the function fails, .nil is returned.
*
* @note To find a window, you must use its exact title.
*/
::routine findTheWindow public
use strict arg title, loops = 20, pause = .2
windowMgr = .WindowsManager~new
do loops
j = SysSleep(pause)
wnd = windowMgr~find(title)
if wnd~class == .WindowObject then leave
end
return wnd
/** sendTextWithPause()
* Sends text to a window and pauses slightly before returning.
*
* @param wnd A window object that represents the window to send the text to.
* @param text The text to send.
*
* @return The return from SysSleep()
*
* @note Not to go into too much detail about the underlying implmentation of
* winsystm.cls, but a brief explantion. When sending text or a key
* press to a window, a Windows API function is used that returns
* immediately, before the text or key press actually makes it to
* the OS window. In a fast dual-core / multi-core / multi-processor
* system this can result in the window receiving the key presses or text
* in a different order than they are sent by ooRexx.
*
* This function and the sendKeyWithPause() function prevent that out of
* ordering by pausing very slightly after doing the send. This actually
* mimics more closely a user entering the data on a keyboard. Anyone
* familiar with Expect, probably knows that Expect provides a similiar
* function.
*/
::routine sendTextWithPause public
use strict arg wnd, text
wnd~sendText(text)
return SysSleep(.01)
/** sendKeyWithPause()
* Sends a key press to a window and pauses slightly before returning.
*
* @param wnd A window object that represents the window to send the key
* press to.
* @param key The key press to send.
*
* @return The return from SysSleep()
*
* @see sendTextWithPause()
*/
::routine sendKeyWithPause public
use strict arg wnd, key
wnd~sendKey(key)
return SysSleep(.01)
/** printChildren()
* Given a window object, enumerates all descendent windows of that window and
* prints the results to the screen.
*
* @param wnd The parent window object.
* @param indent When printing to the console, the amount of indentation.
*
* @return True if this function recursed, otherwise false.
*/
::routine printChildren public
use strict arg wnd, indent
line = indent || wnd~hwnd ":" wnd~id ":" wnd~wclass ":" wnd~title~left(15) ":" wnd~coordinates
childs. = wnd~enumerateChildren
select
when childs.0 == 0 then do
line = line "no children"
say line
return .false
end
when childs.0 == 1 then do
line = line "1 child"
end
otherwise do
line = line childs.0 "children"
end
end
-- End select
say line
indent = indent || " "
do i = 1 to childs.0
childWnd = .WindowObject~new(childs.i.!Handle)
ret = printChildren(childWnd, indent)
end
return .true
/** findDescendent()
* Finds a descendent window with the window title specified, if any.
*
* @param wnd The parent window whose descendents are searched.
* @param title The title (label) of the window being searched for.
*
* @return A .WindowObject representing the window if found. If not found then
* .nil is returned.
*
* @note This function is very similar to .WindowObject~findChild(), except
* that this function will search recursively through the window
* hierarchy for any descendent window with the specified label. The
* WindowObject's findChild() on the other hand only looks at the
* immediate children.
*/
::routine findDescendent public
use strict arg wnd, title
descendent = wnd~findChild(title)
if descendent \== .nil then return descendent
children. = wnd~enumerateChildren
do i = 1 to children.0
childWnd = .WindowObject~new(children.i.!Handle)
descendent = findDescendent(childWnd, title)
if descendent \== .nil then return descendent
end
return .nil
/** isVistaOrLater()
* Simple convenience function to determine if the operating system is at least
* Windows Vista.
*
* @return True if the current OS is Vista or a later version of Windows,
* otherwise false.
*/
::routine isVistaOrLater public
parse value SysVersion() with name ver
return (ver >= 6)
/** getPathToSystemExe()
* Gets the complete path to an executable that is a standard application
* shipped with a Windows system. In a Windows distributions, these
* applications are in the system directory. Getting the complete path ensures
* that the correct application is started.
*
* For instance, there are a surprisingly large number of applications named
* 'calc.' If a user has set up her system with a 'calc' program in a directory
* ahead of the system directory in the path, then that calc program can end up
* being started rather than the expected Windows calculator application.
*
* @param prgName The program whose path is being sought. The .exe is
* expected.
*
* @return The fully qualified path name of the program, if it exists in the
* system directory. If not found, then .nil is returned.
*/
::routine getPathToSystemExe public
use strict arg prgName
shell = .oleObject~new('Shell.Application')
-- 0x25 is the CSIDL_SYSTEM constant. The CSIDL_XXX constants are used to
-- identify well known directories on Windows. Like the My Documents
-- directory, the All Users Start Menu, etc. We use this constant to get the
-- Windows System folder object, and from that the path to the directory.
csidl_system = '25'
folderObj = shell~nameSpace(csidl_system~x2d)
sysFolderPath = folderObj~self~path
if sysFolderPath~right(1) \== '\' then sysFolderPath = sysFolderPath'\'
prgPathName = sysFolderPath || prgName
if \ SysIsFile(prgPathName) then prgPathName = .nil
return prgPathName
/** getWindowTree()
* Constructs a data structure that represents the entire window hierarchy of
* a window.
*
* Each node in the structure represents a window. The node is a .directory
* object whose items contain the important attributes of the window. This
* directory object has an item: children, that is an array of nodes
* representing the children windows of the node.
*
* Each node has these indexes:
*
* node~handle
* node~title
* node~windowClass
* node~state
* node~id
* node~coordinates
* node~children
* node~hasChildren
* node~childrenCount
*
* @param wnd The window object to construct a node for.
*
* @return A tree of window nodes representing the window specified and all its
* children.
*
* @note This is a recursive function.
*/
::routine getWindowTree public
use strict arg wnd
tree = buildWindowDetails(wnd)
child = wnd~firstChild
if child == .nil then return tree
tree~children = .array~new
tree~hasChildren = .true
do while child \== .nil
tree~children~append(getWindowTree(child))
tree~childrenCount += 1
child = child~next
end
return tree
/** buildWindowDetails()
* Private helper function for getWindowTree().
*
* @param wnd The .WindowObject whose details are desired.
*
* @return A directory object whose items reflect a number of attributes of
* a window.
*/
::routine buildWindowDetails
use strict arg wnd
d = .directory~new
d~handle = wnd~handle
d~text = wnd~title
d~windowClass = wnd~wClass
d~state = wnd~state
d~id = wnd~id
d~coordinates = wnd~coordinates
d~children = .nil
d~hasChildren = .false
d~childrenCount = 0
return d
/** printWindowTree
* Given a tree of window nodes, prints out the tree to the console.
*
* @param tree The tree of window nodes to print.
* @param indent The current indentation. Note that this is a recursive
* function and the indent is increased with each recursion.
*
* @return True if the function has recursed, othewise false.
*
* @see getWindowTree()
* @see printChildren()
*
* @note The function produces the exact same output as printChildren(). Its
* main purpose was to debug getWindowTree(). However, it could serve
* as a useful template if one wanted to change how the information or
* what information is printed to the console.
*/
::routine printWindowTree public
use strict arg tree, indent
-- A little defensive programming.
if \ tree~isA(.directory) then return .false
line = indent || tree~handle ":" tree~id ":" tree~windowClass ":" tree~text~left(15) ":" tree~coordinates
select
when tree~childrenCount == 0 then do
line = line "no children"
say line
return .false
end
when tree~childrenCount == 1 then do
line = line "1 child"
end
otherwise do
line = line tree~childrenCount "children"
end
end
-- End select
say line
indent = indent || " "
do child over tree~children
ret = printWindowTree(child, indent)
end
return .true
/** showWindowTree()
* Puts up an ooDialog dialog that displays the window hierarchy of a window in
* a tree view control.
*
* @param tree A tree node data structure produced by the getWindowTree()
* function.
*
* @return True if the dialog was shown, false if it was not.
*
* @see getWindowTree()
*
* @note We use the locate() function to get the directory this source code is
* located in. And then, use that value to create a complete path name
* to our resource files. This ensures the .rc and .h files will always
* be found
*/
::routine showWindowTree public
use strict arg tree
sd = locate()
dlg = .WindowTreeDlg~new(sd"winSystemDlgs.rc", IDD_WINDOW_TREE, , sd"winSystemDlgs.h")
if dlg~initCode == 0 then do
dlg~useTree(tree)
dlg~execute("SHOWTOP")
return .true
end
return .false
/** class: WindowTreeDlg
*
* A simple ooDialog dialog class to display the window hierarchy, parent and
* descendent windows, of any window.
*
* The only caveat is that the class requires a window tree structure created by
* the getWindowTree() public routine in this framework.
*
* This is a subclass of RcDialog, meaning the dialog template is defined in a
* resource script file. The dialog template was created by a GUI dialog
* editor. The dialog template is stored in the winSystemDlgs.rc file and has
* a symbolic resource ID of IDD_WINDOW_TREE. The symbolic resource IDs are
* defined in the winSystemDlgs.h file.
*
* @note For an example of how to use this class see the showWindowTree()
* public routine in this framework.
*/
::class 'WindowTreeDlg' public subclass RcDialog
/** useTree()
* Sets the window tree structure for this dialog. The structure must be set
* prior to executing the dialog, otherwise the dialog will display an empty
* tree view control.
*
* @param windowTree A tree node structure in the same format as that produced
* by the getWindowTree() function.
@
@ @see getWindowTree()
*/
::method useTree
expose windowTree
use arg windowTree
/** initDialog()
* Initializes the dialog controls for this dialog. The only control is the
* Tree control.
*
* The ooDialog framework automatically inovkes this method for every dialog
* immediately after the underlying Windows dialog has been created. Since most
* of the initialization of dialog controls requires that the underlying control
* has been created, this makes initDialog() the proper place to do all the
* control initialization.
*/
::method initDialog
expose windowTree
if windowTree~isA(.directory), windowTree~hasIndex("WINDOWCLASS") then self~doInit(windowTree)
/** doInit()
* A private method that does the actuall work of initializing the tree control.
*
* @param windowTree The window tree node structure for this dialog.
*/
::method doInit private
use strict arg windowTree
-- Get the tree-view control object and then invoke the addNode() recursive
-- method to add all the items to the control.
tree = self~newTreeView(IDC_TREE_WINDOWS)
rootNode = self~addNode(tree, "Root", windowTree)
-- Set the title of this dialog, which will contain the window handle for the
-- window we represent.
title = "The" windowTree~text "(" || windowTree~handle || ") Window Hierarchy"
self~setTitle(title)
-- Expand the root item in the tree-view control.
tree~expand(rootNode)
/** addNode()
* A private recursive function that adds each node to the tree control. Each
* node represents a single window in the hiearchy.
*
* @param tree The tree-view control object.
*
* @param root A reference to the current item in the tree-view control we
* are working with. Note that this can also be the keyword: root
* which will signal that this is to be the initial item in the
* tree-view control. Otherwise, it is a reference to an already
* created item in the tree-view control.
*
* @param node The current node in the window tree structure we are working
* with.
*/
::method addNode private
use strict arg tree, root, node
-- The text displayed for this item.
text = node~handle || ":" node~text
-- Insert (create) a new item in the tree-view control. Insert it after the
-- 'LAST' sub-item under the item referenced by root in the tree-view control.
-- This item is the window item.
newRoot = tree~insert(root, "LAST", text, , , , node~childrenCount)
-- Insert sub-items. Each of these sub-items is an attribute of the window
tree~insert(newRoot, , "Text:" node~text)
tree~insert(newRoot, , "Handle:" node~handle)
tree~insert(newRoot, , "Class:" node~windowClass)
tree~insert(newRoot, , "ID:" node~id)
tree~insert(newRoot, , "Coordinates:" node~coordinates)
tree~insert(newRoot, , "State:" node~state)
-- Now, if the window has children windows, recursively add them to the
-- tree-view control.
if node~hasChildren then do n over node~children
self~addNode(tree, newRoot, n)
end
return newRoot
/** class: MenuDetailer
*
* A class to parse and display the details of a menu bar.
*
* In Windows, a menu bar is similar to a top level window in that they both
* have a hierarchy of contained objects. A top-level window can contain other
* windows, which themselves can contain other windows. A menu bar can contain
* submenus, which can contain submenus.
*
* This hierarchy is easily displayed in a tree-like structure, for both windows
* and menus. The function of displaying a menu hierachy is therefore very
* similar to the set of functions used to display a window hierarchy provided
* in this framework.
*
* However, the MenuDetailer is a more object-orientated approach than that used
* for displaying a window hierarchy. The data and the means to manipulate the
* data are all contained within a single object, the MenuDetailer object.
*
* Note that this is a subclass of ooDialog's RcDialog, but the class can be
* used / useful without ever creating an underlying Windows dialog. The
* ooDialog part is only used in the display() method which produces a graphical
* dislpay of the menu tree.
*/
::class 'MenuDetailer' public subclass RcDialog
::method init
use strict arg wnd
self~newWindow(wnd)
/** newWindow()
* This method is called to create a menu tree node structure that represents
* the menu of the specified window. It is used when a new MenuDetailer object
* is created and when / if the user of the class sets a new window.
*
* @param wnd A window object whose menu is to be 'detailed.;
*/
::method newWindow private
expose menubar maxTextLength textFormat mainWindow
use strict arg wnd
-- Ensure wnd is a WindowObject.
if \ wnd~isA(.WindowObject) then raise syntax 93.948 array ("1 'wnd'", "WindowObject")
-- Set / reset some state variables.
maxTextLength = 0
textFormat = .nil
menubar = .nil
mainWindow = wnd
-- Create the menu tree node structure.
self~createMenubar
/** createMenubar()
* Creates a menu tree node structure that represents the menu of the main
* window.
*
* Conceptually, a menu consists of a container with some number of menu items,
* where each menu item can, possibly, be a submenu. Typically in Windows, the
* top-level container is called a menubar. A tree is a natural data
* structure to represent this.
*
* The menu tree node structure consists of a directory that represents the
* menubar and has this structure:
*
* menubar~ownerHwnd
* menubar~ownerTitle
* menubar~itemCount
* menubar~menuItems an array of menu items where each menu item is a
* directory.
*
* menuitem~pos
* menuitem~id
* menuitem~text
* menuitem~isSubmenu
* menuitem~isSeparator
* menuitem~isTextItem
* menuitem~isChecked
* menuitem~itemCount
* menuitem~menuItems an array of menu items
*
*/
::method createMenubar private
expose menubar mainWindow
menubar = .directory~new
menubar~ownerTitle = mainWindow~title
menubar~ownerHwnd = mainWindow~hwnd
menu = mainWindow~menu
if menu == .nil then do
menubar~itemCount = -1
menubar~menuItems = .nil
end
else do
menubar~itemCount = menu~items
menubar~menuItems = self~populate(menu)
end
/** populate()
* A recursive function to create and return an array of menu items.
*
* @param A MenuObject object whose menuitems will be used to create and
* populate an array.
*
* @return The populated array.
*
* @see createMenubar()
*/
::method populate private
expose maxTextLength
use strict arg menu
maxTextLength = 0
count = menu~items
a = .array~new(count)
do i = 0 to count - 1
d = .directory~new
d~pos = i
d~id = menu~idOf(i)
d~text = menu~textOf(i)
d~isSubmenu = menu~isSubmenu(i)
d~isSeparator = menu~isSeparator(i)
d~isTextItem = \(d~isSeparator | d~isSubmenu)
d~isChecked = menu~isChecked(i)
if d~text~length > maxTextLength then maxTextLength = d~text~length
if d~isSubmenu then do
submenu = menu~submenu(i)
d~itemCount = submenu~items
d~menuItems = self~populate(submenu)
end
else do
d~itemCount = 0
d~menuItems = .nil
end
a[i + 1] = d
end
return a
/** print()
* Outputs a text representation of the menu to the console.
*/
::method print
expose textFormat
-- If not already created, construct an array of text lines that represents
-- the menu. Then output the lines to the console.
if textFormat == .nil then self~toArray
do l over textFormat
say l
end
/** getOutline()
* Return the array of text lines representing the menu to the caller. The
* caller can then use the array as they see fit.
*/
::method getOutline
expose textFormat
-- Return a copy of the array to the caller so that an outsider can not
-- unintentionally or intentionally change our internal data.
if textFormat == .nil then self~toArray
return textFormat~copy
/** getMenubar()
* Return the menu tree node structure to the caller. The caller can then
* format and / or use the data as desired.
*/
::method getMenubar
expose menubar
-- Recreate the menubar if needed.
if menubar == .nil then self~createMenubar
-- Since ooRexx does not have a deep copy, we save a reference to the menuBar
-- object, set our internal reference to the menubar to .nil, and return the
-- saved reference. This allows the caller to change the menubar without
-- changing our internal representation. If we need the menubar structure
-- again, we will re-create it.
populatedMenubar = menubar
menubar = .nil
return populatedMenubar
/** setWindow()
* Changes the main window to a new one.
*/
::method setWindow
use strict arg wnd
self~newWindow(wnd)
/** toArray()
* Transforms the menu tree node structure that represents our menu into an
* array of text lines that represents the menu.
*/
::method toArray private
expose menubar textFormat
-- Check if we need to recreated the menubar.
if menubar == .nil then self~createMenubar
textFormat = .array~new
-- The first line will show data about the main window. It is possible that
-- the window will were initialized with does not have a menu. In addition,
-- many applications now create their menus dynamically. Those types of menus
-- may not contain any menu items until the user actually selects a menu item.
-- For these two cases we return immediately.
line = menubar~ownerTitle "(" || menubar~ownerHwnd || ")"
if menubar~itemCount == -1 then do
textFormat[1] = line "does not have a menu"
return textFormat
end
else if menubar~itemCount == 0 then do
textFormat[1] = line "menu is not populated"
return textFormat
end
-- Add the first line, then recursively add a line for every menu item.
textFormat[1] = line
do item over menubar~menuItems
self~addArrayItem(item, " ")
end
/** addArrayItem()
* A recursive method that adds a line of text for every menu item to an array
* of lines.
*
* @param item A node in the menu tree node structure representing a menu
* item.
*
* @param indent A string of spaces, the length of which shows the depth of
* the menu item within the overall menu.
*/
::method addArrayItem private
expose maxTextLength textFormat
use strict arg item, indent
-- Build up a line of text for the menu item
line = indent
sep = '-'~copies(maxTextLength)
pos = item~pos~right(2) || ':'
id = '[id:' item~id~right(3) || ']'
select
when item~isSubmenu then do
line = line || pos id 'popup menu' item~text~left(maxTextLength)
end
when item~isSeparator then do
line = line || pos id 'separator ' sep
end
otherwise do
line = line || pos id 'menu item ' item~text~left(maxTextLength)
if item~isChecked then line = line '[checked]'
end
end
-- End select
-- Add the line to our array of lines.
textFormat~append(line)
-- If item is itself a menu, recursively add its menu items to the array.
if item~isSubmenu then do
-- Increase our indent by 2 spaces.
indent = indent || " "
do menuItem over item~menuItems
self~addArrayItem(menuItem, indent)
end
end
/** display()
* Displays our menu in a graphical format using a tree control in a dialog.
*/
::method display
-- Initialize our ooDialog superclass. The dialog template is stored in the
-- resource script file: winSystemDlgs.rec with a symbolic ID of IDD_MENU_TREE
-- and the symbolic IDs for the dialog are defined in winSystemDlgs.h
--
-- Note: We use the locate() function to get the directory this source code
-- is located in. And then, use that value to create a complete path name to
-- our resource files. This ensures the .rc and .h files will always be
-- found.
sd = locate()
self~init:super(sd"winSystemDlgs.rc", IDD_MENU_TREE, , sd"winSystemDlgs.h")
self~execute("SHOWTOP")
/** initDialog()
* Initializes the dialog controls for this dialog. The only control is the
* Tree control.
*
* The ooDialog framework automatically inovkes this method for every dialog
* immediately after the underlying Windows dialog has been created. Since most
* of the initialization of dialog controls requires that the underlying control
* has been created, this makes initDialog() the proper place to do all the
* control initialization.
*
* @see WindowTreeDlg::initDialog()
*/
::method initDialog
expose menubar
-- Check if we need to recreate the menubar.
if menubar == .nil then self~createMenubar
-- Set the dialog title to a subset of what will be the text for the first
-- item in the tree-view control.
rootText = menubar~ownerTitle "(" || menubar~ownerHwnd || ")"
self~setTitle(rootText "Menu Hierarchy")
-- The tree-view control is populated in a similar manner as was the tree-view
-- control in the WindowTreeDlg class in this framework. See that class for
-- comment if needed.
-- Get the tree-view object, set the first item, expand it.
tree = self~newTreeView(IDC_TREE_MENUS)
if menubar~itemCount == -1 then do
rootText = rootText "[window does not have a menu]"
end
else if menubar~itemCount == 0 then do
rootText = rootText "[menu is not populated]"
end
rootNode = tree~add(rootText, , , "EXPANDED")
-- If the menubar has menu items, recursively add then to the tree-view
-- control.
if menubar~itemCount > 0 then do item over menubar~menuItems
self~addNode(tree, rootNode, item)
end
/** addNode()
* Recursively add tree-view items for each menu item in our menu.
*
* @param tree The tree-view control object.
*
* @param root A reference to the current item in the tree-view control we
* are working with.
*
* @param menuItem The current node in the menu tree structure we are working
* with.
*
* @see WindowTreeDlg::initDialog()
*/
::method addNode private
expose maxTextLength
use strict arg tree, root, menuItem
text = menuItem~pos~right(2) || ':'
if menuItem~isSeparator then text = text '-'~copies(maxTextLength)
else text = text menuItem~text
if menuItem~isSubmenu then count = menuItem~itemCount
else count = 0
newRoot = tree~insert(root, "LAST", text, , , , count)
tree~insert(newRoot, , "Text:" menuItem~text)
tree~insert(newRoot, , "ID:" menuItem~id)
tree~insert(newRoot, , "Submenu?" self~logicalToString(menuItem~isSubmenu))
tree~insert(newRoot, , "Text Item?" self~logicalToString(menuItem~isTextItem))
tree~insert(newRoot, , "Separator?" self~logicalToString(menuItem~isSeparator))
tree~insert(newRoot, , "Checked?" self~logicalToString(menuItem~isChecked))
if menuItem~isSubmenu then do mi over menuItem~menuItems
self~addNode(tree, newRoot, mi)
end
return newRoot
/** logicalToString()
* Simple helper method to convert an ooRexx logical value to a string
* representation.
*
* @param logical The value to convert.
*
* @return The string "true" if logical is strictly true, otherwise the string
* "false"
*/
::method logicalToString private
use strict arg logical
-- Work with anything sent to us. This uses the short-cut AND operator. Once
-- a test fails, the rest of the tests are not evaluated. If logical is a
-- string object, and the data type of the string object is lOgical, and
-- logical is true then return "true" Othewise return "false"
if logical~isA(.String), logical~datatype('O'), logical then return 'true'
else return 'false'