727 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Rexx
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			727 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Rexx
		
	
	
		
			Executable File
		
	
	
	
	
| /*----------------------------------------------------------------------------*/
 | |
| /*                                                                            */
 | |
| /* 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.               */
 | |
| /*                                                                            */
 | |
| /*----------------------------------------------------------------------------*/
 | |
| 
 | |
| /* A demonstration of some of the Menu features in ooDialog.
 | |
|  *
 | |
|  * This example focuses on context menus.  Two different context menus are used
 | |
|  * in this example, showing 2 different ways to create the context menus.  One
 | |
|  * of the context menus is shown when the user right-clicks on the list view
 | |
|  * control.  The other context menu is shown when the user right-clicks on any
 | |
|  * other place on the dialog.  The first menu is specific to the list view, the
 | |
|  * other menu is specific to the dialog.
 | |
|  *
 | |
|  * The list view menu is created by loading a menu from a resource script file.
 | |
|  * Menus created from a resouce script are always menu bars.  In this example,
 | |
|  * the menu bar is *not* attached to the dialog, rather it is simply used as the
 | |
|  * source of the list view context menu.
 | |
|  *
 | |
|  * The second context menu is created dynamically using methods of the PopupMenu
 | |
|  * class.
 | |
|  */
 | |
| 
 | |
|   -- Locate the directory our source code is in.  Then this program can be
 | |
|   -- executed from anywhere.  It will work if the program is dragged and dropped
 | |
|   -- on ooDialog.exe for instance.
 | |
|   srcDir = locate()
 | |
| 
 | |
|   -- When using symbolic IDs with menus, as this program does, the programmer
 | |
|   -- *must* use the global constant directory, (.constDir,) rather than the
 | |
|   -- constDir attribute of a dialog.  Menus are independent of dialogs.
 | |
|   --
 | |
|   -- The use of the global constant directory is controlled by the .Application
 | |
|   -- object.  The .Application object is also used to set other application-wide
 | |
|   -- defaults or values.  In this program, we also want to make the default for
 | |
|   -- automatic data detection to be off.  Both of these requirements can be done
 | |
|   -- with one method call to the .Application object.  As a convenience, the
 | |
|   -- setDefaults() method will also populate the .constDir with symbols.
 | |
|   --
 | |
|   -- In this invokcation, the application is set to use the .constDir only, "O",
 | |
|   -- symbols are loaded into the .constDir from the file: ContextMenu.h, and the
 | |
|   -- default for automatic data detection is set to off (.false.)  Note that we
 | |
|   -- create a complete path name for ContextMenu.h
 | |
|   .application~setDefaults("O", srcDir'ContextMenu.h', .false)
 | |
| 
 | |
|   -- The folloing demonstrates that menu objects are distinct from dialogs.
 | |
|   --
 | |
|   -- You can create a menu completely indepedent of a dialog.  Note that at this
 | |
|   -- point in the program, there is no dialog object at all.  Once created, the
 | |
|   -- menu object can then be used where ever it is convenient.
 | |
|   --
 | |
|   -- The code for creating the list view menu is put in a separate function so
 | |
|   -- that people can focus on only one thing at a time.  The function is at the
 | |
|   -- end of this file.  In createListViewMenu(), the context menu is created
 | |
|   -- dynamically and passed back to us.
 | |
|   contextMenu = createListViewMenu()
 | |
| 
 | |
|   -- Create a RcDialog as normally done.
 | |
|   dlg = .ContextMenuDlg~new(srcDir"ContextMenu.rc", IDD_CONTEXT)
 | |
| 
 | |
|   -- putMenu() is a method added to our dialog class.  It is used to pass the
 | |
|   -- context menu object to the dialog.  There are any number of ways to
 | |
|   -- accomplish this.  For instance the menu object could have been passed in
 | |
|   -- as an argument to new() above.
 | |
|   dlg~putMenu(contextMenu)
 | |
| 
 | |
|   -- Excute the dialog as usual.
 | |
|   if dlg~initCode = 0 then do
 | |
|     dlg~execute("SHOWTOP")
 | |
|   end
 | |
|   else do
 | |
|     say 'Error creating the context menu dialog initCode:' dlg~initCode
 | |
|     return 99
 | |
|   end
 | |
| 
 | |
| return 0
 | |
| -- End of entry point.
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
 | |
|   Directives, Classes, or Routines.
 | |
| \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
 | |
| ::requires "ooDialog.cls"
 | |
| 
 | |
| ::class 'ContextMenuDlg' subclass RcDialog
 | |
| 
 | |
| /** init()
 | |
|  *
 | |
|  * The init() method is invoked for every object instantiation.  This is part of
 | |
|  * ooRexx and has nothing to do with ooDialog, except for this:
 | |
|  *
 | |
|  * ooDialog programming is primarily based on subclassing the dialog classes
 | |
|  * provided by the ooDialog framework.  If you over-ride the init() method, you
 | |
|  * *must* initialize the base class, with the proper arguments for the base
 | |
|  * class, before you invoke any methods of the base class.
 | |
|  *
 | |
|  * Here with create the second context menu and create some arrays that contain
 | |
|  * the list view items.  Both of these tasks do not need to be done in this
 | |
|  * method, they could be done elsewhere.
 | |
|  */
 | |
| ::method init
 | |
|   expose dlgPopup
 | |
|   use arg rcScript, dlgID
 | |
|   forward class (super) continue
 | |
| 
 | |
|   -- A menu bar can be loaded from a resource script file using the
 | |
|   -- .ScriptMenuBar class.
 | |
|   --
 | |
|   -- The first argument to init(), rcScript, is the .rc file that contains the
 | |
|   -- dialog template.  That .rc file also contains the menu template for the
 | |
|   -- dialog context menu.  In the new method of the .ScriptMenuBar the .rc file
 | |
|   -- is parsed and the  menu with symbolic resource id of: IDM_RC_DLG is loaded
 | |
|   -- into memory.
 | |
|   menuBar = .ScriptMenuBar~new(rcScript, IDM_RC_DLG)
 | |
| 
 | |
|   -- If an error occurs in a menu object initialization, then a condition is
 | |
|   -- always raised.  menuBar will never be 0
 | |
|   if menuBar == 0 then do
 | |
|     -- This code will never execute because if the menu bar was not created, a
 | |
|     -- condition was raised.
 | |
|     say 'ERROR: menubar shold be good, not 0.'
 | |
|     say 'A condition should have been raised.'
 | |
|     say 'SystemErrorCode:' .SystemErrorCode SysGetErrortext(.SystemErrorCode)
 | |
|     self~initCode = 1
 | |
|     return
 | |
|   end
 | |
| 
 | |
|   -- A menu bar is made up of a number of pop up menus.  You can get one of
 | |
|   -- those pop up menus by specifying either its resource ID or its position.
 | |
|   --
 | |
|   -- In this program, the menu bar itself is not used.  It contains only 1 pop
 | |
|   -- up menu, which is used as a context menu in this program.  Which pop up
 | |
|   -- menu to get is specified either by its resource ID or by its position in
 | |
|   -- the menu.  Here, we get it by its position, which is 1.  Since both a
 | |
|   -- resource ID and a position are numbers, we need to denote whether we are
 | |
|   -- getting the menu by position or by resource ID.  The second argument is the
 | |
|   -- 'byPosition' argument.  If true, the first arugment is a position.  If
 | |
|   -- false, the first argument is a resource ID.  The default is .false.
 | |
|   dlgPopup = menubar~getPopup(1, .true)
 | |
| 
 | |
|   -- Start with the menu item to enable the list view greyed out.
 | |
|   dlgPopup~disable(IDM_RC_ENABLE_LV)
 | |
| 
 | |
|   -- Create an array of items for the list view control.  This could be done in
 | |
|   -- initDialog(), or anywhere convenient.
 | |
|   self~createArrays()
 | |
| 
 | |
| /** initDialog()
 | |
|  *
 | |
|  * initDialog() is invoked automatically by the dialog framework as soon as the
 | |
|  * underlying Windows dialog is created.  This is the place to do any
 | |
|  * initialization of the dialog controls, initialization that requires the
 | |
|  * underlying dialog or dialog control to exist.
 | |
|  *
 | |
|  * We would not necessarily need to make the menu connections here.  However,
 | |
|  * as you will see in makeMenuConnections() method, we are using the window
 | |
|  * handles of the dialog and the list view to filter the context menu
 | |
|  * notifications.  This does require that the underlying dialog and controls do
 | |
|  * exist.
 | |
|  */
 | |
| ::method initDialog
 | |
| 
 | |
|   self~makeMenuConnections
 | |
|   self~initListView
 | |
| 
 | |
| 
 | |
| /** makeMenuConnections()
 | |
|  *
 | |
|  * Connects the menu related events to event handling methods in this dialog.
 | |
|  *
 | |
|  * There are 2 types of connections made here.
 | |
|  *
 | |
|  * 1.) The context menu event is connected.  The context menu event is generated
 | |
|  * when ever the user right clicks on the dialog, types the shift-F10 keyboard
 | |
|  * combination, or the VK_APPS key (Natural keyboard.)  The context menu event
 | |
|  * needs to be connected to a method that shows the context menu.
 | |
|  *
 | |
|  * 2.) The menu item command events are connected.  A menu item command event is
 | |
|  * generated when the user selects a menu item.
 | |
|  */
 | |
| ::method makeMenuConnections private
 | |
|   expose lvPopup dlgPopup
 | |
| 
 | |
|   -- Connect a click on a menu item in the short cut menus to this dialog.
 | |
|   --
 | |
|   -- For the dialog popup menu, we connect all menu items to the same method,
 | |
|   -- onMenuCommand().  onMenuCommand() will recieve the resource ID of the menu
 | |
|   -- item selected, and we use that to decide what action to take.
 | |
|   if \ dlgPopup~connectAllCommandEvents(onMenuCommand, self) then do
 | |
|     say 'Error conecting menu items. SystemErrorCode:' || -
 | |
|     .SystemErrorCode SysGetErrortext(.SystemErrorCode)
 | |
|   end
 | |
| 
 | |
|   -- To understand this explanation, note this point: although menu objects are
 | |
|   -- independent of dialog objects, at the time a menu is shown, the operating
 | |
|   -- system requires that the underlying menu be assigned to an underlying
 | |
|   -- dialog.  For menu bars, the assigned dialog is the dialog the menu bar is
 | |
|   -- attached to.  For pop up menus, the menu could be assigned to a dialog each
 | |
|   -- time the context menu is shown.  But, it is usually more convenient to just
 | |
|   -- assign the menu to a dialog one time.  That is what the assignTo() method
 | |
|   -- does.
 | |
|   --
 | |
|   -- For the list view pop up menu, we connect each menu item to its own method.
 | |
|   -- There are menu methods to connect individual menu items.  But there are
 | |
|   -- also a number of convenience methods to connect a number of menu items at
 | |
|   -- one time.
 | |
|   --
 | |
|   -- The second optional arguement in assignTo() turns on automatic menu item
 | |
|   -- connection.  The default is off.  The third optional argument is the name
 | |
|   -- of a method to connect all the items to.  So, you can either connect each
 | |
|   -- menu item to its own method, (omit the third argument,) or you can connect
 | |
|   -- all menu items to one method.  When each menu item is connected to its own
 | |
|   -- method, the method name is constructed by the ooDialog framework using the
 | |
|   -- text of the menu item.
 | |
|   --
 | |
|   -- For example, if the menu item text is 'Blow your own horn' then the method
 | |
|   -- name will be blowYourOwnHorn().
 | |
|   --
 | |
|   -- This is what we do here, assign the list view menu to this dialog, and at
 | |
|   -- the same time connect all the menu item comand events to an individual
 | |
|   -- method.
 | |
|   if \ lvPopup~assignTo(self, .true) then do
 | |
|     say 'Error conecting menu items. SystemErrorCode:' || -
 | |
|     .SystemErrorCode SysGetErrortext(.SystemErrorCode)
 | |
|   end
 | |
|   say
 | |
| 
 | |
|   -- Next we connect the context menu message (right mouse click) with two
 | |
|   -- methods.  If you supply a window handle as the second, optional, parameter,
 | |
|   -- then the event notifications are filtered by that window handle.  In this
 | |
|   -- way you can easily show a context menu specific to a certain control.  If
 | |
|   -- you do not use a window handle to filter the event notifications, you can
 | |
|   -- still do the same thing, you just need to determine the mouse position at
 | |
|   -- the time of the event and relate that position to the context menu you
 | |
|   -- want to show
 | |
|   --
 | |
|   -- Here we send all right-clicks on the list-view control to one method and
 | |
|   -- all clicks on the dialog to a second method.
 | |
|   if \ lvPopup~connectContextMenu(onListViewContext, self~newListView(IDC_LV)~hwnd) then do
 | |
|     say 'Error conecting context menu. SystemErrorCode:' || -
 | |
|     .SystemErrorCode SysGetErrortext(.SystemErrorCode)
 | |
|   end
 | |
| 
 | |
|   -- If we only use the first connectContextMenu(), then right clicks on the Ok
 | |
|   -- and cancel buttons are ignored.  The shift-F10 and VK_APPS events are
 | |
|   -- also ignored when either of the buttons has the focus.
 | |
|   --
 | |
|   -- That's perfectly fine, but it depends on what you want.  Here, we pretend
 | |
|   -- that we don't want that behavior, and show how to prevent it.  Simply add
 | |
|   -- filtered connections for the Ok and Cancel push buttons.
 | |
|   --
 | |
|   -- You can play with this to see it works.  Comment out the third and forth
 | |
|   -- connections and then tab to the Ok or Cancel key and use Shift-F10.  There
 | |
|   -- will be no context menu.  Or right click on one of those buttons, no
 | |
|   -- context menu.  Uncomment the connections and try the same thing.
 | |
|   if \ dlgPopup~connectContextMenu(onDialogContext, self~hwnd, self) then do
 | |
|     say 'Error conecting context menu. SystemErrorCode:' || -
 | |
|     .SystemErrorCode SysGetErrortext(.SystemErrorCode)
 | |
|   end
 | |
|   else if \ dlgPopup~connectContextMenu(onDialogContext, self~newPushButton(IDOK)~hwnd, self) then do
 | |
|     say 'Error conecting context menu. SystemErrorCode:' || -
 | |
|     .SystemErrorCode SysGetErrortext(.SystemErrorCode)
 | |
|   end
 | |
|   else if \ dlgPopup~connectContextMenu(onDialogContext, self~newPushButton(IDCANCEL)~hwnd, self) then do
 | |
|     say 'Error conecting context menu. SystemErrorCode:' || -
 | |
|     .SystemErrorCode SysGetErrortext(.SystemErrorCode)
 | |
|   end
 | |
| 
 | |
| 
 | |
| /** onListViewContext()
 | |
|  *
 | |
|  * This is an event handler for the context menu event.
 | |
|  *
 | |
|  * The context menu event is generated when the user right-clicks the mouse,
 | |
|  * types the shift-F10 key combination, or types the VK_APPS key.  In this
 | |
|  * program, onListViewContext() is only generated when the mouse is right
 | |
|  * clicked on the list view control, or if the list view has the input focus and
 | |
|  * shift-F10 or VK_APPS are used.
 | |
|  *
 | |
|  * Look at the makeMenuConnections() method to see how the connection is made.
 | |
|  *
 | |
|  * Three arguments are passed to the event handler for the context menu event:
 | |
|  * the window handle of the window clicked, (or that has the focus if it is a
 | |
|  * keyboard event,) and x / y co-ordinates.  For a mouse click, the x / y co-
 | |
|  * ordinates are the position of the mouse, in pixels.  For the keyboard keys, x
 | |
|  * and y are both -1.
 | |
|  *
 | |
|  * When the x and y arguments are -1, we need to decide where to place the
 | |
|  * context menu when it is shown.  This is a design decision, there is no real
 | |
|  * right answer.  A normal thing to do here would be to place it at the selected
 | |
|  * list view item.  Instead, I decided to place at the lower right corner of the
 | |
|  * list view.  However, the list view has a vertical scroll bar which is within
 | |
|  * the area of the list view and I didn't like the look of that.
 | |
|  *
 | |
|  * So, I decided to move it to the left of the scroll bar.  I still didn't like
 | |
|  * that because of the shadowing, so I take that point and shift it up and over
 | |
|  * 15 pixels.
 | |
|  */
 | |
| ::method onListViewContext
 | |
|   expose lvPopup listView
 | |
|   use arg hwnd, x, y
 | |
| 
 | |
|   if x == -1, y == -1 then do
 | |
|     -- The keyboard was used, not the mouse.  Position the context menu as
 | |
|     -- described in the comments above.
 | |
|     rect = listView~windowRect
 | |
|     x = rect~right - .SM~cxVScroll + 15
 | |
|     y = rect~bottom - 15
 | |
|   end
 | |
| 
 | |
|   -- pos is the point on the screen, in pixels, to place the context menu.
 | |
|   pos = .Point~new(x, y)
 | |
| 
 | |
|   -- We show the menu.  The second optional argument is the dialog to assign the
 | |
|   -- the menu to, but we've already done that in makeMenuConnections().
 | |
|   ret = lvPopup~show(pos)
 | |
|   if ret == -1 then do
 | |
|     say 'lvPopup~show() failed SystemErrorCode:' || -
 | |
|     .SystemErrorCode SysGetErrortext(.SystemErrorCode)
 | |
|   end
 | |
| 
 | |
| 
 | |
| /** onDialogContext()
 | |
|  *
 | |
|  * This is an event handler for the context menu event.
 | |
|  *
 | |
|  * The comments for onListViewContext() above apply here.  This method is
 | |
|  * invoked when the user rigth clicks anywhere but the list-view, or for the
 | |
|  * keyboard context menu event, whenever the list view does not have the focus.
 | |
|  *
 | |
|  * Here we also need to decide where to position the context menu for the
 | |
|  * keyboard event.  I decided to place it in the rectangluar space between the
 | |
|  * rigth edge of the list view and the right edge of the dialog, in the center.
 | |
|  * I don't count the dialog caption bar as part of that space.
 | |
|  */
 | |
| ::method onDialogContext
 | |
|   expose dlgPopup listView
 | |
|   use arg hwnd, x, y
 | |
| 
 | |
|   if x == -1, y == -1 then do
 | |
|     -- The keyboard was used, not the mouse.  Position the context menu as
 | |
|     -- described in the comments above.
 | |
|     lvR = listView~windowRect
 | |
|     dlgR = self~windowRect
 | |
| 
 | |
|     xOffset = (dlgR~right - lvR~right) % 2
 | |
|     yOffset = (dlgR~bottom - (dlgR~top + .SM~cyCaption)) % 2
 | |
| 
 | |
|     x = lvR~right + xOffset
 | |
|     y = dlgR~bottom - yOffset
 | |
|   end
 | |
| 
 | |
|   -- This is the point where the context menu is positioned.
 | |
|   pos = .Point~new(x, y)
 | |
| 
 | |
|   -- In contrast to the list view pop up menu, the dialog pop up menu has never
 | |
|   -- been assigned to a dialog.  Therefore, each time we show it, we need to
 | |
|   -- specify the dialog owner.  Which is done in the second arguemnt.
 | |
|   ret = dlgPopup~show(pos, self)
 | |
|   if ret == -1 then do
 | |
|     say 'dlgPopup~show() failed SystemErrorCode:' || -
 | |
|     .SystemErrorCode SysGetErrortext(.SystemErrorCode)
 | |
|   end
 | |
| 
 | |
| /** onMenuCommand()
 | |
|  *
 | |
|  * This is the event handler for any menu item selected in the dialog pop up
 | |
|  * menu.  We simply determine which menu item was selected through the id
 | |
|  * argument and take the appropriate action.
 | |
|  */
 | |
| ::method onMenuCommand unguarded
 | |
|   use arg id
 | |
| 
 | |
|   select
 | |
|     when id == .constDir[IDM_RC_SHOW      ] then self~showMessage
 | |
|     when id == .constDir[IDM_RC_ENABLE_LV ] then self~enableListView(.true)
 | |
|     when id == .constDir[IDM_RC_DISABLE_LV] then self~enableListView(.false)
 | |
|     when id == .constDir[IDM_RC_BEEP      ] then beep(330, 250)
 | |
|     when id == .constDir[IDM_RC_QUIT      ] then return self~ok
 | |
|   end
 | |
|   -- End select
 | |
| 
 | |
| 
 | |
| /** sort()
 | |
|  *
 | |
|  * The event handler when the Sort menu item is selected in the list view pop up
 | |
|  * menu.
 | |
|  */
 | |
| ::method sort
 | |
|   expose sArray
 | |
|   self~rearrangeItems(sArray)
 | |
| 
 | |
| /** jumble()
 | |
|  *
 | |
|  * The event handler when the Jumble menu item is selected in the list view pop
 | |
|  * up menu.
 | |
|  *
 | |
|  * Here we re-order the list view items in a somewhat random order.
 | |
|  */
 | |
| ::method jumble
 | |
|   expose nArray
 | |
| 
 | |
|   tempArray = .array~new(11)
 | |
| 
 | |
|   indexes = .set~new
 | |
|   do while indexes~items < 11
 | |
|     count = indexes~items
 | |
|     do while indexes~items == count
 | |
|       i = random(1, 11)
 | |
|       if \ indexes~hasItem(i) then do
 | |
|         tempArray~append(nArray[i])
 | |
|         indexes~put(i)
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   self~rearrangeItems(tempArray)
 | |
| 
 | |
| /** reverseSort()
 | |
|  *
 | |
|  * The event handler when the Reverse Sort menu item is selected in the list
 | |
|  * view pop up menu.
 | |
|  */
 | |
| ::method reverseSort
 | |
|   expose rsArray
 | |
|   self~rearrangeItems(rsArray)
 | |
| 
 | |
| /** orderByFirstName()
 | |
|  *
 | |
|  * The event handler when the Order By First Name menu item is selected in the
 | |
|  * list view pop up menu.
 | |
|  */
 | |
| ::method orderByFirstName
 | |
|   expose fnArray
 | |
|   self~rearrangeItems(fnArray)
 | |
| 
 | |
| /** orderByLastName()
 | |
|  *
 | |
|  * The event handler when the Order By Last Name menu item is selected in the
 | |
|  * list view pop up menu.
 | |
|  */
 | |
| ::method orderByLastName
 | |
|   expose lnArray
 | |
|   self~rearrangeItems(lnArray)
 | |
| 
 | |
| /** orderByProfession()
 | |
|  *
 | |
|  * The event handler when the Order By Profession menu item is selected in the
 | |
|  * list view pop up menu.
 | |
|  */
 | |
| ::method orderByProfession
 | |
|   expose profArray
 | |
|   self~rearrangeItems(profArray)
 | |
| 
 | |
| /** restoreOriginalOrder()
 | |
|  *
 | |
|  * The event handler when the Restore Original Order menu item is selected in
 | |
|  * the list view pop up menu.
 | |
|  */
 | |
| ::method restoreOriginalOrder
 | |
|   expose nArray
 | |
|   self~rearrangeItems(nArray)
 | |
| 
 | |
| /** rearrangeItems()
 | |
|  *
 | |
|  * Places the list view items in the order specified.  As explained elsewhere,
 | |
|  * the purpose of this example program is to show how context menus work, not
 | |
|  * to show how list view controls work.
 | |
|  *
 | |
|  * Therefore we don't actually sort the list view items, we just use some pre-
 | |
|  * ordered arrays of the items.  To change the order, we delete all the existing
 | |
|  * items and replace them using the an array of the same items in a different
 | |
|  * order.  The array is passed in here as the 'a' argument.
 | |
|  *
 | |
|  * The disable, delete, prepare ... enable redraw sequence seems, to me, to
 | |
|  * reduce flicker in the list view.  However, there are not enough items in the
 | |
|  * list view to tell for sure.  An interesting experiment would be to try it
 | |
|  * with several hundreds of items.
 | |
|  */
 | |
| ::method rearrangeItems private
 | |
|   expose listView
 | |
|   use strict arg a
 | |
| 
 | |
|   listView~disable
 | |
|   listView~deleteAll
 | |
|   listView~prepare4nItems(11)
 | |
| 
 | |
|   do r over a
 | |
|     listView~addRow( , , r~fName, r~lName, r~profession)
 | |
|   end
 | |
| 
 | |
|   listView~enable
 | |
|   listView~redrawItems
 | |
| 
 | |
| 
 | |
| /** showMessage()
 | |
|  *
 | |
|  * Helper method.  Shows a simple message to the user in response to the Show
 | |
|  * Message menu item.
 | |
|  */
 | |
| ::method showMessage private
 | |
| 
 | |
|   msg = "This is a good example of context menus."
 | |
|   z = MessageDialog(msg, self~hwnd, "Context Menu Message", "OK", "INFORMATION")
 | |
| 
 | |
| /** enableListView()
 | |
|  *
 | |
|  * Helper method.  Enables or disables the list view control in response to the
 | |
|  * Enable List View or Disable List View menu items.
 | |
|  *
 | |
|  * When the list view control is enabled, we disable the Enable List View menu
 | |
|  * item and enable the Disable List View menu item.  When the list view is
 | |
|  * disabled we do the reverse.  Note that when the list view is disabled, it is
 | |
|  * no longer possible for the user to trigger the list view context menu.
 | |
|  */
 | |
| ::method enableListView private
 | |
|   expose listView dlgPopup
 | |
|   use strict arg enable
 | |
| 
 | |
|   if enable then do
 | |
|     dlgPopup~disable(IDM_RC_ENABLE_LV)
 | |
|     dlgPopup~enable(IDM_RC_DISABLE_LV)
 | |
|     listView~enable
 | |
|   end
 | |
|   else do
 | |
|     dlgPopup~disable(IDM_RC_DISABLE_LV)
 | |
|     dlgPopup~enable(IDM_RC_ENABLE_LV)
 | |
|     listView~disable
 | |
|   end
 | |
| 
 | |
| /** initListView()
 | |
|  *
 | |
|  * Initializes the list view by adding the columns, setting the extended list
 | |
|  * view style, and adding the list view items.
 | |
|  */
 | |
| ::method initListView private
 | |
|   expose nArray listView
 | |
| 
 | |
|   listView = self~newListView(IDC_LV)
 | |
| 
 | |
|   listView~addExtendedStyle("FULLROWSELECT DOUBLEBUFFER GRIDLINES")
 | |
| 
 | |
|   listView~insertColumnPX(1, 'First Name', 68)
 | |
|   listView~insertColumnPX(2, 'Last Name', 70)
 | |
|   listView~insertColumnPX(3, 'Profession', 102)
 | |
| 
 | |
|   do r over nArray
 | |
|     listView~addRow( , , r~fName, r~lName, r~profession)
 | |
|   end
 | |
| 
 | |
| /** createArrays()
 | |
|  *
 | |
|  * The purpose of this example program is to demonstrate how context menus work,
 | |
|  * not how list view controls work.  Rather than actually sort the list view
 | |
|  * items, we just produce some pre-sorted arrays to use.
 | |
|  *
 | |
|  * The n, ln, fn, etc., prefixes stand for normal, first name, last name,
 | |
|  * profession, sorted, and reverse sorted.
 | |
|  */
 | |
| ::method createArrays private
 | |
|   expose nArray lnArray fnArray profArray sArray rsArray
 | |
| 
 | |
|   nArray    = .array~new(11)
 | |
|   lnArray   = .array~new(11)
 | |
|   fnArray   = .array~new(11)
 | |
|   profArray = .array~new(11)
 | |
|   sArray    = .array~new(11)
 | |
|   rsArray   = .array~new(11)
 | |
| 
 | |
|   nArray[1 ] = .directory~new~~put('Mark',     fName)~~put('Thompson',   lName)~~put('Mechanic', profession)
 | |
|   nArray[2 ] = .directory~new~~put('Sue',      fName)~~put('Lamont',     lName)~~put('Nurse', profession)
 | |
|   nArray[3 ] = .directory~new~~put('Mary',     fName)~~put('Greensmith', lName)~~put('CEO', profession)
 | |
|   nArray[4 ] = .directory~new~~put('Susan',    fName)~~put('Dunn',       lName)~~put('Stock broker', profession)
 | |
|   nArray[5 ] = .directory~new~~put('Hank',     fName)~~put('Miller',     lName)~~put('Librarian', profession)
 | |
|   nArray[6 ] = .directory~new~~put('Larry',    fName)~~put('Williams',   lName)~~put('Doctor', profession)
 | |
|   nArray[7 ] = .directory~new~~put('Alice',    fName)~~put('Toklas',     lName)~~put('Dentist', profession)
 | |
|   nArray[8 ] = .directory~new~~put('Bill',     fName)~~put('Maher',      lName)~~put('Lawyer', profession)
 | |
|   nArray[9 ] = .directory~new~~put('Vail',     fName)~~put('Miesfeld',   lName)~~put('Software Engineer', profession)
 | |
|   nArray[10] = .directory~new~~put('Eric',     fName)~~put('Clapton',    lName)~~put('Cook', profession)
 | |
|   nArray[11] = .directory~new~~put('Christin', fName)~~put('Henesy',     lName)~~put('Detective', profession)
 | |
| 
 | |
|   lnArray[1 ] = .directory~new~~put('Eric',     fName)~~put('Clapton',    lName)~~put('Cook', profession)
 | |
|   lnArray[2 ] = .directory~new~~put('Susan',    fName)~~put('Dunn',       lName)~~put('Stock broker', profession)
 | |
|   lnArray[3 ] = .directory~new~~put('Mary',     fName)~~put('Greensmith', lName)~~put('CEO', profession)
 | |
|   lnArray[4 ] = .directory~new~~put('Christin', fName)~~put('Henesy',     lName)~~put('Detective', profession)
 | |
|   lnArray[5 ] = .directory~new~~put('Sue',      fName)~~put('Lamont',     lName)~~put('Nurse', profession)
 | |
|   lnArray[6 ] = .directory~new~~put('Bill',     fName)~~put('Maher',      lName)~~put('Lawyer', profession)
 | |
|   lnArray[7 ] = .directory~new~~put('Vail',     fName)~~put('Miesfeld',   lName)~~put('Software Engineer', profession)
 | |
|   lnArray[8 ] = .directory~new~~put('Hank',     fName)~~put('Miller',     lName)~~put('Librarian', profession)
 | |
|   lnArray[9 ] = .directory~new~~put('Mark',     fName)~~put('Thompson',   lName)~~put('Mechanic', profession)
 | |
|   lnArray[10] = .directory~new~~put('Alice',    fName)~~put('Toklas',     lName)~~put('Dentist', profession)
 | |
|   lnArray[11] = .directory~new~~put('Larry',    fName)~~put('Williams',   lName)~~put('Doctor', profession)
 | |
| 
 | |
|   fnArray[1 ] = .directory~new~~put('Alice',    fName)~~put('Toklas',     lName)~~put('Dentist', profession)
 | |
|   fnArray[2 ] = .directory~new~~put('Bill',     fName)~~put('Maher',      lName)~~put('Lawyer', profession)
 | |
|   fnArray[3 ] = .directory~new~~put('Christin', fName)~~put('Henesy',     lName)~~put('Detective', profession)
 | |
|   fnArray[4 ] = .directory~new~~put('Eric',     fName)~~put('Clapton',    lName)~~put('Cook', profession)
 | |
|   fnArray[5 ] = .directory~new~~put('Hank',     fName)~~put('Miller',     lName)~~put('Librarian', profession)
 | |
|   fnArray[6 ] = .directory~new~~put('Larry',    fName)~~put('Williams',   lName)~~put('Doctor', profession)
 | |
|   fnArray[7 ] = .directory~new~~put('Mark',     fName)~~put('Thompson',   lName)~~put('Mechanic', profession)
 | |
|   fnArray[8 ] = .directory~new~~put('Mary',     fName)~~put('Greensmith', lName)~~put('CEO', profession)
 | |
|   fnArray[9 ] = .directory~new~~put('Sue',      fName)~~put('Lamont',     lName)~~put('Nurse', profession)
 | |
|   fnArray[10] = .directory~new~~put('Susan',    fName)~~put('Dunn',       lName)~~put('Stock broker', profession)
 | |
|   fnArray[11] = .directory~new~~put('Vail',     fName)~~put('Miesfeld',   lName)~~put('Software Engineer', profession)
 | |
| 
 | |
|   profArray[1 ] = .directory~new~~put('Mary',     fName)~~put('Greensmith', lName)~~put('CEO', profession)
 | |
|   profArray[2 ] = .directory~new~~put('Eric',     fName)~~put('Clapton',    lName)~~put('Cook', profession)
 | |
|   profArray[3 ] = .directory~new~~put('Alice',    fName)~~put('Toklas',     lName)~~put('Dentist', rprofession)
 | |
|   profArray[4 ] = .directory~new~~put('Christin', fName)~~put('Henesy',     lName)~~put('Detective', profession)
 | |
|   profArray[5 ] = .directory~new~~put('Larry',    fName)~~put('Williams',   lName)~~put('Doctor', profession)
 | |
|   profArray[6 ] = .directory~new~~put('Bill',     fName)~~put('Maher',      lName)~~put('Lawyer', profession)
 | |
|   profArray[7 ] = .directory~new~~put('Hank',     fName)~~put('Miller',     lName)~~put('Librarian', profession)
 | |
|   profArray[8 ] = .directory~new~~put('Mark',     fName)~~put('Thompson',   lName)~~put('Mechanic', profession)
 | |
|   profArray[9 ] = .directory~new~~put('Sue',      fName)~~put('Lamont',     lName)~~put('Nurse', profession)
 | |
|   profArray[10] = .directory~new~~put('Vail',     fName)~~put('Miesfeld',   lName)~~put('Software Engineer', profession)
 | |
|   profArray[11] = .directory~new~~put('Susan',    fName)~~put('Dunn',       lName)~~put('Stock broker', profession)
 | |
| 
 | |
|   -- Sorted array is same as fnArray
 | |
|   sArray[1 ] = .directory~new~~put('Alice',    fName)~~put('Toklas',     lName)~~put('Dentist', profession)
 | |
|   sArray[2 ] = .directory~new~~put('Bill',     fName)~~put('Maher',      lName)~~put('Lawyer', profession)
 | |
|   sArray[3 ] = .directory~new~~put('Christin', fName)~~put('Henesy',     lName)~~put('Detective', profession)
 | |
|   sArray[4 ] = .directory~new~~put('Eric',     fName)~~put('Clapton',    lName)~~put('Cook', profession)
 | |
|   sArray[5 ] = .directory~new~~put('Hank',     fName)~~put('Miller',     lName)~~put('Librarian', profession)
 | |
|   sArray[6 ] = .directory~new~~put('Larry',    fName)~~put('Williams',   lName)~~put('Doctor', profession)
 | |
|   sArray[7 ] = .directory~new~~put('Mark',     fName)~~put('Thompson',   lName)~~put('Mechanic', profession)
 | |
|   sArray[8 ] = .directory~new~~put('Mary',     fName)~~put('Greensmith', lName)~~put('CEO', profession)
 | |
|   sArray[9 ] = .directory~new~~put('Sue',      fName)~~put('Lamont',     lName)~~put('Nurse', profession)
 | |
|   sArray[10] = .directory~new~~put('Susan',    fName)~~put('Dunn',       lName)~~put('Stock broker', profession)
 | |
|   sArray[11] = .directory~new~~put('Vail',     fName)~~put('Miesfeld',   lName)~~put('Software Engineer', profession)
 | |
| 
 | |
|   -- Just reverse the array indexes
 | |
|   rsArray[11] = .directory~new~~put('Alice',    fName)~~put('Toklas',     lName)~~put('Dentist', profession)
 | |
|   rsArray[10] = .directory~new~~put('Bill',     fName)~~put('Maher',      lName)~~put('Lawyer', profession)
 | |
|   rsArray[9 ] = .directory~new~~put('Christin', fName)~~put('Henesy',     lName)~~put('Detective', profession)
 | |
|   rsArray[8 ] = .directory~new~~put('Eric',     fName)~~put('Clapton',    lName)~~put('Cook', profession)
 | |
|   rsArray[7 ] = .directory~new~~put('Hank',     fName)~~put('Miller',     lName)~~put('Librarian', profession)
 | |
|   rsArray[6 ] = .directory~new~~put('Larry',    fName)~~put('Williams',   lName)~~put('Doctor', profession)
 | |
|   rsArray[5 ] = .directory~new~~put('Mark',     fName)~~put('Thompson',   lName)~~put('Mechanic', profession)
 | |
|   rsArray[4 ] = .directory~new~~put('Mary',     fName)~~put('Greensmith', lName)~~put('CEO', profession)
 | |
|   rsArray[3 ] = .directory~new~~put('Sue',      fName)~~put('Lamont',     lName)~~put('Nurse', profession)
 | |
|   rsArray[2 ] = .directory~new~~put('Susan',    fName)~~put('Dunn',       lName)~~put('Stock broker', profession)
 | |
|   rsArray[1 ] = .directory~new~~put('Vail',     fName)~~put('Miesfeld',   lName)~~put('Software Engineer', profession)
 | |
| 
 | |
| 
 | |
| -- The putMenu() method simply provides a convenient way to pass in the menu
 | |
| -- object created in the program entry code to this dialog.
 | |
| ::method putMenu
 | |
|   expose lvPopup
 | |
|   use strict arg lvPopup
 | |
| 
 | |
| 
 | |
| /** routine::createListViewMenu()
 | |
|  *
 | |
|  * This routine creates the list view context menu dynamically using methods of
 | |
|  * the .PopupMenu.  It is very easy to use.  Menu items can be inserted or
 | |
|  * removed from a menu at any time.
 | |
|  *
 | |
|  * The other point that is being made by creating this menu in a routine is that
 | |
|  * menu objects are truely independent of a dialog.  A menu object can be
 | |
|  * created without any existing dialogs.  The object can then be used with any
 | |
|  * dialog.
 | |
|  */
 | |
| ::routine createListViewMenu
 | |
|   use strict arg
 | |
| 
 | |
|   -- Create a new empty pop up menu.
 | |
|   m = .PopupMenu~new(IDM_LV_BAR)
 | |
| 
 | |
|   -- Insert the menu items.  The first argument is the resource ID of the item
 | |
|   -- before which the inserted item goes.  The second argument is the resource
 | |
|   -- ID of the item to insert.
 | |
|   --
 | |
|   -- For the first item, since there are no items in the menu, the insert before
 | |
|   -- argument does not matter.  I ususally just use the same resource ID as the
 | |
|   -- item being inserted.  You could use any positive number.
 | |
|   --
 | |
|   -- For the rest of the items, here they are just sort of in a random order.
 | |
|   -- This is to demonstrate how the 'insert before' works.  I usually insert the
 | |
|   -- items in a last comes first order because it makes it a little easy to
 | |
|   -- visualize how the menu will look, if you picture the menu upside down.
 | |
|   m~insertItem(IDM_LV_RESTORE, IDM_LV_RESTORE, "Restore Original Order")
 | |
|   m~insertItem(IDM_LV_RESTORE, IDM_LV_PROFESSION, "Order by Profession")
 | |
|   m~insertItem(IDM_LV_PROFESSION, IDM_LV_FNAME, "Order by First Name")
 | |
| 
 | |
|   m~insertItem(IDM_LV_FNAME, IDM_LV_REVERSE, "Reverse Sort")
 | |
|   m~insertItem(IDM_LV_REVERSE, IDM_LV_SORT, "Sort")
 | |
|   m~insertItem(IDM_LV_REVERSE, IDM_LV_JUMBLE, "Jumble")
 | |
| 
 | |
|   m~insertItem(IDM_LV_PROFESSION, IDM_LV_LNAME, "Order by Last Name")
 | |
| 
 | |
|   m~insertSeparator(IDM_LV_FNAME, IDM_LV_SEP1)
 | |
|   m~insertSeparator(IDM_LV_RESTORE, IDM_LV_SEP2)
 | |
| 
 | |
|   return m
 | |
| 
 |