851 lines
30 KiB
Rexx
Executable File
851 lines
30 KiB
Rexx
Executable File
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/* 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. */
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* treeViewCustomDraw.rex
|
|
*
|
|
* This example demonstrates many of the features of a tree-view control.
|
|
* Including, but not limited to: label editing of items, custom draw, using
|
|
* image lists to supply the icons for tree-view items, using a custom compare
|
|
* function in the Rexx dialog to sort the tree-view items, displaying info
|
|
* tips, etc..
|
|
*
|
|
* Note: this program uses the public routine, locate(), to get the full path
|
|
* name to the directory this source code file is located. In places, the
|
|
* variable holding this value has been callously abbreviated to 'sd' which
|
|
* stands for source directory.
|
|
*
|
|
*/
|
|
|
|
-- Get our source code file location.
|
|
srcDir = locate()
|
|
|
|
-- Use the global .constDir for symbolic IDs and turn automatic data
|
|
-- detection off.
|
|
.application~setDefaults('O', srcDir'rc\treeViewCustomDraw.h', .false)
|
|
|
|
dlg = .InventoryDlg~new(srcDir"rc\treeViewCustomDraw.rc", IDD_TREE_DLG)
|
|
if dlg~initCode = 0 then do
|
|
ret = dlg~execute("SHOWTOP")
|
|
end
|
|
|
|
return 0
|
|
|
|
::requires "ooDialog.cls" -- Require the ooDialog framework.
|
|
|
|
|
|
/*- TreeViewConstants - Class - - - - - - - - - - - - - - - - - - - - - - - - *\
|
|
|
|
This mixin class defines some constant values and is inherited by the two
|
|
dialog classes in this example.
|
|
|
|
\*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
::class 'TreeViewConstants' mixinclass Object
|
|
::attribute BMP_FILE get
|
|
sd = locate()
|
|
return sd"rc\treeViewCustomDraw.bmp" -- Icons for selected/not-selected items.
|
|
::attribute TREE_FILE get
|
|
sd = locate()
|
|
return sd"treeViewCustomDraw.inp" -- Input file with the items to build the tree.
|
|
::attribute ITEM_FILE get
|
|
sd = locate()
|
|
return sd"treeViewCustomDrawi.inp" -- Input file with dynamically added items.
|
|
|
|
::constant APPLICATION_TITLE "Crazy Sam's Emporium - Inventory"
|
|
|
|
::constant UNSELECTED_FOLDER 0 -- Index for the icon of an unselected folder item
|
|
::constant SELECTED_FOLDER 1 -- Index for the icon of a selected folder item
|
|
::constant UNSELECTED_LEAF 2 -- Index for the icon of an unselected leaf item
|
|
::constant SELECTED_LEAF 3 -- Index for the icon of a selected leaf item
|
|
|
|
|
|
/*- InventoryDlg - Class- - - - - - - - - - - - - - - - - - - - - - - - - - - *\
|
|
|
|
This is the main dialog class for this example. It contains the tree-view
|
|
control that the example is all about.
|
|
|
|
\*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
::class 'InventoryDlg' subclass RcDialog inherit CustomDraw TreeViewConstants
|
|
|
|
/** isLeafItem() [class method]
|
|
*
|
|
* The isLeafItem() method provides a convenient way to test if any tree-view
|
|
* item is a leaf item rather than a folder item. Leaf items and folder items
|
|
* in this example use different icon images, so we just test which icon is set
|
|
* for the item.
|
|
*/
|
|
::method isLeafItem class
|
|
use strict arg treeView, item
|
|
|
|
info. = treeView~itemInfo(item)
|
|
|
|
return info.!Image == self~UNSELECTED_LEAF
|
|
|
|
|
|
/** init()
|
|
*
|
|
* Initialization of the Rexx dialog object. We do several things here.
|
|
*
|
|
* 1.) Initialize the super class. Never invoke any method on a dialog class
|
|
* before the super class is initialized.
|
|
*
|
|
* 2.) So a sanity check to ensure the data files needed can be found.
|
|
*
|
|
* 3.) Connect the event handlers for the tree-view.
|
|
*
|
|
* 4.) Create custom colors to draw the individual items in the tree-view.
|
|
*
|
|
* 5.) Initialize the custom draw interface and register the tree-view control
|
|
* to use custom draw.
|
|
*/
|
|
::method init
|
|
expose bkClr oddLevelClr evenLevelClr selectedClr leafClr
|
|
use arg rcFile, idDlg
|
|
|
|
self~init:super(rcFile, idDlg)
|
|
if self~initCode <> 0 then return self~initCode
|
|
|
|
if self~checkForRequiredFiles == .false then do
|
|
self~initCode = 17
|
|
return self~initCode
|
|
end
|
|
|
|
-- Connect dialog control events to methods in the Rexx dialog.
|
|
self~connectTreeViewEvent(IDC_TREE, "EXPANDING", "onExpanding", .true)
|
|
self~connectTreeViewEvent(IDC_TREE, "DEFAULTEDIT")
|
|
self~connectTreeViewEvent(IDC_TREE, "KEYDOWN", "onKeyDown")
|
|
self~connectTreeViewEvent(IDC_TREE, "GETINFOTIP", "onGetInfoTip")
|
|
|
|
self~connectButtonEvent(IDC_PB_NEW, "CLICKED", "onNewItem")
|
|
self~connectButtonEvent(IDC_PB_DELETE, "CLICKED", "onDeleteItem")
|
|
self~connectButtonEvent(IDC_PB_SORT, "CLICKED", "onSortChildren")
|
|
self~connectButtonEvent(IDC_PB_EXP_ALL, "CLICKED", "onExpandAll")
|
|
self~connectButtonEvent(IDC_PB_COL_ALL, "CLICKED", "onCollapseAll")
|
|
self~connectButtonEvent(IDC_PB_INFO, "CLICKED", "onItemInfo")
|
|
|
|
-- Create the custom colors use to draw the tree-view items
|
|
bkClr = self~RGB(250, 250, 250)
|
|
oddLevelClr = self~RGB( 82, 61, 0)
|
|
evenLevelClr = self~RGB( 0, 20, 82)
|
|
selectedClr = self~RGB( 82, 0, 20)
|
|
leafClr = self~RGB( 0, 82, 20)
|
|
|
|
-- Initialize the custom draw interface and register the tree-view control
|
|
-- to use custom draw.
|
|
self~customDraw
|
|
self~customDrawControl(IDC_TREE, 'TreeView')
|
|
|
|
return 0
|
|
|
|
/** initDialog()
|
|
*
|
|
* Just as the init() method is for initializing the Rexx dialog object, the
|
|
* initDialog() method is used to initialize the underlying Windows dialog.
|
|
* Some things, like inserting the items into a tree-view, can only be done
|
|
* after the Windows dialog exists. Those types of things are done here.
|
|
*/
|
|
::method initDialog
|
|
expose tv
|
|
|
|
tv = self~newTreeView("IDC_TREE")
|
|
|
|
-- Set the image list for the tree-vies
|
|
image = .Image~getImage(self~BMP_FILE)
|
|
imageList = .ImageList~create(.Size~new(16, 12), 'COLOR8', 5, 2)
|
|
if \image~isNull, \imageList~isNull then do
|
|
imageList~add(image)
|
|
tv~setImageList(imageList, 'NORMAL')
|
|
image~release
|
|
end
|
|
|
|
-- Read the file containing the tree input data and build the tree.
|
|
do while lines(self~TREE_FILE)
|
|
args = self~makeArgs(linein(self~TREE_FILE))
|
|
tv~sendWith('add', args)
|
|
end
|
|
|
|
-- Select the item with the text of Computers.
|
|
hItem = tv~find('Computers')
|
|
if hItem \== 0 then tv~select(hItem)
|
|
|
|
return 0
|
|
|
|
|
|
/*- - - - - - - - - - Event handler(s) - - - - - - - - - - - - - - - - - - - -*/
|
|
|
|
/** onCustomDraw()
|
|
*
|
|
* This is the event handler for the custom draw event. Certain of the dialog
|
|
* controls in Windows support custom draw. Those controls send custom draw
|
|
* event notifications through out the paint cycle when the control is drawing
|
|
* or redrawing itself
|
|
*
|
|
* Please read the custom draw documentation in the ooDialog reference manual to
|
|
* fully understand the details.
|
|
*
|
|
* We take advantage of custom draw here to paint each individual tree-view item
|
|
* a custom color depending on exactly which item is about to be drawn.
|
|
*
|
|
* Our scheme is relatively simple. Every other level of the tree is painted an
|
|
* alternating color. If an item is selected, it is painted a reddish color to
|
|
* match the reddish selected icon. If not selected and a leaf node, we paint
|
|
* it a greenish color to match the non-folder, non-selected greenish icon.
|
|
*/
|
|
::method onCustomDraw unguarded
|
|
expose tv bkClr oddLevelClr evenLevelClr selectedClr leafClr
|
|
use arg tvcds
|
|
|
|
if tvcds~drawStage == self~CDDS_ITEMPREPAINT then do
|
|
tvcds~reply = self~CDRF_NEWFONT
|
|
|
|
selected = tv~selected
|
|
isLeaf = .InventoryDlg~isLeafItem(tv, tvcds~item)
|
|
|
|
if selected == tvcds~item then do
|
|
tvcds~clrText = selectedClr
|
|
tvcds~clrTextBk = bkClr
|
|
end
|
|
else if isLeaf then do
|
|
tvcds~clrText = leafClr
|
|
tvcds~clrTextBk = bkClr
|
|
end
|
|
else if tvcds~level // 2 == 1 then do
|
|
tvcds~clrText = oddLevelClr
|
|
tvcds~clrTextBk = bkClr
|
|
end
|
|
else do
|
|
tvcds~clrText = evenLevelClr
|
|
tvcds~clrTextBk = bkClr
|
|
end
|
|
|
|
return .true
|
|
end
|
|
|
|
return .false
|
|
|
|
|
|
/** onExpanding()
|
|
*
|
|
* This is the event handler for the EXPANDING event. The method is invoked
|
|
* when a tree-view item is about to be expanded or collapsed. The method is
|
|
* invoked before the item is expanded or collapsed.
|
|
*
|
|
* This method demonstrates dynamically adding items to the tree. The node,
|
|
* 'Special Offers' is initially added to the tree during initDialog with no
|
|
* children.
|
|
*
|
|
* When the user expands the node, the child items for the node are dynamically
|
|
* added here. The items are loaded from a file in the same manner as the
|
|
* original items are loaded from a file to build the tree.
|
|
*
|
|
* Note that the fourth argument to connectTreeViewEvent() was used for the
|
|
* EXPANDING event and set to true. This causes the interpreter to wait for the
|
|
* reply from this event handler, before it replies to the operating system. If
|
|
* the interpreter were to not wait for the reply, the tree-view control would
|
|
* immediately expand the item before the children were added.
|
|
*
|
|
* When the user collapses the node, all its children items are removed. This
|
|
* is done through the collapseAndReset method, which collapse the item and also
|
|
* tells the tree-view control to deallocate the children.
|
|
*/
|
|
::method onExpanding unguarded
|
|
expose tv
|
|
use arg tree, item, what, extra
|
|
|
|
itemInfo. = tv~itemInfo(item)
|
|
|
|
if itemInfo.!TEXT = "Special Offers" then do
|
|
if what == "EXPANDED", tv~child(item) == 0 then do
|
|
do while lines(self~ITEM_FILE)
|
|
args = self~makeArgs(item, , linein(self~ITEM_FILE))
|
|
newItem = tv~sendWith('insert', args)
|
|
end
|
|
end
|
|
else if what == "COLLAPSED", tv~child(item) \== 0 then do
|
|
tv~collapseAndReset(item)
|
|
end
|
|
end
|
|
|
|
return .true
|
|
|
|
|
|
/** onKeyDown()
|
|
*
|
|
* This is the event handler for the key down event. It is invoked each time
|
|
* the user presses a key when the tree-view has the focus.
|
|
*
|
|
* We examime the key pressed to see if it is the insert or delete key.
|
|
*
|
|
* On the insert key we simulate pushing the 'New Item' push button by invoking
|
|
* the event handler for that button. This allows the user to add a new tree-
|
|
* view item.
|
|
*
|
|
* On the delete key, we delete the selected item.
|
|
*/
|
|
::method onKeyDown unguarded
|
|
expose tv
|
|
use arg treeId, key
|
|
|
|
if key == .VK~DELETE then
|
|
tv~delete(tv~Selected)
|
|
else if key == .VK~INSERT then
|
|
self~onNewItem
|
|
|
|
return 0
|
|
|
|
/** onNewItem()
|
|
*
|
|
* This is the event handler for the 'New Item' push button. It is invoked when
|
|
* the user clicks that button.
|
|
*
|
|
* We put up a dialog that allows the user to fill out the details for a new
|
|
* tree-view item and insert it into the tree-view.
|
|
*/
|
|
::method onNewItem unguarded
|
|
expose tv
|
|
|
|
sd = locate()
|
|
dlg = .NewTreeItemDlg~new(sd"rc\treeViewCustomDraw.rc", IDD_ADD_TREE_ITEM, tv)
|
|
dlg~execute
|
|
|
|
return 0
|
|
|
|
|
|
/** onDeleteItem()
|
|
*
|
|
* This is the event handler for the 'Delete Item' push button. This method is
|
|
* invoked when the user clicks that button.
|
|
*
|
|
* We expand the selected item and all its children recursively.
|
|
*/
|
|
::method onDeleteItem unguarded
|
|
expose tv
|
|
|
|
selected = tv~selected
|
|
if selected == 0 then do
|
|
title = self~APPLICATION_TITLE~subword(1, 3) '- Warning'
|
|
msg = "There is no tree-view item selected. If you" || .endOfLine ||-
|
|
"continue, all items will be deleted." || .endOfLine~copies(2) ||-
|
|
"Do you want to continue and delete all items?"
|
|
|
|
ret = MessageDialog(msg, self~hwnd, title, 'YESNO', 'WARNING', 'DEFBUTTON2')
|
|
if ret == self~IDNO then return 0
|
|
end
|
|
|
|
tv~delete(selected)
|
|
return 0
|
|
|
|
|
|
/** onGetInfoTip()
|
|
*
|
|
* This is the event handler for the INFOTIP event. It is invoked when the
|
|
* tree-view wants the text to display in the info tip.
|
|
*
|
|
* If we return the empty string, then no info tip is displayed. Otherwise, the
|
|
* text returned is displayed. Here, we only return text when the itemData is
|
|
* the string: '...' In that case we return some text. In all other cases we
|
|
* return the empty string and no info tip is displayed.
|
|
*/
|
|
::method onGetInfoTip unguarded
|
|
expose tv
|
|
use arg id, hItem, text, maxLen, itemData
|
|
|
|
if itemData == '...' then return 'There are too many books to list'
|
|
else return ''
|
|
|
|
|
|
/** onSortChildre()
|
|
*
|
|
* This is the event handler for the 'Reverse Sort' push button. This method is
|
|
* invoked when the user pushes that button.
|
|
*
|
|
* Note that the tree-view provides a function to sort the children of an item,
|
|
* but it only sorts in ascending alphabetical order of the item text. The
|
|
* ooDialog TreeView class provides that function in the sortChildren() method.
|
|
*
|
|
* To sort in any other order requires using the sortChildrenCB() method. With
|
|
* this method, the programmer names a 'call back' method in the Rexx dialog.
|
|
* This method is then invoked by the tree-view for each item it needs to
|
|
* determine the order of.
|
|
*
|
|
* This is what we do here, we use the sortChildrenCB method and provide on own
|
|
* comparsion method, rexxSort().
|
|
*
|
|
* We sort the children of the selected item. If no item is selected we sort
|
|
* the children of the root item. Note that only the direct children of the
|
|
* parent item are sorted. To sort all items, the programmer would need to
|
|
* implement a recursive function, similar to the expandAll() or collapseAll()
|
|
* functions.
|
|
*/
|
|
::method onSortChildren unguarded
|
|
expose tv
|
|
|
|
selectedItem = tv~selected
|
|
if selectedItem == 0 then selectedItem = tv~root
|
|
|
|
ret = tv~sortChildrenCB(selectedItem, rexxSort)
|
|
|
|
|
|
/** rexxSort()
|
|
*
|
|
* This is our comparison callback function. We are passed the user item data
|
|
* for the first tree-view item and the user item data for the second tree-view
|
|
* item. The method needs to return a positive number if the first item is
|
|
* greater than the second, a negative number if the first item is less than the
|
|
* second, and 0 if the two items are equivalent.
|
|
*
|
|
* In this program, for every tree-view item inserted, we set the item data for
|
|
* that item to be the text of the item. Then, in our comparison function here,
|
|
* we just do a reverse comparison of that text. This orders the items in
|
|
* descending order rather than ascending order. The userParam argument is
|
|
* ignored here.
|
|
*/
|
|
::method rexxSort unguarded
|
|
use arg itemData1, itemData2, userParam
|
|
|
|
-- Reverse sort:
|
|
return itemData2~compareTo(itemData1)
|
|
|
|
|
|
/** onExpandAll()
|
|
*
|
|
* This is the event handler for the 'Expand All' push button. This method is
|
|
* invoked when the user clicks that button.
|
|
*
|
|
* We expand the selected item and all its children recursively.
|
|
*/
|
|
::method onExpandAll unguarded
|
|
expose tv
|
|
|
|
if tv~selected == 0 then do
|
|
title = self~APPLICATION_TITLE~subword(1, 3) '- Error'
|
|
msg = "There is no tree-view item selected. Before" || .endOfLine ||-
|
|
"you can expand all items you must first" || .endOfLine ||-
|
|
"select an item"
|
|
|
|
ret = MessageDialog(msg, self~hwnd, title, 'OK', 'WARNING')
|
|
return 0
|
|
end
|
|
|
|
if self~isFullyExpanded(tv~selected) then do
|
|
title = self~APPLICATION_TITLE~subword(1, 3) '- Information'
|
|
msg = "The selected tree-view item is already fully" || .endOfLine ||-
|
|
"expanded. Expanding will take place, but it" || .endOfLine ||-
|
|
"have no visile effect."
|
|
|
|
ret = MessageDialog(msg, self~hwnd, title, 'OK', 'WARNING')
|
|
end
|
|
|
|
self~expandAll(tv~selected)
|
|
|
|
return 0
|
|
|
|
/** onCollapseAll()
|
|
*
|
|
* This is the event handler for the 'Collapse All' push button. This method is
|
|
* invoked when the user clicks that button.
|
|
*
|
|
* We collapse the selected item and all its children recursively.
|
|
*/
|
|
::method onCollapseAll unguarded
|
|
expose tv
|
|
|
|
if tv~selected == 0 then do
|
|
title = self~APPLICATION_TITLE~subword(1, 3) '- Error'
|
|
msg = "There is no tree-view item selected. Before" || .endOfLine ||-
|
|
"you can collapse all items you must first" || .endOfLine ||-
|
|
"select an item"
|
|
|
|
ret = MessageDialog(msg, self~hwnd, title, 'OK', 'WARNING')
|
|
return 0
|
|
end
|
|
|
|
itemInfo. = tv~itemInfo(tv~selected)
|
|
if itemInfo.!State~wordPos('EXPANDED') == 0 then do
|
|
title = self~APPLICATION_TITLE~subword(1, 3) '- Information'
|
|
msg = "The selected tree-view item is already collapsed. You" || .endOfLine || -
|
|
"may not see anything, but all children of the selected" || .endOfLine || -
|
|
"item will be collapsed."
|
|
|
|
ret = MessageDialog(msg, self~hwnd, title, 'OK', 'INFORMATION')
|
|
end
|
|
|
|
self~collapseAll(tv~selected)
|
|
|
|
return 0
|
|
|
|
/** onItemInfo
|
|
*
|
|
* This is the event handler for the 'Item Info' push button. It iw invoked
|
|
* when the button is clicked.
|
|
*
|
|
* We get the selected tree-view item and display information about it.
|
|
*/
|
|
::method onItemInfo unguarded
|
|
expose tv
|
|
|
|
selected = tv~selected
|
|
|
|
if selected == 0 then do
|
|
title = self~APPLICATION_TITLE~subword(1, 3) '- Error'
|
|
msg = "There is no tree-view item selected. Before" || .endOfLine ||-
|
|
"you can display information on an item you" || .endOfLine ||-
|
|
"must first select an item"
|
|
|
|
ret = MessageDialog(msg, self~hwnd, title, 'OK', 'WARNING')
|
|
return 0
|
|
end
|
|
|
|
itemInfo. = tv~itemInfo(selected)
|
|
tab1 = '09'x
|
|
tab2 = tab1~copies(2)
|
|
|
|
hasChildren = self~logicalToString(itemInfo.!Children)
|
|
|
|
title = self~APPLICATION_TITLE~changeStr('-', '') '- Item Information'
|
|
msg = 'The Selected Item is:' || tab1 || '"'itemInfo.!Text'"' || .endOfLine~copies(2) || -
|
|
' Contains children:' || tab2 || hasChildren || .endOfLine || -
|
|
' Unselected icon index:' || tab1 || itemInfo.!Image || .endOfLine || -
|
|
' Selected icon index:' || tab2 || itemInfo.!SelectedImage || .endOfLine || -
|
|
' Item state:' || tab2 || itemInfo.!State || .endOfLine || -
|
|
' Is leaf node:' || tab2 || self~logicalToString(.InventoryDlg~isLeafItem(tv, selected))
|
|
|
|
ret = MessageDialog(msg, self~hwnd, title 'OK', 'INFORMATION')
|
|
|
|
return 0
|
|
|
|
/** help()
|
|
*
|
|
* The help command event handler is connected automatically by the ooDialog
|
|
* framework to the command with ID of 9. The default help event handler
|
|
* provided by ooDialog does nothing.
|
|
*
|
|
* In this example, the Help push button is given the resource ID of 9, and
|
|
* therefore the help event handler is invoked when the user clicks on the Help
|
|
* button. Rather than do noting when the button is clicked, we over-ride the
|
|
* help method here. We still do not do much, just inform the user that there
|
|
* is no help.
|
|
*/
|
|
::method help unguarded
|
|
title = self~APPLICATION_TITLE
|
|
msg = "There is no help available for this example program."
|
|
ret = MessageDialog(msg, self~hwnd, title, "OK", "INFORMATION")
|
|
return 0
|
|
|
|
|
|
/*- - - - - - - - - - Helper Methods - - - - - - - - - - - - - - - - - - - - -*/
|
|
|
|
|
|
/** expandAll()
|
|
*
|
|
* This helper method recursively expands all children nodes of the specified
|
|
* tree-view item.
|
|
*/
|
|
::method expandAll private unguarded
|
|
expose tv
|
|
use strict arg item
|
|
|
|
if item == 0 then return 0
|
|
|
|
tv~expand(item)
|
|
|
|
nextItem = tv~child(item)
|
|
do while nextItem \== 0
|
|
self~expandAll(nextItem)
|
|
nextItem = tv~next(nextItem)
|
|
end
|
|
|
|
return 0
|
|
|
|
/** collapseAll()
|
|
*
|
|
* This helper method recursively collapse the specified tree-view item and all
|
|
* its children.
|
|
*/
|
|
::method collapseAll private unguarded
|
|
expose tv
|
|
use strict arg item
|
|
|
|
if item == 0 then return 0
|
|
|
|
nextItem = tv~child(item)
|
|
do while nextItem \== 0
|
|
self~collapseAll(nextItem)
|
|
nextItem = tv~next(nextItem)
|
|
end
|
|
|
|
tv~collapse(item)
|
|
|
|
return 0
|
|
|
|
|
|
/** isFullyExpanded()
|
|
*
|
|
* This helper method checks if the specified item is fully expanded by
|
|
* recursively descending though the children of the item and checking that each
|
|
* item is expanded.
|
|
*
|
|
* Returns .true if the item is fully expanded, otherwise .false.
|
|
*/
|
|
::method isFullyExpanded private unguarded
|
|
expose tv
|
|
use strict arg item
|
|
|
|
if item == 0 then return .true
|
|
if .InventoryDlg~isLeafItem(tv, item) then return .true
|
|
|
|
itemInfo. = tv~itemInfo(item)
|
|
if itemInfo.!State~wordPos('EXPANDED') == 0 then return .false
|
|
|
|
nextItem = tv~child(item)
|
|
do while nextItem \== 0
|
|
if \ self~isFullyExpanded(nextItem) then return .false
|
|
nextItem = tv~next(nextItem)
|
|
end
|
|
|
|
return .true
|
|
|
|
/** checkForRequiredFiles()
|
|
*
|
|
* This helper method is used to check that the bitmap and input files are
|
|
* available. It returns .true if they are found and .false if they are not
|
|
* found.
|
|
*/
|
|
::method checkForRequiredFiles private
|
|
|
|
haveError = .false
|
|
file = ''
|
|
|
|
if stream(self~BMP_FILE, "C", "QUERY EXISTS") = "" then do
|
|
haveError = .true
|
|
file = self~BMP_FILE
|
|
end
|
|
else if stream(self~TREE_FILE, "C", "QUERY EXISTS") = "" then do
|
|
haveError = .true
|
|
file = self~TREE_FILE
|
|
end
|
|
else if stream(self~ITEM_FILE, "C", "QUERY EXISTS") = "" then do
|
|
haveError = .true
|
|
file = self~ITEM_FILE
|
|
end
|
|
|
|
if haveError then do
|
|
title = self~APPLICATION_TITLE~subword(1, 3) '- File Error'
|
|
msg = "The required data file" file || .endOfLine || -
|
|
"does not exist. Without that file, this progam" || .endOfLine || -
|
|
"will not run." || .endOfLine~copies(2) || -
|
|
"The program will abort."
|
|
|
|
ret = MessageDialog(msg, 0, title, 'OK', 'ERROR')
|
|
end
|
|
|
|
return \haveError
|
|
|
|
|
|
/** makeArgs()
|
|
*
|
|
* This helper method turns a line of comma separted values into an argument
|
|
* array with empty values turned into empty indexes in the array.
|
|
*/
|
|
::method makeArgs private unguarded
|
|
if arg() == 1 then line = arg(1)
|
|
else line = arg(3)
|
|
|
|
args = line~makeArray(',')
|
|
if arg() == 1 then do
|
|
newArgs = .array~new(args~items)
|
|
do i = 1 to args~items
|
|
if args[i]~strip \== "" then newArgs[i] = args[i]~strip('B', '"')
|
|
end
|
|
end
|
|
else do
|
|
newArgs = .array~new(args~items + 2)
|
|
newArgs[1] = arg(1)
|
|
newArgs[2] = 'LAST'
|
|
|
|
j = 2
|
|
do i = 1 to args~items
|
|
j += 1
|
|
if args[i]~strip \== "" then newArgs[j] = args[i]~strip('B', '"')
|
|
end
|
|
end
|
|
|
|
return newArgs
|
|
|
|
/** logicalToString()
|
|
*
|
|
* This helper method converts and returns a value to true or false if value is
|
|
* a logical, otherwise it returns the empty string.
|
|
*/
|
|
::method logicalToString private
|
|
use strict arg logical
|
|
if logical == .true then return 'True'
|
|
else if logical == .false then return 'False'
|
|
else return ''
|
|
|
|
/** print()
|
|
*
|
|
* This helper function is useful in debugging. It prints the label of the
|
|
* specified tree-view item to the screen.
|
|
*/
|
|
::method print private unguarded
|
|
expose tv
|
|
use strict arg item, indent
|
|
|
|
itemInfo. = tv~itemInfo(item);
|
|
say indent || itemInfo.!Text
|
|
|
|
return 0
|
|
|
|
|
|
/*- NewTreeItemDlg - Class- - - - - - - - - - - - - - - - - - - - - - - - - - *\
|
|
|
|
This dialog class allows the user to add a new item to the tree-view
|
|
|
|
\*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
::class 'NewTreeItemDlg' subclass RcDialog inherit TreeViewConstants
|
|
|
|
/** init()
|
|
*
|
|
* The initialization of the Rexx dialog object. We just use this to grab a
|
|
* reference to the tree-view control in the parent dialog.
|
|
*/
|
|
::method init
|
|
expose treeControl
|
|
use arg scriptFile, dlgID, treeControl
|
|
|
|
-- Initialize the super class.
|
|
self~init:super(scriptFile, dlgID)
|
|
|
|
/** initDialog()
|
|
*
|
|
* The initialization of the Windows dialog. We use this to set the state of
|
|
* the various controls in the dialog.
|
|
*/
|
|
::method initDialog
|
|
expose treeControl editControl childRB folderChk selected
|
|
|
|
-- Save a reference to the current selected item
|
|
selected = treeControl~selected
|
|
|
|
editControl = self~newEdit(IDC_EDIT_NAME)
|
|
childRB = self~newRadioButton(IDC_RB_CHILD)
|
|
siblingRB = self~newRadioButton(IDC_RB_SIBLING)
|
|
folderChk = self~newCheckBox(IDC_CHK_FOLDER)
|
|
|
|
-- If the selected is the root of the tree, a new item has to be inserted as
|
|
-- a child. So disable the radio buttons that allow the user to choose to
|
|
-- insert as a child or sibling. And, pre-check the add as a folder check
|
|
-- box.
|
|
if selected == treeControl~root then do
|
|
childRB~~check~disable
|
|
siblingRB~disable
|
|
folderChk~check
|
|
end
|
|
else if .InventoryDlg~isLeafItem(treeControl, selected) then do
|
|
siblingRB~~check~disable
|
|
childRB~disable
|
|
end
|
|
else do
|
|
siblingRB~check
|
|
end
|
|
|
|
-- Set a visual cue for the edit control. This will only show when the edit
|
|
-- control has no text in it, and does not have the focus.
|
|
editControl~setCue("Enter name of new item")
|
|
|
|
return 0
|
|
|
|
/** ok()
|
|
*
|
|
* The is the event handler for the Ok button. It is invoked when the user
|
|
* clicks that button.
|
|
*
|
|
* The ooDialog framework provides a default implementation for this event. It
|
|
* is important to always close a dialog, once the underlying Windows dialog has
|
|
* been created, through the Ok or Cancel event handlers. This ensures that the
|
|
* dialog is ended correctly and that the needed clean up.
|
|
*
|
|
* Here we over-ride the superclass ok method so that we can check that the user
|
|
* entered the correct data for the new item and then insert it into the tree-
|
|
* view. If the user did not enter the data correctly, we giver her a chance to
|
|
* correct it. We give her a chance to cancel and if she does, we end the
|
|
* dialog cancel instead of ok.
|
|
*
|
|
* Note that we end the dialog by either calling the superclass ok or cancel
|
|
* method. We prevent the dialog from closing by *not* invoking one of those
|
|
* methods and simply returning.
|
|
*/
|
|
::method ok
|
|
expose treeControl editControl childRB folderChk selected
|
|
|
|
-- Make sure the user has given the item a name.
|
|
text = editControl~getText~strip
|
|
if text == "" then do
|
|
msg = "You must enter the name of the new item"
|
|
title = self~APPLICATION_TITLE~changestr('-', '') '- Add Item'
|
|
|
|
ret = MessageDialog(msg, self~hwnd, title, 'WARNING', 'OKCANCEL')
|
|
if ret == self~IDCANCEL then return self~cancel:super
|
|
|
|
editControl~assignFocus
|
|
return 0
|
|
end
|
|
|
|
-- See if the user wants to add this as a folder item, or a regular item.
|
|
-- This will determine the image IDs we use when we insert the item
|
|
addAsFolder = folderChk~checked
|
|
|
|
-- Now insert the item either as a child or a sibling depending on what the
|
|
-- user requested. Note that we set the item data on each insert to the text
|
|
-- of the inserted item.
|
|
if childRB~checked then do
|
|
if addAsFolder then newItem = treeControl~insert(selected, , text, self~UNSELECTED_FOLDER, self~SELECTED_FOLDER, , , text)
|
|
else newItem = treeControl~insert(selected, , text, self~UNSELECTED_LEAF, self~SELECTED_LEAF, , , text)
|
|
treeControl~expand(treeControl~parent(newItem))
|
|
end
|
|
else do
|
|
if addAsFolder then treeControl~insert(treeControl~Parent(selected), , text, self~UNSELECTED_FOLDER, self~SELECTED_FOLDER, , , text)
|
|
else treeControl~insert(treeControl~Parent(selected), , text, self~UNSELECTED_LEAF, self~SELECTED_LEAF, , , text)
|
|
end
|
|
|
|
-- Finally, quit by invoking the super class ok() method.
|
|
return self~ok:super
|
|
|
|
|