514 lines
19 KiB
Rexx
Executable File
514 lines
19 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. */
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* File: imageButton.rex
|
|
*
|
|
* Category: ooDialog example
|
|
*
|
|
* Syntax:
|
|
* rc = imageButton
|
|
*
|
|
* Purpose:
|
|
* Provides some example code of new ooDialog feature introduced in 4.0.0
|
|
*
|
|
* This program uses a resource script (.rc) file for the dialog definition.
|
|
* The dialog was designed using a "what you see is what you get" dialog
|
|
* editor. The resource script can be compiled into a binary image for use
|
|
* with a ResDialog. In this case it is just left as a .rc file for use in
|
|
* a RcDialog.
|
|
*
|
|
* Dialogs built using a dialog editor are much easier to create and maintain
|
|
* then UserDialogs.
|
|
*
|
|
* Input:
|
|
* None.
|
|
*
|
|
* Returns:
|
|
* 0 on success, non-zero if the dialog could not be created for some reason.
|
|
*
|
|
* Notes:
|
|
* This is a short program written to amuse my niece. Two of the button
|
|
* controls in the dialog use an image list. The text for the buttons is 'Add
|
|
* Pictures' and 'View Pictures' with a picture of a camera on them. When the
|
|
* user moves the mouse pointer over either one of the buttons, the picture
|
|
* changes to a skull and the text changes to 'Death if you touch me.' When
|
|
* the user pushes either of the buttons the picture changes to a giant bee.
|
|
*
|
|
* My niece and I think this is hilarious.
|
|
*
|
|
* The dialog shows how to use a button control image list. Since I am not
|
|
* artistic, I produced the images by cutting them out of digital pictures
|
|
* I have taken over the last few years.
|
|
*
|
|
* A professional application would of course use professional graphics. This
|
|
* dialog just gives an idea of what the button control can do with an image
|
|
* list.
|
|
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
|
use arg
|
|
|
|
-- Ensure we can be run from any directory.
|
|
srcDir = locate()
|
|
|
|
rcFile = srcDir"resources\imageButton.rc"
|
|
symbolFile = srcDir"resources\imageButton.h"
|
|
|
|
.application~setDefaults("O", symbolFile, .false)
|
|
|
|
dlg = .ImageListDlg~new(rcFile, IDD_IMAGELIST_BUTTON)
|
|
|
|
if dlg~initCode <> 0 then do
|
|
say "The Image List Dialog was not created correctly"
|
|
say "Init code:" dlg~initCode
|
|
return 99
|
|
end
|
|
|
|
dlg~execute("SHOWTOP", IDI_DLG_OOREXX)
|
|
|
|
return 0
|
|
-- End of entry point.
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*\
|
|
Directives, Classes, or Routines.
|
|
\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
|
::requires "ooDialog.cls"
|
|
|
|
::class 'ImageListDlg' subclass RcDialog
|
|
|
|
::method initDialog
|
|
expose pbPush pbView pbAdd stStatus ctr imagesLoaded
|
|
|
|
self~connectButtonEvent(IDC_PB_PUSHME, "CLICKED", "onPushMe")
|
|
self~connectButtonEvent(IDC_PB_VIEW, "CLICKED", "onView")
|
|
self~connectButtonEvent(IDC_PB_ADD, "CLICKED", "onAdd")
|
|
|
|
self~connectButtonEvent(IDC_PB_VIEW, "HOTITEM", onHover)
|
|
self~connectButtonEvent(IDC_PB_ADD, "HOTITEM", onHover)
|
|
|
|
imagesLoaded = .false
|
|
ctr = 0
|
|
|
|
pbPush = self~newPushButton(IDC_PB_PUSHME)
|
|
pbView = self~newPushButton(IDC_PB_VIEW)
|
|
pbAdd = self~newPushButton(IDC_PB_ADD)
|
|
stStatus = self~newStatic(IDC_ST_STATUS)
|
|
|
|
stStatus~setText("")
|
|
|
|
self~setPictureButtons
|
|
|
|
::method setPictureButtons private
|
|
expose pbView pbAdd stStatus imagesLoaded imageList
|
|
|
|
-- Using an image list with the button controls needs at least comctl32
|
|
-- version 6. See what version we are running under and don't use the image
|
|
-- list if we are on an older version of Windows.
|
|
if .DlgUtil~comCtl32Version < 6 then return self~oldSetButtons
|
|
|
|
pbView~style = "MULTILINE BOTTOM"
|
|
|
|
-- In the following code, there are a lot of if tests. You can change any of
|
|
-- the if tests to its converse to get an idea of the dialog behavior when
|
|
-- errors happen. Have fun, experiment.
|
|
|
|
srcDir = .application~srcDir
|
|
|
|
-- The images are loaded from files.
|
|
files = .array~new()
|
|
files[1] = srcDir"resources\Normal.bmp" -- Normal
|
|
files[2] = srcDir"resources\Hot.bmp" -- Hot (hover)
|
|
files[3] = srcDir"resources\Pushed.bmp" -- Pushed
|
|
files[4] = srcDir"resources\Disabled.bmp" -- Disabled
|
|
files[5] = srcDir"resources\Default.bmp" -- Default button
|
|
files[6] = srcDir"resources\Hot.bmp" -- Stylus hot, tablet PC only
|
|
|
|
-- .ImageList~create() exposes the Windows API ImageList_Create(). Use the
|
|
-- MSDN documentation to fully understand this API. Simply do a Google search
|
|
-- with: MSDN ImageList_Create
|
|
--
|
|
-- The IIC_COLOR24 flag and the size values correspond to the values the
|
|
-- bitmaps were created with. You can look at the properties page for one
|
|
-- of the bitamp files in Explorer to see these values.
|
|
|
|
-- We set the flags to create a 24 bit color, masked image list.
|
|
flags = 'COLOR24 MASK'
|
|
|
|
-- Create an empty .ImageList object:
|
|
imageList = .ImageList~create(.Size~new(61, 46), flags, 10, 10);
|
|
|
|
-- Add the images to the image list, only if the image list is not null. If
|
|
-- the .ImageList object is null, an error happened when it was created and it
|
|
-- is not usable.
|
|
--
|
|
-- When an image is added to the image list, the index of the image is
|
|
-- returned. -1 signals that the image was not added. We use that as a flag
|
|
-- to indicate some images are not added.
|
|
index = -1
|
|
lastGoodIndex = -1
|
|
if \ imageList~isNull then do
|
|
stStatus~setText('Created image list:' imageList~handle)
|
|
|
|
-- We use a colorref when adding the image to the image list. The 255,255,
|
|
-- 255 value is white. Essentially it tells the image list to create a mask
|
|
-- that causes every white pixel in the image to be transparent.
|
|
--
|
|
-- The only image it has any effect on is the camera. In the camera image,
|
|
-- the background uses white pixels. So, when the camera image is drawn,
|
|
-- the background becomes transparent, which allows the button color to show
|
|
-- through.
|
|
cRef = .Image~colorRef(255, 255, 255)
|
|
|
|
-- For each image file, get an .Image object and add it to our image list.
|
|
do f over files
|
|
|
|
-- The defaults for the optional arguments to getImage() are good for this
|
|
-- application. Again, isNull() signals the .Image object is not valid.
|
|
image = .Image~getImage(f)
|
|
if \ image~isNull then do
|
|
|
|
-- Add the image, quit if we get back a -1.
|
|
index = imageList~addMasked(image, cRef)
|
|
if index == -1 then leave
|
|
|
|
stStatus~setText('Added image index:' index)
|
|
lastGoodIndex = index
|
|
|
|
-- The underlying ImageList control makes a copy of the image passed to
|
|
-- it. We can now relase the image to free up the system resource.
|
|
-- Note that when this program ends, the OS will free the resource
|
|
-- automatically, so this is NOT necessary. Note also that you should
|
|
-- not release SHARED images. However, when an image is loaded from a
|
|
-- file, it can not be a shared image.
|
|
image~release
|
|
|
|
-- Once you release the image, the .Image object is no longer valid and
|
|
-- trying to use it will raise a syntax error. You can use the isNull()
|
|
-- method on any image object at any time to test if it is valid without
|
|
-- raising a syntax error. In addition, if you invoke the release()
|
|
-- method after the image has already been released, it is just ignored.
|
|
end
|
|
else do
|
|
say 'Failed to load bitmap:' f 'system error code:' .SystemErrorCode
|
|
end
|
|
end
|
|
end
|
|
else do
|
|
stStatus~setText('Error creating .ImageList. Images are not available.')
|
|
return
|
|
end
|
|
|
|
if lastGoodIndex == -1 then do
|
|
stStatus~setText("Failed to add any images. Images are not available")
|
|
imageList~release
|
|
return
|
|
end
|
|
|
|
imagesLoaded = .true
|
|
|
|
if index == -1 then do
|
|
stStatus~setText("Not all images were loaded. Last good image at index" lastGoodIndex)
|
|
end
|
|
|
|
-- Set up the alignment and margin around the image on the 'View Pictures'
|
|
-- button. Then set the image list in the button control
|
|
align = 'LEFT'
|
|
margin = .Rect~new(1)
|
|
|
|
pbView~setImageList(imageList, margin, align)
|
|
|
|
if .SystemErrorCode <> 0 then do
|
|
stStatus~setText('setImageList() failed. SystemErrorCode:' .SystemErrorCode 'Images not available.')
|
|
imageList~release
|
|
return
|
|
end
|
|
else do
|
|
stStatus~setText("View Pictures button image list loaded.")
|
|
end
|
|
|
|
-- Temporarily set the title to the longest text, to calculate ideal size.
|
|
pbView~setTitle("DEATH if you touch me")
|
|
bestSize = pbView~getIdealSize
|
|
|
|
-- Write this to the screen, example only.
|
|
say 'Got ideal size:' bestSize
|
|
say ' width: ' bestSize~width
|
|
say ' height:' bestSize~height
|
|
say
|
|
|
|
-- Now reset the button label and set the size to the ideal size.
|
|
pbView~setTitle("View Pictures")
|
|
pbView~setRect(0, 0, bestSize~width, bestSize~height, "NOMOVE")
|
|
|
|
-- Example only of the getImageList() method. A .Directory object is
|
|
-- returned.
|
|
d = pbView~getImageList
|
|
|
|
-- Just write this to the screen.
|
|
say 'Got View Picutes button control image list information.'
|
|
say 'Returned:' d
|
|
if d <> .nil then do
|
|
say ' image list:' d~imageList
|
|
say ' handle: ' d~imageList~handle
|
|
say ' rect: ' d~rect
|
|
say ' align: ' d~alignmentKeyword
|
|
say 'Image margins:' d~rect~left',' d~rect~top',' d~rect~right',' d~rect~bottom
|
|
end
|
|
else do
|
|
stStatus~setText("Error getting View Pictures button control image list information.")
|
|
end
|
|
say
|
|
|
|
-- For the Add Pictures button, use the same image list created for the View
|
|
-- Pictures button. We just change the margins and the alignment.
|
|
align = 'CENTER'
|
|
margin~left = 10
|
|
margin~right = 10
|
|
margin~top = 30
|
|
margin~bottom = 15
|
|
|
|
pbAdd~setImageList(imageList, margin, align)
|
|
|
|
if .SystemErrorCode <> 0 then do
|
|
stStatus~setText('setImageList() failed. SystemErrorCode:' .SystemErrorCode 'Images not available.')
|
|
|
|
-- Releasing the image list now, after it has been successfully set for the
|
|
-- View Pictures button, causes the images to disappear from that button.
|
|
--
|
|
-- In general, DON'T release an image list that is in use. This is just
|
|
-- done here to demonstrate that things won't blow up.
|
|
imageList~release
|
|
return
|
|
end
|
|
else do
|
|
stStatus~setText("Add Pictures button image list loaded.")
|
|
end
|
|
|
|
-- Now, the same as above, figure out the ideal size and then adjust the
|
|
-- button size.
|
|
pbAdd~style = "BOTTOM MULTILINE"
|
|
pbAdd~setTitle("DEATH if you touch me")
|
|
s = pbAdd~getIdealSize
|
|
pbAdd~setTitle("Add Pictures")
|
|
pbAdd~setRect(0, 0, s~width, s~height, "NOMOVE")
|
|
|
|
-- Again, just for an example of the getImageList() method.
|
|
d = pbAdd~getImageList
|
|
|
|
say 'Got Add Picutes button control image list information.'
|
|
say 'Returned:' d
|
|
if d <> .nil then do
|
|
-- Write this to the screen. Note that the image list hand is the same as
|
|
-- the above.
|
|
say ' image list:' d~imageList
|
|
say ' handle: ' d~imageList~handle
|
|
say ' rect: ' d~rect
|
|
say ' align: ' d~alignmentKeyword
|
|
say 'Image margins:' d~rect~left',' d~rect~top',' d~rect~right',' d~rect~bottom
|
|
end
|
|
else do
|
|
stStatus~setText("Error getting Add Pictures button control image list information.")
|
|
end
|
|
say
|
|
|
|
return
|
|
|
|
-- This method allows the dialog to work on systems prior to XP. We just use
|
|
-- the old fashioned buttons.
|
|
::method oldSetButtons private
|
|
expose pbView pbAdd
|
|
|
|
pbView~setTitle("View Pictures")
|
|
pbAdd~setTitle("Add Pictures")
|
|
|
|
pbView~resize(50, 15)
|
|
pbAdd~resize(50, 15)
|
|
|
|
return .true
|
|
|
|
-- As the user pushes the Push Me button, we cycle through some different
|
|
-- examples of changing the button state and style. This changes the pictures
|
|
-- displayed on the buttons. (Provided of course that there were no errors in
|
|
-- setting the button control image list(s).) The state and style changes work
|
|
-- whether we have images or not, so we don't check for pictures loaded or not
|
|
-- on those.
|
|
::method onPushMe unguarded
|
|
expose pbView pbAdd stStatus ctr imagesLoaded oldImageList
|
|
|
|
select
|
|
when ctr == 0 then do
|
|
pbView~state = "FOCUS"
|
|
stStatus~setText('View Pictures button set to the Focused state.')
|
|
ctr += 1
|
|
end
|
|
when ctr == 1 then do
|
|
pbAdd~style = "DEFPUSHBUTTON"
|
|
stStatus~setText('Add Pictures button now the default push button.')
|
|
ctr += 1
|
|
end
|
|
when ctr == 2 then do
|
|
pbView~style = "DEFPUSHBUTTON"
|
|
stStatus~setText('View Pictures button now the default push button.')
|
|
ctr += 1
|
|
end
|
|
when ctr == 3 then do
|
|
pbView~disable
|
|
stStatus~setText('View Pictures button now disabled.')
|
|
ctr += 1
|
|
end
|
|
when ctr == 4 then do
|
|
pbView~enable
|
|
cancel = self~newPushButton(IDCANCEL)
|
|
cancel~style = "DEFPUSHBUTTON"
|
|
stStatus~setText('View Pictures button now enabled, should not be default push button.')
|
|
ctr = 5
|
|
end
|
|
when ctr == 5 then do
|
|
if imagesLoaded then do
|
|
oldImageList = pbView~setImageList(.nil)
|
|
stStatus~setText('Removed View Pictures button images. Push "Push Me one more time to restore".')
|
|
end
|
|
ctr = 6
|
|
end
|
|
when ctr == 6 then do
|
|
if imagesLoaded, oldImageList \== .nil then do
|
|
pbView~setImageList(oldImageList~imageList, oldImageList~rect, oldImageList~alignment)
|
|
stStatus~setText('Restored View Pictures button images.')
|
|
end
|
|
ctr = 0
|
|
end
|
|
otherwise do
|
|
-- Should be impossible to happen. Just do the same as if ctr were 6.
|
|
if imagesLoaded, oldImageList \== .nil then do
|
|
pbView~setImageList(oldImageList~imageList, oldImageList~rect, oldImageList~alignment)
|
|
stStatus~setText('Restored View Pictures button images.')
|
|
end
|
|
ctr = 0
|
|
end
|
|
end
|
|
-- End select
|
|
|
|
-- This is the amusing part. We get this event message when the mouse is first
|
|
-- moved over a button (entering will be .true) or when the mouse moves off a
|
|
-- button (entering will be .false.) We change the text and style of the
|
|
-- button, the button control itself changes the picture.
|
|
::method onHover unguarded
|
|
expose imagesLoaded pbView pbAdd
|
|
use arg id, entering
|
|
|
|
if \ imagesLoaded then return
|
|
|
|
isViewButton = (id == .constDir[IDC_PB_VIEW])
|
|
|
|
if entering then do
|
|
text = "DEATH if you touch me"
|
|
style = "TOP"
|
|
end
|
|
else do
|
|
if isViewButton then text = "View Pictures"
|
|
else text = "Add Pictures"
|
|
style = "BOTTOM"
|
|
end
|
|
|
|
if isViewButton then do
|
|
pbView~style = style
|
|
pbView~setTitle(text)
|
|
end
|
|
else do
|
|
pbAdd~style = style
|
|
pbAdd~setTitle(text)
|
|
end
|
|
|
|
::method onView unguarded
|
|
expose stStatus
|
|
stStatus~setText('In onView, no actions defined.')
|
|
|
|
::method onAdd unguarded
|
|
expose stStatus
|
|
stStatus~setText('In onAdd, no actions defined.')
|
|
|
|
|
|
/* When you are done using an image list you can release it to free up system
|
|
* resources.
|
|
*
|
|
* Please note that when this program ends, the process will disappear and the
|
|
* OS will free up the resources automatically.
|
|
*
|
|
* As noted in a comment above, if you release the image list when it is in
|
|
* use, the pictures disappear. Testing has shown that releasing an image list
|
|
* when it is in use, does not crash the system. However, releasing an image
|
|
* list while it is in use would seem to be a foolish practice.
|
|
*
|
|
* When would you want to release an image list? In this case if this dialog
|
|
* was a part of a bigger, long running application. When this dialog ends, it
|
|
* makes sense to free up the system resources used for the image list. Since
|
|
* the main application is still running, the OS will not release the resources
|
|
* used in this dialog.
|
|
*
|
|
* Microsoft says, you should release an image list when you are done with it.
|
|
*
|
|
* In this case that would be when the dialog is closed. There are at least 2
|
|
* strategies that could be employed.
|
|
*
|
|
* 1.) Save a reference to the image list handle returned by .ImageList~create()
|
|
* and pass it to release().
|
|
*
|
|
* 2.) Use getImageList() to ask the button to give you the image list
|
|
* information and use the image list object that is returned as part of the
|
|
* .Directory object. The image list object in strategy 1 and 2 are the same
|
|
* object.
|
|
*
|
|
* Don't forget to release the handle if either the dialog is canceled or if it
|
|
* is closed. Below is a demonstration of both strategies.
|
|
*/
|
|
|
|
::method cancel
|
|
expose imageList imagesLoaded
|
|
|
|
if imagesLoaded then imageList~release
|
|
return self~cancel:super
|
|
|
|
::method ok
|
|
expose imagesLoaded pbView
|
|
|
|
if imagesLoaded then do
|
|
imageListInfo = pbView~getImageList
|
|
if imageListInfo <> .nil then imageListInfo~imageList~release
|
|
end
|
|
|
|
return self~ok:super
|
|
|