rexx-things/modules/windows/oodialog/examples/imageButton.rex
2025-03-12 20:50:48 +00:00

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