1032 lines
35 KiB
Rexx
Executable File
1032 lines
35 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. */
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Name: oodListViews.rex
|
|
* Type: Open Object Rexx (ooRexx) example
|
|
* Resources: oodListViews.rc, oodListViews1.bmp, oodListViews2.bmp
|
|
*
|
|
* Description:
|
|
*
|
|
* Demonstrates the different views possible in the list-view control. Shows
|
|
* how to use the ControlDialog class to populate each page in a tab
|
|
* control. Demonstrates some other list-view features, such as info tips,
|
|
* user item data, in place label editing, drag and drop in icon view, 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.
|
|
*
|
|
*/
|
|
|
|
srcDir = locate()
|
|
.application~useGlobalConstDir("O", srcDir'rc\oodListViews.h')
|
|
|
|
dlg = .ListsDialog~new(srcDir"rc\oodListViews.rc", IDD_LISTVIEWS)
|
|
|
|
if dlg~initCode <> 0 then do
|
|
say "Error instantiating the ListsDialog, aborting"
|
|
return 99
|
|
end
|
|
|
|
dlg~execute("SHOWTOP")
|
|
|
|
return 0
|
|
|
|
::requires "ooDialog.cls"
|
|
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
|
|
ListsDialog Class - the main (top-level) dialog.
|
|
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
|
::class 'ListsDialog' subclass RcDialog
|
|
|
|
::method init
|
|
expose sd
|
|
|
|
forward class (super) continue
|
|
|
|
if self~initCode <> 0 then return self~initCode
|
|
|
|
sd = locate()
|
|
|
|
self~createImageLists
|
|
self~initRecords
|
|
|
|
self~connectButtonEvent(IDC_PB_ADDRECORD, "CLICKED", "onAdd")
|
|
self~connectButtonEvent(IDC_PB_EDITRECORD, "CLICKED", "onEdit")
|
|
self~connectButtonEvent(IDC_PB_FORWARD, "CLICKED", "onForward")
|
|
self~connectButtonEvent(IDC_PB_BACKWARD, "CLICKED", "onBackward")
|
|
|
|
self~connectButtonEvent(IDC_CK_INFOTIPS, "CLICKED", onCheckClicked)
|
|
|
|
self~connectTabEvent(IDC_TAB, 'SELCHANGE', 'onNewTab')
|
|
|
|
|
|
::method initDialog
|
|
expose tabControl pageDialog smallIcons normalIcons records pbBackward pbForward ckInfoTips sd
|
|
|
|
-- Set the Use Info Tips check box.
|
|
ckInfoTips = self~newCheckBox(IDC_CK_INFOTIPS)
|
|
ckInfoTips~check
|
|
|
|
-- Disable the edit record push button.
|
|
self~newPushButton(IDC_PB_EDITRECORD)~disable
|
|
|
|
-- Save a reference to the push buttons.
|
|
pbBackward = self~newPushButton(IDC_PB_BACKWARD)
|
|
pbForward = self~newPushButton(IDC_PB_Forward)
|
|
|
|
-- We start on the first page, so going backwards is not possible.
|
|
pbBackward~disable
|
|
|
|
-- Add the tabs to the tab control.
|
|
tabControl = self~newTab(IDC_TAB)
|
|
tabControl~addSequence("List", "Report", "Icon", "Small Icon")
|
|
|
|
-- Get the display rectangle of the tab control.
|
|
displayRect = self~calculateDisplayArea(tabControl)
|
|
|
|
-- The tab control is quite wide in relation to the default space the 4 tabs
|
|
-- occupy. Make the tabs wide enough to take up a little over 1/2 of the width
|
|
-- of the tab control.
|
|
w = (displayRect~right * (7 / 12)) % 4
|
|
tabControl~setMinTabWidth(w)
|
|
|
|
pageDialog = .PageDialog~new(sd"rc\oodListViews.rc", IDD_PAGE, , , , , self)
|
|
pageDialog~useInfoTips = .true
|
|
pageDialog~initialize(smallIcons, normalIcons, records)
|
|
|
|
-- Use execute() to properly start a ControlDialog.
|
|
pageDialog~execute
|
|
self~positionAndShow(pageDialog, tabControl, displayRect)
|
|
|
|
|
|
/** calculateDisplayArea()
|
|
*
|
|
* Tab controls contain two areas, the tabs themselves and the display area.
|
|
* The display area is where the content for each tab is drawn.
|
|
*
|
|
* We need to match the control dialog(s) size and position with the display
|
|
* area size and position. There are two approaches here:
|
|
*
|
|
* We could calculate the size of the largest dialog, resize the tab control to
|
|
* match, and position the control dialog over the display area.
|
|
*
|
|
* We can get the size and position of the tab control's display area and resize
|
|
* and reposition the control dialog(s) to match. This is the approach we use
|
|
* here.
|
|
*/
|
|
::method calculateDisplayArea private
|
|
use strict arg tabControl
|
|
|
|
-- Given a rectangle describing the tab control's size and position, the tab
|
|
-- control itself will calculate the display area's size and position.
|
|
r = tabControl~windowRect
|
|
tabControl~calcDisplayRect(r)
|
|
|
|
-- Save the size of the display area, we need it later.
|
|
s = .Size~new(r~right - r~left, r~bottom - r~top)
|
|
|
|
-- Now we need to map the display area's position on the screen, to the client
|
|
-- co-ordinates of the main dialog. The control dialog(s) are children windows
|
|
-- of the main dialog, which is why we need to use the client-area of the
|
|
-- dialog, not the client area of the tab control.
|
|
p = .Point~new(r~left, r~top)
|
|
self~screen2client(p)
|
|
|
|
-- Create our display rectangle. This is used in setWindowPosition(), which
|
|
-- takes a point / size rectangle. ooDialog defines a point / size rectangle
|
|
-- as using the left and top attributes for the position of the upper left
|
|
-- corner of a rectangle, using the right attribute for the width of the
|
|
-- rectangle, and using the bottom attribute for the height of the rectangle.
|
|
return .Rect~new(p~x, p~y, s~width, s~height)
|
|
|
|
|
|
/** positionAndShow()
|
|
*
|
|
* Used to resize and reposition the control dialog (PageDialog so it occupies
|
|
* the display area of the tab control.
|
|
*/
|
|
::method positionAndShow private
|
|
use strict arg dlg, tabControl, displayRect
|
|
|
|
-- We can not position the control dialog until the underlying Windows dialog
|
|
-- is created and we have a valid window handle. This takes a small but
|
|
-- disecernable amount of time. The actual amount of time can vary widely and
|
|
-- depends on both the amount of concurrency going on in the interpreter and
|
|
-- the amount of multi-tasking going on in the operating system.
|
|
--
|
|
-- We don't want to spin forever if there is some error in creating the
|
|
-- dialog, so we set a timeout here. However, it is very short and may not be
|
|
-- long enough. If it is not long enough, it is better to increase the time
|
|
-- sleeping rather than the loop counter.
|
|
do i = 1 to 10
|
|
if dlg~hwnd <> 0 then leave
|
|
z = SysSleep(.005)
|
|
end
|
|
|
|
if dlg~hwnd == 0 then do
|
|
say "Error creating dialog for the tab with index:" 1", aborting"
|
|
return self~cancel:super
|
|
end
|
|
|
|
-- Now resize and reposition the control dialog to the tab control's display
|
|
-- area. We need to position the control dialog *above* the tab control in
|
|
-- the Z-order so that it shows.
|
|
dlg~setWindowPos(tabControl~hwnd, displayRect, "SHOWWINDOW NOOWNERZORDER")
|
|
|
|
|
|
/** onCheckClicked()
|
|
*
|
|
* This is the event handler for the CLICKED event of the Use Info Tips check
|
|
* box. Each time it is clicked, we update the Page dialog with its state.
|
|
*/
|
|
::method onCheckClicked unguarded
|
|
expose ckInfoTips pageDialog
|
|
|
|
if ckInfoTips~checked then pageDialog~useInfoTips = .true
|
|
else pageDialog~useInfoTips = .false
|
|
|
|
|
|
/** onNewTab()
|
|
*
|
|
* This is the method we connected to the SELCHANGE event of the tab control.
|
|
*
|
|
* It is invoked when the user changes to a new tab using the tab control's
|
|
* interface. I.e. by clicking on a tab or by using the keyboard when the
|
|
* tab control has the focus.
|
|
*
|
|
* In general, there is usually a different control dialog for each page of
|
|
* the tab. In that usage, the programmer would hide the dialog of the old
|
|
* page and show the dialog of the new page. However, in this application we
|
|
* only use one control dialog. When the page is changed, we tell the page
|
|
* dialog to change the view style of the list view to produce the effect of
|
|
* changing pages.
|
|
*/
|
|
::method onNewTab unguarded
|
|
expose tabControl pageDialog
|
|
use arg ignore1, ignore2
|
|
|
|
pageDialog~refreshView(tabControl~selectedIndex + 1)
|
|
self~checkButtons
|
|
|
|
|
|
/** onForward()
|
|
*
|
|
* The event handle for the 'Forward' button's CLICKED event. We select the
|
|
* next tab in the tab control and then force the page to update by calling the
|
|
* 'onNewTab' method ourselfs.
|
|
*
|
|
* Note that we do not need to check that the selected index plus 1 is valid
|
|
* because the Forward button is disabled when we are on the last page.
|
|
*/
|
|
::method onForward
|
|
expose tabControl
|
|
|
|
tabControl~selectIndex(tabControl~selectedIndex + 1)
|
|
self~onNewTab
|
|
|
|
|
|
/** onBackward()
|
|
*
|
|
* The event handle for the 'Backward' button's CLICKED event. Coments are the
|
|
* same as for the onForward() method above.
|
|
*/
|
|
::method onBackward
|
|
expose tabControl
|
|
|
|
tabControl~selectIndex(tabControl~selectedIndex - 1)
|
|
self~onNewTab
|
|
|
|
|
|
/** onAdd()
|
|
*
|
|
* The event handle for the 'Add' button's CLICKED event. Here we display the
|
|
* address dialog which allows the user add a new record, which in turn will
|
|
* show up as a new item in the list-view.
|
|
*/
|
|
::method onAdd unguarded
|
|
expose pageDialog sd
|
|
|
|
dlg = .AddressDialog~new(sd'rc\oodListViews.rc', IDD_ADDRESS)
|
|
|
|
if dlg~initCode = 0 then do
|
|
if dlg~execute("SHOWTOP") == self~IDOK then do
|
|
|
|
rec = .directory~new
|
|
rec~FirstName = dlg~IDC_EDIT_FNAME
|
|
rec~Lastname = dlg~IDC_EDIT_LNAME
|
|
rec~Street = dlg~IDC_EDIT_STREET
|
|
rec~City = dlg~IDC_EDIT_CITY
|
|
rec~State = dlg~IDC_EDIT_STATE
|
|
rec~ZipCode = dlg~IDC_EDIT_ZIPCODE
|
|
rec~Age = dlg~IDC_EDIT_AGE
|
|
rec~isEditable = dlg~IDC_CHK_EDITABLE
|
|
|
|
if dlg~IDC_RB_MALE = 1 then rec~Sex = "M"
|
|
else rec~Sex = "F"
|
|
|
|
-- Have the page dialog add the record to the list-view.
|
|
pageDialog~addRecord(rec)
|
|
end
|
|
end
|
|
|
|
|
|
/** onEdit()
|
|
*
|
|
* The event handle for the 'Edit' button's CLICKED event. Here we display the
|
|
* address dialog, but set its mode to edit, which allows the user to edit an
|
|
* existing record.
|
|
*
|
|
* The address dialog is set in edit mode by passing a record into init(). If
|
|
* the address dialog is ended with okay, then we update the record with the
|
|
* values the user entered and have the page dialog update the list-view item.
|
|
*/
|
|
::method onEdit unguarded
|
|
expose pageDialog sd
|
|
|
|
rec = pageDialog~getSelectedRecord
|
|
|
|
dlg = .AddressDialog~new(sd'rc\oodListViews.rc', IDD_ADDRESS, rec)
|
|
|
|
if dlg~initCode = 0 then do
|
|
if dlg~execute("SHOWTOP") == self~IDOK then do
|
|
|
|
rec~FirstName = dlg~IDC_EDIT_FNAME
|
|
rec~Lastname = dlg~IDC_EDIT_LNAME
|
|
rec~Street = dlg~IDC_EDIT_STREET
|
|
rec~City = dlg~IDC_EDIT_CITY
|
|
rec~State = dlg~IDC_EDIT_STATE
|
|
rec~ZipCode = dlg~IDC_EDIT_ZIPCODE
|
|
rec~Age = dlg~IDC_EDIT_AGE
|
|
rec~isEditable = dlg~IDC_CHK_EDITABLE
|
|
|
|
if dlg~IDC_RB_MALE = 1 then rec~Sex = "M"
|
|
else rec~Sex = "F"
|
|
|
|
-- Have the page dialog refresh, update, the item.
|
|
pageDialog~refreshSelectedItem
|
|
end
|
|
end
|
|
|
|
|
|
/** cancel()
|
|
*
|
|
* The user can not end a ControlDialog, so we need to end the page dialog
|
|
* here during cancel. Passing .false to endExecution() says the user
|
|
* canceled the main dialog.
|
|
*/
|
|
::method cancel
|
|
expose pageDialog
|
|
|
|
-- Always use endExecution() to close a ControlDialog.
|
|
pageDialog~endExecution(.false)
|
|
return self~cancel:super
|
|
|
|
|
|
/** ok()
|
|
*
|
|
* The user can not end a ControlDialog, so we need to end the page dialog
|
|
* here during ok. Passing .true to endExecution() says the user closed the
|
|
* main dialog with ok.
|
|
*/
|
|
::method ok
|
|
expose pageDialog
|
|
|
|
-- Always use endExecution() to close a ControlDialog.
|
|
pageDialog~endExecution(.true)
|
|
return self~ok:super
|
|
|
|
|
|
/** initAutoDetection()
|
|
*
|
|
* We turn off auto detection for this dialog.
|
|
*/
|
|
::method initAutoDetection
|
|
self~noAutoDetection
|
|
|
|
/** checkButtons()
|
|
*
|
|
* Enables or disables the forwards / backwards buttons to fit the current
|
|
* page. I.e., if we are on the first page, the backwards button should be
|
|
* disabled, etc..
|
|
*/
|
|
::method checkButtons private
|
|
expose tabControl pbForward pbBackward
|
|
|
|
index = tabControl~selectedIndex + 1
|
|
|
|
if index == 1 then do
|
|
pbBackward~disable
|
|
pbForward~enable
|
|
end
|
|
else if index == 4 then do
|
|
pbBackward~enable
|
|
pbForward~disable
|
|
end
|
|
else do
|
|
pbBackward~enable
|
|
pbForward~enable
|
|
end
|
|
|
|
|
|
/** createImageLists()
|
|
*
|
|
* Create the small and large icon image lists. These will be used in the
|
|
* report, small icon, and icon views of the list-view. The small icon image
|
|
* list is used by both the report and the small icon views, normal icon image
|
|
* list is used by the icon view.
|
|
*
|
|
* In this program we create the initial records and image lists here, in the
|
|
* main dialog, and then pass the objects to the page dialog. The creation
|
|
* could just as well have been done in the page dialog itself. The example
|
|
* program does it this way to try and show that how you use a ControlDialog is
|
|
* very flexible.
|
|
*/
|
|
::method createImageLists private
|
|
expose smallIcons normalIcons sd
|
|
|
|
small = .Image~getImage(sd"rc\oodListViews1.bmp")
|
|
tmpIL = .ImageList~create(.Size~new(16, 12), COLOR4, 4, 0)
|
|
if \small~isNull, \tmpIL~isNull then do
|
|
tmpIL~add(small)
|
|
small~release
|
|
smallIcons = tmpIL
|
|
end
|
|
else do
|
|
smallIcons = .nil
|
|
end
|
|
|
|
normal = .Image~getImage(sd"rc\oodListViews2.bmp")
|
|
tmpIL = .ImageList~create(.Size~new(32, 32), COLOR4, 4, 0)
|
|
if \normal~isNull, \tmpIL~isNull then do
|
|
tmpIL~add(normal)
|
|
normal~release
|
|
normalIcons = tmpIL
|
|
end
|
|
else do
|
|
normalIcons = .nil
|
|
end
|
|
|
|
|
|
/** initRecords()
|
|
*
|
|
* Create 3 records to start with. The records could be pulled from a database.
|
|
* Here we just fake it. Each 'record' is put into an array and then the array
|
|
* is passed to the page dialog where is record is used to insert a single item
|
|
* into the list-view.
|
|
*/
|
|
::method initRecords private
|
|
expose records
|
|
|
|
records = .array~new(3)
|
|
|
|
rec = .directory~new
|
|
rec~FirstName = "Mike"
|
|
rec~Lastname = "Miller"
|
|
rec~Street = "432 Fifth Ave"
|
|
rec~City = "New York"
|
|
rec~State = "NY"
|
|
rec~ZipCode = "12897"
|
|
rec~Age = "35"
|
|
rec~Sex = "M"
|
|
rec~IsEditable = .false
|
|
records[1] = rec
|
|
|
|
rec = .directory~new
|
|
rec~FirstName = "Sue"
|
|
rec~Lastname = "Thaxton"
|
|
rec~Street = "5179 Bellevue St"
|
|
rec~City = "Tucson"
|
|
rec~State = "AZ"
|
|
rec~ZipCode = "87340"
|
|
rec~Age = "30"
|
|
rec~Sex = "F"
|
|
rec~IsEditable = .true
|
|
records[2] = rec
|
|
|
|
rec = .directory~new
|
|
rec~FirstName = "Dave"
|
|
rec~Lastname = "Hewitt"
|
|
rec~Street = "4932 Texas St"
|
|
rec~City = "San Diego"
|
|
rec~State = "CA"
|
|
rec~ZipCode = "92110"
|
|
rec~Age = "49"
|
|
rec~Sex = "M"
|
|
rec~IsEditable = .true
|
|
records[3] = rec
|
|
|
|
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
|
|
PageDialog Class
|
|
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
|
::class 'PageDialog' subclass RcControlDialog
|
|
|
|
-- This attribute allows the parent dialog to notify us if the Use Info Tips
|
|
-- check box is checked or not. When it is not checked, we don't display any
|
|
-- info tips.
|
|
::attribute useInfoTips
|
|
|
|
::method initialize
|
|
expose smallIcons normalIcons records haveSelection pbEdit
|
|
use strict arg smallIcons, normalIcons, records
|
|
|
|
self~connectListViewEvent(IDC_LISTVIEW, "COLUMNCLICK")
|
|
self~connectListViewEvent(IDC_LISTVIEW, "ACTIVATE", "onActivate", .true)
|
|
self~connectListViewEvent(IDC_LISTVIEW, "DBLCLK", "onDoubleClick", .true)
|
|
self~connectListViewEvent(IDC_LISTVIEW, "BEGINDRAG", "DefListDragHandler")
|
|
self~connectListViewEvent(IDC_LISTVIEW, "BEGINEDIT", "onBeginEdit", .true)
|
|
self~connectListViewEvent(IDC_LISTVIEW, "ENDEDIT", , .true)
|
|
self~connectListViewEvent(IDC_LISTVIEW, "GETINFOTIP")
|
|
self~connectListViewEvent(IDC_LISTVIEW, "SELECTCHANGED")
|
|
|
|
self~initUpdateListView(IDC_LISTVIEW)
|
|
|
|
haveSelection = .false
|
|
pbEdit = self~ownerDialog~newPushButton(IDC_PB_EDITRECORD)
|
|
|
|
::method initDialog
|
|
expose lv smallIcons normalIcons records ckInfoTips useInfoTips
|
|
|
|
lv = self~newListView(IDC_LISTVIEW)
|
|
|
|
if smallIcons <> .nil then lv~setImageList(smallIcons, SMALL)
|
|
if normalIcons <> .nil then lv~setImageList(normalIcons, NORMAL)
|
|
|
|
lv~insertColumn(0, "Name", 50)
|
|
lv~insertColumn(1, "Street", 60)
|
|
lv~insertColumn(2, "City", 50)
|
|
lv~insertColumn(3, "State", 20)
|
|
lv~insertColumn(4, "Zip Code", 30)
|
|
lv~insertColumn(5, "Age", 20)
|
|
|
|
do r over records
|
|
self~addRecord(r, .false)
|
|
end
|
|
|
|
lv~addExtendedStyle("FULLROWSELECT DOUBLEBUFFER GRIDLINES INFOTIP")
|
|
|
|
|
|
/** refreshView()
|
|
*
|
|
* Invoked by the main dialog when the user has switched to a new tab in the tab
|
|
* control.
|
|
*
|
|
* Typically an application uses a different dialog for each page of a tab
|
|
* control. In this example program however, the different pages just show the
|
|
* different view types of a list-view. Therefore the same dialog, (and the same
|
|
* list-view,) are used for each page. When a new page is to be displayed,
|
|
* rather than switching to a different dialog, the current view of the list-
|
|
* view is simply changed to the correct view for that page.
|
|
*/
|
|
::method refreshView unguarded
|
|
expose lv
|
|
use strict arg index
|
|
|
|
select
|
|
when index == 1 then lv~setView("LIST")
|
|
when index == 2 then lv~setView("REPORT")
|
|
when index == 3 then lv~setView("ICON")
|
|
when index == 4 then lv~setView("SMALLICON")
|
|
otherwise return .false
|
|
end
|
|
-- End select
|
|
|
|
return .true
|
|
|
|
|
|
/** onBeginEdit()
|
|
*
|
|
* The event handler for the label begin edit event, invoked when the user
|
|
* initiates a label editing operation.
|
|
*
|
|
* When the label editing is initiated, the operating system creates and
|
|
* postitions a edit control, but doesn't show it. The system then sends the
|
|
* label begin edit notification. Here we have access to the edit control,
|
|
* which could be customized by using the typical methods of the edit object.
|
|
*
|
|
* In addition, the edit operation can be vetoed by returning false. I.e.,
|
|
* return true to allow the editing, and false to disallow it. To demonstrate
|
|
* this, we check if the record is editable and if it is not we disallow the
|
|
* label editing operation by returning false from this event handler.
|
|
*/
|
|
::method onBeginEdit unguarded
|
|
use arg id, itemIndex, editCtrl, listViewCtrl
|
|
|
|
rec = listViewCtrl~getItemData(itemIndex)
|
|
if rec~isEditable then return .true
|
|
|
|
reply .false
|
|
|
|
msg = "The record for" rec~FirstName rec~LastName 'can not be changed.'
|
|
title = "Label Edit Error"
|
|
j = MessageDialog(msg, self~hwnd, title, , "WARNING")
|
|
|
|
return
|
|
|
|
|
|
/** onEndEdit()
|
|
*
|
|
* This is the event handler for the end label edit notification. It is invoked
|
|
* when the user ends the label editing operation. If the user canceled the
|
|
* operation, then the 'text' argument will be the .nil object. Otherwise it
|
|
* will be the text the user entered.
|
|
*
|
|
* We use this event to validate the label to a degree and to update the record
|
|
* for the item when the user edits the label.
|
|
*/
|
|
::method onEndEdit unguarded
|
|
use arg id, itemIndex, text, listViewCtrl
|
|
|
|
if text == .nil then return .false
|
|
|
|
if text~words == 2 & text~word(1)~right(1) == ',' then do
|
|
reply .true
|
|
|
|
rec = listViewCtrl~getItemData(itemIndex)
|
|
rec~FirstName = text~word(2)
|
|
rec~LastName = text~word(1)~strip('T', ',')
|
|
|
|
return
|
|
end
|
|
|
|
reply .false
|
|
|
|
msg = "The format for a record label must be" || .endOfLine || -
|
|
"last name, comma, first name. For" || .endOfLine || -
|
|
"example: Swift, Tom" || .endOfLine~copies(2) || -
|
|
"The change is rejected."
|
|
|
|
title = "Label Editing Error"
|
|
j = MessageDialog(msg, self~hwnd, title, , "WARNING")
|
|
|
|
return
|
|
|
|
|
|
/** onColumnClick()
|
|
*
|
|
* The event handler for a column click event, invoked when the user clicks on a
|
|
* column header when the list-view is in Report view.
|
|
*
|
|
* We use the event to demonstrate some of the list-view methods.
|
|
*/
|
|
::method onColumnClick unguarded
|
|
use arg id, column, listView
|
|
|
|
-- Get the current width, in pixels, of the column clicked and then set its
|
|
-- width to 10 pixels more.
|
|
listView~setColumnWidthPx(column, listView~columnWidthPx(column) + 10)
|
|
|
|
-- Display information about the column clicked.
|
|
d = .Directory~new
|
|
if listView~getColumnInfo(column, d) then do
|
|
tab = '09'x
|
|
msg = "Column Title:"tab d~text || .endOfLine || -
|
|
"Subitem index:"tab d~subitem || .endOfLine || -
|
|
"Column Width:"tab d~width || .endOfLine || -
|
|
"Allignment:"tab d~fmt || .endOfLine~copies(2) || -
|
|
"Note: each time you click on a column" || .endOfLine || -
|
|
"its width is increased."
|
|
j = MessageDialog(msg, self~hwnd, "Column Click Detected")
|
|
end
|
|
else do
|
|
msg = "An error ocurred getting the column information."
|
|
j = MessageDialog(msg, self~hwnd, "Windows API Error", "OK", "ERROR")
|
|
end
|
|
|
|
|
|
/** onDoubleClick()
|
|
*
|
|
* The event handler for a double clikc event, invoked when a list-view item is
|
|
* double clicked.
|
|
*
|
|
* We use the event to demonstrate:
|
|
*
|
|
* 1.) That a double click also activates an item and that the double click
|
|
* comes first. When you double click an item you will see a message box
|
|
* displaying the arguments to this method, the 'age increase' message box,
|
|
* and then a message box from the onActivate() method.
|
|
*
|
|
* 2.) Some of the list-view methods.
|
|
*
|
|
*/
|
|
::method onDoubleClick unguarded
|
|
use arg id, item, subitem, state, isSingleClick, listView
|
|
|
|
tab = '09'x
|
|
tab2 = tab~copies(2)
|
|
true = self~booleanToText(isSingleClick)
|
|
msg = 'onDoubleClick()' || .endOfLine~copies(2) || -
|
|
'id:'tab2 id || .endOfLine || -
|
|
'item:'tab2 item || .endOfLine || -
|
|
'subitem:'tab2 subitem || .endOfLine || -
|
|
'state:'tab2 state || .endOfLine || -
|
|
'single click:'tab true || .endOfLine || -
|
|
'listView:'tab2 listView
|
|
|
|
ret = MessageDialog(msg, self~hwnd, "onDoubleClick Method Invoked", OK, INFORMATION)
|
|
|
|
-- Get the index of the item with the focus, use the index to retrieve the
|
|
-- item information and the text associated with it
|
|
index = listView~focused
|
|
|
|
-- The item's information is returned in the .directory object passed in to
|
|
-- the getItemInfo() method.
|
|
d = .directory~new
|
|
listView~getItemInfo(index, d)
|
|
|
|
parse value d~text with lastName ', ' firstName
|
|
|
|
pronoun = 'his'
|
|
if d~image == 1 then pronoun = "her"
|
|
|
|
age = listView~itemText(index, 5)
|
|
|
|
msg = "You have double clicked on the item for" firstName lastName || "0d0a0d0a"x ,
|
|
"Should" pronoun "age be increased by 1?"
|
|
|
|
ret = MessageDialog(msg, self~hwnd, "Age Increment", YESNO, INFORMATION)
|
|
if ret == self~IDYES then do
|
|
age += 1
|
|
listView~setItemText(index, 5, age)
|
|
|
|
-- Now we need to update the age in the record for this item. We retrieve
|
|
-- the record from the user item data and update the age.
|
|
rec = listView~getItemData(index)
|
|
rec~age = age
|
|
end
|
|
|
|
-- Deselect the focused item and move the focus to the first item
|
|
listView~deselect(index)
|
|
listView~focus(0)
|
|
return 0
|
|
|
|
|
|
/** onActivate()
|
|
*
|
|
* The event handler for an activate event, invoked when a list-view item is
|
|
* activated.
|
|
*
|
|
* We use the event to demonstrate that a double click event also generates an
|
|
* activate event. We just print out the argument values here.
|
|
*/
|
|
::method onActivate unguarded
|
|
use arg id, ptr, nCode, listView
|
|
|
|
tab = '09'x
|
|
msg = 'onActivate()' || .endOfLine~copies(2) || -
|
|
'id:'tab id || .endOfLine || -
|
|
'ptr:'tab ptr || .endOfLine || -
|
|
'nCode:'tab nCode || .endOfLine || -
|
|
'listView:'tab listView
|
|
|
|
ret = MessageDialog(msg, self~hwnd, "onActivate Method Invoked", OK, INFORMATION)
|
|
|
|
return 0
|
|
|
|
|
|
/** onGetInfoTip()
|
|
*
|
|
* This is the event handler for the INFOTIP event. This method is invoked when
|
|
* the list-view wants the text for an info tip.
|
|
*
|
|
* If this method returns the empty string then no info tip will be shown.
|
|
* Otherwise, the info tip will contain the text sent back.
|
|
*
|
|
* The maxLen argument will be the maximum length allowed for the returned text.
|
|
* You should never assume what this length is, although it appears to usually
|
|
* be 1023. If the text sent back is longer than maxLen, it will automatically
|
|
* be truncated.
|
|
*
|
|
* Note that the original versions of this example did not use the user item
|
|
* data feature of the list-view. Instead they trackd the records through an
|
|
* array. When this example was updated to work with the user item data
|
|
* feature, this method was left as is, to show more than one way of associating
|
|
* a record with a list-view item.
|
|
*/
|
|
::method onGetInfoTip unguarded
|
|
expose lv records useInfoTips
|
|
use arg id, item, text, maxLen
|
|
|
|
text = ''
|
|
|
|
if useInfoTips then do
|
|
r = records[item + 1]
|
|
text = r~firstName r~lastName '('r~age')' || .endOfLine || -
|
|
r~street || .endOfLine || -
|
|
r~city',' r~state r~zipcode
|
|
end
|
|
|
|
return text
|
|
|
|
|
|
/** onSelectionChanged()
|
|
*
|
|
* This is the event handler for the selection changed event. It is invoked
|
|
* each time the selection state of an item changes. We use this event to
|
|
* enable or disable the Edit Record push button. The button is only enabled
|
|
* when an item is selected, and that item is an editable record. When no item
|
|
* is selected, or if the selected item is not editable, then the push button is
|
|
* disabled.
|
|
*
|
|
* The event happens each time a selected item is deselected and each time an
|
|
* unselected item is selected. This means that quite often when an item is
|
|
* selected there are two events, the old selected item losing its selection and
|
|
* the newly selected item gaining the selection.
|
|
*/
|
|
::method onSelectChanged unguarded
|
|
expose haveSelection pbEdit
|
|
use arg id, itemIndex, state, lv
|
|
|
|
if state == 'SELECTED' then haveSelection = .true
|
|
else if state == 'UNSELECTED' & lv~selected == -1 then haveSelection = .false
|
|
|
|
if \ haveSelection then do
|
|
pbEdit~disable
|
|
end
|
|
else if state == 'SELECTED' then do
|
|
-- We have a selection and an item was just selected, see if it is editable:
|
|
rec = lv~getItemData(itemIndex)
|
|
if rec~isEditable then pbEdit~enable
|
|
else pbEdit~disable
|
|
end
|
|
|
|
|
|
::method booleanToText unguarded private
|
|
use strict arg boolean
|
|
if boolean == .true then return 'true'
|
|
else if boolean == .false then return 'false'
|
|
else return 'not a boolean'
|
|
|
|
|
|
/** addRecord()
|
|
*
|
|
* Used to add an item to the list view.
|
|
*
|
|
* Note that it does not matter what view the list-view is in. When the list-
|
|
* view is in report view, then all the information will be visible to the user.
|
|
* When it is in other views, only some of the information is visible to the
|
|
* user.
|
|
*/
|
|
::method addRecord unguarded
|
|
expose lv records counter
|
|
use strict arg rec, newRecord = .true
|
|
|
|
iconSex = 0
|
|
if rec~sex = "F" then iconSex = 1
|
|
|
|
index = lv~addRow(, iconSex, rec~lastName', 'rec~firstName, rec~street, rec~city, rec~state, rec~ZipCode, rec~age)
|
|
lv~setItemData(index, rec)
|
|
|
|
if newRecord then records~append(rec)
|
|
|
|
|
|
/** getSelectedRecord()
|
|
*
|
|
* The owner dialog, the top-level dialog, invokes this method to get access to
|
|
* the data record of the currently selected item.
|
|
*
|
|
* Because the program only enables the 'Edit Record' button when an item that
|
|
* is editable is selected, we do not need to do a lot of checks here. We
|
|
* simply get the selected index, get the user item data for that index, and
|
|
* send the record back.
|
|
*/
|
|
::method getSelectedRecord
|
|
expose lv
|
|
return lv~getItemData(lv~selected)
|
|
|
|
|
|
/** refreshSelectedRecord()
|
|
*
|
|
* This method is invoked by the owner dialog to notify us that the data in a
|
|
* record has been changed. We simply get the record for the selected item and
|
|
* modify the item with the record data.
|
|
*
|
|
* Because of the way the program is structured, there must be a selected item.
|
|
* We check anyway that the selected index is value
|
|
*/
|
|
::method refreshSelectedItem
|
|
expose lv pbEdit
|
|
|
|
index = lv~selected
|
|
if index == -1 then return .false
|
|
|
|
rec = lv~getItemData(index)
|
|
|
|
iconSex = 0
|
|
if rec~sex = "F" then iconSex = 1
|
|
|
|
lv~modify(index, 0, rec~lastName', 'rec~firstName, iconSex)
|
|
lv~modify(index, 1, rec~street)
|
|
lv~modify(index, 2, rec~city)
|
|
lv~modify(index, 3, rec~state)
|
|
lv~modify(index, 4, rec~zipCode)
|
|
lv~modify(index, 5, rec~age)
|
|
|
|
-- The is editable status of the record could have changed.
|
|
if rec~isEditable then pbEdit~enable
|
|
else pbEdit~disable
|
|
|
|
return .true
|
|
|
|
|
|
/** leaving()
|
|
*
|
|
* The ooDialog framework invokes the leaving method automatically when a dialog
|
|
* is being ended. The default implementation does nothing. The method is
|
|
* intended to be over-ridden by the programmer, if desired, to provide the
|
|
* proper place to clean up resources.
|
|
*
|
|
* We use it here to release the image lists resources. This is not needed in
|
|
* this stand alone program. When the interpreter process ends, (which will
|
|
* happen now that the dialog is ending,) the operating system will free all the
|
|
* resources. The example program does this because ... because it is an
|
|
* example and this makes a good place to explain about the leaving() method.
|
|
*/
|
|
::method leaving
|
|
expose smallIcons normalIcons
|
|
if smallIcons \== .nil then smallIcons~release
|
|
if normalIcons \== .nil then normalIcons~release
|
|
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
|
|
AddressDialog Class
|
|
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
|
::class 'AddressDialog' subclass RcDialog
|
|
|
|
::method init
|
|
expose rec
|
|
self~init:super(arg(1), arg(2))
|
|
|
|
if self~initCode <> 0 then return self~initCode
|
|
|
|
if arg(3, 'E') then rec = arg(3)
|
|
else rec = .nil
|
|
|
|
-- Initialize the data attributes. These attributes are added automatically
|
|
-- to this dialog by the ooDialog framework. The attributes are used to
|
|
-- reflect the state of the underlying dialog controls.
|
|
--
|
|
-- If we have a record, we are in edit mode, otherwise we are in add mode.
|
|
if rec == .nil then do
|
|
self~IDC_RB_MALE = 1
|
|
self~IDC_RB_FEMALE = 0
|
|
self~IDC_EDIT_FNAME = ''
|
|
self~IDC_EDIT_LNAME = ''
|
|
self~IDC_EDIT_STREET = ''
|
|
self~IDC_EDIT_CITY = ''
|
|
self~IDC_EDIT_STATE = ''
|
|
self~IDC_EDIT_ZIPCODE = ''
|
|
self~IDC_EDIT_AGE = ''
|
|
self~IDC_CHK_EDITABLE = 1
|
|
end
|
|
else do
|
|
if rec~Sex == 'M' then do
|
|
self~IDC_RB_MALE = 1
|
|
self~IDC_RB_FEMALE = 0
|
|
end
|
|
else do
|
|
self~IDC_RB_MALE = 0
|
|
self~IDC_RB_FEMALE = 1
|
|
end
|
|
self~IDC_EDIT_FNAME = rec~FirstName
|
|
self~IDC_EDIT_LNAME = rec~Lastname
|
|
self~IDC_EDIT_STREET = rec~Street
|
|
self~IDC_EDIT_CITY = rec~City
|
|
self~IDC_EDIT_STATE = rec~State
|
|
self~IDC_EDIT_ZIPCODE = rec~ZipCode
|
|
self~IDC_EDIT_AGE = rec~Age
|
|
self~IDC_CHK_EDITABLE = rec~isEditable
|
|
end
|
|
|
|
return self~initCode
|
|
|
|
|
|
::method initDialog
|
|
|
|
-- To help with data validation, restrict the number of characters the user
|
|
-- can type into these edit controls. Note that in the resource script, the
|
|
-- zip code and age edit controls are defined as numeric only. The state edit
|
|
-- control is defined as upper-case only.
|
|
self~newEdit(IDC_EDIT_STATE)~setLimit(2)
|
|
self~newEdit(IDC_EDIT_ZIPCODE)~setLimit(5)
|
|
self~newEdit(IDC_EDIT_AGE)~setLimit(3)
|
|
|
|
|
|
/** validate()
|
|
*
|
|
* The validate() method is invoked automatically by the ooDialog framework when
|
|
* the user closes a dialog with 'ok'. The default implementation simply
|
|
* returns true. The method is meant to be over-ridden by the programmer, if
|
|
* desired, to validate the user input is correct. The method must return .true
|
|
* or .false. When .true is returned, the dialog continues to close. If .false
|
|
* is returned, the dialog will not be closed.
|
|
*
|
|
* Here we do a bare minimum of validation. We just check that something has
|
|
* been entered for the first and last names.
|
|
*/
|
|
::method validate
|
|
|
|
if self~namesEnteredOkay then return .true
|
|
|
|
-- It's not very user friendly to prevent the user from closing the dialog
|
|
-- without letting him know why.
|
|
msg = "The last and first names must be specified for each new record."
|
|
title = "New Record Input Error"
|
|
j = MessageDialog(msg, self~hwnd, title, , "WARNING")
|
|
|
|
-- Place the focus on the first edit control that has invalid data:
|
|
if self~newEdit(IDC_EDIT_FNAME)~getText~strip == "" then
|
|
self~focusControl(IDC_EDIT_FNAME)
|
|
else
|
|
self~focusControl(IDC_EDIT_LNAME)
|
|
|
|
return .false
|
|
|
|
|
|
/* Simple convenience function to make validate() more readable */
|
|
::method namesEnteredOkay private
|
|
if self~newEdit(IDC_EDIT_LNAME)~getText~strip == "" then return .false
|
|
if self~newEdit(IDC_EDIT_FNAME)~getText~strip == "" then return .false
|
|
return .true
|