Chapter 25. Resources

Table of Contents
Image Class
ImageList Class
ResourceImage Class

In the Windows OS, a resource, is binary data used by a Windows-based application. Usually, the binary data is attached to one of the application's executable files (*.exe or *.dll.) However, the binary data can also be generated dynamically in memory. (Which is common in ooDialog, for example the dialog template for a UserDialog is generated in memory.)

The data in standard resources describes things familar to ooDialog programmers like, dialog boxes, icons, menus, cursors, bitmaps, fonts, etc. The standard resources also include accelerator tables, string-table entries, message-table entries, and other resourcess that ooDialog does not currently have support for, but may support in the future.

This chapter describes ooDialog classes that provide access to Windows resources. The classes allow the oodialog programmer to use and manipulate resources in their ooDialog programs. This is an area of ooDialog that is slated for future improvements. The classes listed in the following table are documented in this chapter:

Table 25-1. ooDialog Resource Classes

ClassLink to Description
ImageImage Class
ImageListImageList Class
ResourceImageResourceImage Class

Image Class

The Image class is used to work with and manipulate images. Currently, the image types supported include bitmaps, icons, cursors (cusors are a type of icon,) and enhanced metafiles. The enhanced metafile support is very limited at this time.

The class supports loading images from files, from resources contained in any executable files (*.exe and *.dll,) from the files associated with the ooDialog program, and from the generally available system resources.

Note: The .Image class is the future direction that ooDialog will take for working with images, including bitmaps. This is a more flexible approach than the older bitmap methods used when ooDialog was first developed. It will allow access to more of the modern features of the Windows user interface than the older approach does. The older bitmap methods were designed to work with Windows 3.1 and have a number of limitations. The ooDialog programmer is strongly encouraged to migrate her code towards the .Image class. The older bitmap methods should be considered depracated, to a degree. Unfortunately, replacement methods for all of the older bitmap methods have not as yet been implmented. So, the older methods may still be necessary for some situations.

A loaded image takes up some small part of the systems's resources. It is common to release an image when the programmer is done with it. The .Image class has the release() method to allow the programmer to release the image, if desired. It is important to note that when the ooDialog program ends, that is when the ooRexx interpreter process ends, the Windows operating system cleans up all the system resources associated with any images used in the ooDialog program. Not releasing images does no harm and the ooDialog programmer should not be unduly worried about this aspect of images.

In addition, when images are loaded as shared, the operating system completely manages them. Shared images should not be released. The .Image class tracks which images are loaded as shared and will not call the underlying API to release a shared image. So, again, the ooDialog programmer does not need to worry about mistakenly releasing a shared image. Once an image is released, it is no longer valid and the object can not be used as an argument to methods requiring a valid image. This applies to shared images also, even though they are not actually released.

Why then would the ooDialog programmer want to release images? The main reason would be to minimize the memory footprint of an application. In a normal ooDialog program, with five to ten images, releasing the images would have no noticeable impact on the memory footprint. However, in a long running Rexx program that opened and closed a lot of dialogs that used images, releasing the images as the dialogs were closed would make a difference in the long run. The operating system would not clean up the resources used by the images until the main Rexx program ended. If the main program ran for days, or maybe was never intended to be shut down, it would make sense to release images that were no longer needed.

Requires:

The Image class requires the class definition file ooDialog.cls:

::requires "ooDialog"

Methods:

Instances of the Image class implement the methods listed in the following table:

Table 25-2. Methods of the .Image Class

Method......description
colorRef (Class method)colorRef
fromFiles (Class method)fromFiles
fromIDs (Class method)fromIDs
getBValue (Class method)getBValue
getGValue (Class method)getGValue
getImage (Class method)getImage
getRValue (Class method)getRValue
toID (Class method)toID
new (Class method)new
handlehandle
isNullisNull
releaserelease
systemErrorCodesystemErrorCode

new (Class method)

The Image class does not allow new Image objects to be instantiated from Rexx code using the new() method. New Image objects are obtained through one of the other Image class methods, or they are returned from methods of other classes.

These methods are used to create new Image object(s).

Image (class) method: getImage()
Image (class) method: fromFiles()
Image (class) method: fromIDs()
ResourceImage (instance) method: getImage()
ResourceImage (instance) method: getImages()

toID (Class method)

>>--toID(--symbolicName--)-----------------------------------------><

The toID method is used to translate a symbolic name to its integer value. In general the symbolic name is related to images or color. Many of the arguments to the methods of classes related to images use the integer value of a symbolic ID in the Windows API. This method allows the programmer to use the symbolic ID without knowing what the actual integer value is.

Take for example the task of retrieving the 'Question' icon resource from the system using the getImage() method. The ooDialog programmer could either use the numerical value of 32514 or use the toID method as follows. Note that the two invocations of getImage() are equivalent:

  
    qIcon = .Image~getImage(.Image~toID(IDI_QUESTION))

    qIcon = .Image~getImage(32514)
  
  

The symbolic ID keywords are spelled exactly as Microsoft spells them in the MSDN library which allows the ooDialog to easily look up the meaning of any single ID while at the same time reducing the documentation task for the ooDialog developers. The keywords are case sensitive and must be all in upper-case.

Arguments:

The single required argument is:

symbolicName

The symbolic name whose numeric value is desired. There any number of symbolic names and they are not listed here. Rather the symbolic names are listed in the documentation for the methods they are applicable to.

Return value:

The return value is the numeric value of the symbol.

Example:
    
    say 'The numeric value of the IDI_WINLOGO symbol is:' .Image~toID(IDI_WINLOGO)
    /*
      Output on the console would be:

      The numeric value of the IDI_WINLOGO symbol is: 32517
    */
    
    

getImage (Class method)

>>--getImage(--id--+--------+--+--------+--+---------+--)-----><
                   +-,-type-+  +-,-size-+  +-,-flags-+

Instantiates a new .Image object from either an image file or from one of the system images. When id is a number then the corresponding system image is used. Otherwise, id is taken to be the name of an image file. File names can be either relative or absolute.

Details

Sets the .SystemErrorCode variable.

Raises syntax errors when incorrect arguments are detected.

Provides an interface to the Win32 API: LoadImage(). Use the MSDN library documentation to get more information on the arguments to this method.

Arguments:

id

If not a number, then id must be the name of a stand-alone image file.

If id is a number than it is taken to be the resource id of an image provided by the system. The following are the symbolic names for all the system images. You can use .Image~toID() to get the correct numeric value for any of the following symbols:

IDI_APPLICATIONOCR_NORMALOBM_CLOSEOBM_RGARROWD
IDI_HANDOCR_IBEAMOBM_UPARROWOBM_LFARROWD
IDI_QUESTIONOCR_WAITOBM_DNARROWOBM_MNARROW
IDI_EXCLAMATIONOCR_CROSSOBM_RGARROWOBM_COMBO
IDI_ASTERISKOCR_UPOBM_LFARROWOBM_UPARROWI
IDI_WINLOGOOCR_SIZENWSEOBM_REDUCEOBM_DNARROWI
OCR_SIZENESWOBM_ZOOMOBM_RGARROWI
OCR_SIZEWEOBM_RESTOREOBM_LFARROWI
OCR_SIZENSOBM_REDUCEDOBM_SIZE
OCR_SIZEALLOBM_ZOOMDOBM_BTSIZE
OCR_NOOBM_RESTOREDOBM_CHECK
OCR_HANDOBM_UPARROWDOBM_CHECKBOXES
OCR_APPSTARTINGOBM_DNARROWDOBM_BTNCORNERS

type

Specifies the type of the image: bitmap, icon, or cursor. You can use .Image~toID() to get the correct numeric value for one of the following symbols:

IMAGE_BITMAPIMAGE_ICON
IMAGE_CURSOR 

The default is IMAGE_BITMAP.

size

A .Size object that specifies the size of the image.

The default is a size of 0x0. Under most circumstances this indicates that the actual size of the image should be used. However, the MSDN library documentation should be consulted for other meanings.

flags

The load resource flags for the LoadImage() API. The flags are one or more of the following symbols. You can use .Image~toID() to get the correct numeric value for any of the following symbols. The or method of the .DlgUtil class can be used to combine more than one of the symbols if needed.

LR_DEFAULTCOLORLR_CREATEDIBSECTION
LR_DEFAULTSIZELR_LOADFROMFILE
LR_LOADMAP3DCOLORSLR_LOADTRANSPARENT
LR_MONOCHROMELR_SHARED
LR_VGACOLOR 

When id specifies a file name, the default flags are LR_LOADFROMFILE, othewise the default flags are LR_SHARED | LR_DEFAULTSIZE. Note that the system images must be loaded as shared.

Return value:

A .Image object that represents the image specified. If an error happened, the object may not be valid. Use the isNull() method to check if the image is valid. If there was an error, .SystemErrorCode may help to determine the error.

Example:
    
    flags = .DlgUtil~or(.Image~toID(LR_DEFAULTSIZE), .Image~toID(LR_SHARED), -
                        .Image~toID(LR_LOADMAP3DCOLORS))

    questionIcon = .Image~getImage(.Image~toID(IDI_QUESTION),              -
                                   .Image~toID(IMAGE_ICON),                -
                                   .Size~new(0, 0), flags)
    if questionIcon~isNull then do
      say 'Error getting the question icon.  Error code:' .SystemErrorCode
    end

    /* Note that you can always use the raw numeric value for the args.
     * This works just as well:
     */
    questionIcon = .Image~getImage(32514, 1, .Size~new(0, 0), 36928)
    if questionIcon~isNull then do
      say 'Error getting the question icon.  Error code:' .SystemErrorCode
    end

    
    

fromFiles (Class method)

>>--fromFiles(--files--+--------+--+--------+--+---------+--)----><
                       +-,-type-+  +-,-size-+  +-,-flags-+

Gets an array of .Image objects, using an array of file names. This method is useful to load more than one image at a time, when all the images have the same specifications, type, size, and flags.

Details

Sets the .SystemErrorCode variable.

Raises syntax errors when incorrect arguments are detected.

Provides an interface to the Win32 API: LoadImage(). Use the MSDN library documentation to get more information on the arguments to this method.

Arguments:

files

An array of file names to use to instantiate the .Image objects. The array can contain any number of file names, but it can not be sparse. That is, each index of the array must contain a file name. If an incorrect item is detected in the array, then a syntax error is raised and no images are returned.

On the other hand, if there is an error with the Win32 API loading an image, then no syntax error is raised. The index in the array for that image is left empty. One way to check for this type of error is to compare the number of items in the returned array with the number of items in file name array.

type

Specifies the type of the image, bitmap, icon, or cursor. You can use .Image~toID() to get the correct numeric value for one of the following symbols:

IMAGE_BITMAPIMAGE_ICON
IMAGE_CURSOR 

The default is IMAGE_BITMAP.

size

A .Size object that specifies the size of the image. The default is a size of 0x0. Under most circumstances this indicates that the actual size of the image should be used. However, the MSDN library documentation should be consulted for other meanings.

flags

The load resource flags for the LoadImage() API. The flags are one or more of the following symbols. You can use .Image~toID() to get the correct numeric value for any of the following symbols. The or method of the .DlgUtil class can be used to combine more than one of the symbols if needed.

LR_DEFAULTCOLORLR_CREATEDIBSECTION
LR_DEFAULTSIZELR_LOADFROMFILE
LR_LOADMAP3DCOLORSLR_LOADTRANSPARENT
LR_MONOCHROMELR_SHARED
LR_VGACOLOR 

The default is LR_LOADFROMFILE.

Return value:

The method returns an array of .Image objects, one object for each image that was loaded from a file successfully.

Example:
      


      
      

fromIDs (Class method)

>>--fromIDs(--ids,--+--------+--+--------+--+---------+--)-----><
                    +-,-type-+  +-,-size-+  +-,-flags-+

Uses an array of resource IDs to instantiate and return an array of .Image objects. This method loads the system image resources referenced by the resource IDs in the array. The images must all be the same type and size, and use the same load flags.

Details

Sets the .SystemErrorCode variable.

Raises syntax errors when incorrect arguments are detected.

Provides an interface to the Win32 API: LoadImage(). Use the MSDN library documentation to get more information on the arguments to this method.

Arguments:

ids

An array of resource IDs to use to instantiate the .Image objects. The array can contain any number of IDs, but it can not be sparse. That is, each index of the array must contain a number. If an incorrect item is detected in the array, then a syntax error is raised and no images are returned.

On the other hand, if there is an error with the Win32 API loading an image, then no syntax error is raised. The index in the array for that image is left empty. One way to check for this type of error is to compare the number of items in the returned array with the number of items in ID array.

type

Specifies the type of the image: bitmap, icon, or cursor. You can use .Image~toID() to get the correct numeric value for one of the following symbols:

IMAGE_BITMAPIMAGE_ICON
IMAGE_CURSOR 

The default is IMAGE_ICON

size

A .Size object that specifies the size of the image. The default is a size of 0x0. Under most circumstances this indicates that the actual size of the image should be used. However, the MSDN library documentation should be consulted for other meanings.

flags

The load resource flags for the LoadImage() API. The flags are one or more of the following symbols. You can use .Image~toID() to get the correct numeric value for any of the following symbols. The or method of the .DlgUtil class can be used to combine more than one of the symbols if needed.

LR_DEFAULTCOLORLR_CREATEDIBSECTION
LR_DEFAULTSIZELR_LOADFROMFILE
LR_LOADMAP3DCOLORSLR_LOADTRANSPARENT
LR_MONOCHROMELR_SHARED
LR_VGACOLOR 

The default is LR_SHARED | LR_DEFAULTSIZE.

Return value:

The method returns an array of .Image objects, one object for each system image that was loaded successfully.

Example:
    


    
    

colorRef (Class Method)


>>-.Image~colorRef(--+---+--+-----+--+-----+--)--------------><
                     +-R-+  +-,-G-+  +-,-B-+

The Windows API uses a COLORREF to specify a RGB color. This is a 32-bit number with a hexidecimal format of: 0x00bbggrr. The colorRef() method provides a way to construct a COLORREF from the red, green, and blue values. Each color value (red, green, or blue) is a whole number in the range of 0 to 255 inclusive.

Windows also defines 2 special values for a COLORREF, CLR_NONE and CLR_DEFAULT. To get the numeric value for either of these values use CLR_NONE or CLR_DEFAULT for the R argument. E.g., ref = .Image~colorRef("CLR_NONE").

Arguments:

All the arguments are optional, with 0 being the default for any omitted argument.

R

The red component of the RGB color, or one of the CLR_NONE / CLR_DEFAULT keywords. Case is not significant for either of the two keywords.

G

The green component of the RGB value.

B

The blue component of the RGB value.

Return value:

The return is a valid RGB color, specified as a COLORREF. Some method arguments related to images or colors require a COLORREF.

Example:
 ref = .Image~colorRef(245, 89, 255)
 say 'A fancy purple:' ref '(0x' || ref~d2x~right(8, '0')')'
 say

::requires 'oodPlain.cls'

/* Output:

A fancy purple: 16734709 (0x00FF59F5)

*/

getRValue (Class Method)

>>-.Image~getRValue(--colorRef--)--------------------------><

Returns the red component of a RGB color.

Arguments:

The single argument is:

colorRef

A RGB color, a COLORREF. See the colorRef() method for a brief discussion of these terms.

Return value:

The return is the red component of the specified RGB color.

Example:
    
      progressBar = self~getProgressBar("IDC_PB_FILES")
      if progressBar <> .nil then do
        purple = .Image~colorRef(245, 89, 255)

        say 'Going to set the background color for the progress bar'
        oldColor = progressBar~backgroundColor(purple)

        say 'Old background color was:' oldColor '(0x'oldColor~d2x')'
        say '  Red:  ' .Image~getRValue(oldColor)
        say '  Green:' .Image~getGValue(oldColor)
        say '  Blue: ' .IMage~getBValue(oldColor)
        say
      end

      /* Output might be:

      Going to set the background color for the progress bar
      Old background color was: 11460781 (0xAEE0AD)
        Red:   173
        Green: 224
        Blue:  174

      */
    
    

getGValue (Class Method)

>>-.Image~getGValue(--colorRef--)--------------------------><

Returns the green component of a RGB color.

Arguments:

The single argument is:

colorRef

A RGB color, a COLORREF. See the colorRef() method for a brief discussion of these terms.

Return value:

The return is the green component of the specified RGB color.

Example:

See the example for the getRValue() method.

getBValue (Class Method)

>>-.Image~getBValue(--colorRef--)--------------------------><

Returns the blue component of a RGB color.

Arguments:

The single argument is:

colorRef

A RGB color, a COLORREF. See the colorRef() method for a brief discussion of these terms.

Return value:

The return is the blue component of the specified RGB color.

Example:

See the example for the getRValue() method.

handle

>>--handle----------------------------------------------------><

Returns the Windows system handle to the image this object represents. It is an error to invoke this method if the image is null, or after the image has been released.

Currently, the handle is only useful for display. In the ooDialog framework, methods that use images for arguments, use the .Image object, not the image handle. Older methods that use a bitmap handle for a argument will not work with this handle.

Arguments:

The method does not have an argument.

Return value:

The return is the handle to the image.

Example:
    
    hIcon = .Image~getImage(.Image~toID(IDI_QUESTION), .Image~toID(IMAGE_ICON))
    say 'hIcon:  ' hIcon
    say ' handle:' hIcon~handle

    /* Output to the console might be (on a 64-bit Windows system)

       hIcon:  an Image
       handle: 0x000000000001002B
    */
    
    

release

>>--release----------------------------------------------------><

Releases the image. This will free up operating system resources used for the image. See the introduction to the .Image class for some discussion on releasing an image.

Once an image object has been released, it is an error to use the object. However, the isNull() and systemErrorCode() methods can always be used. The isNull() method will return .true after an image object has been released. Shared images should not be released, the operating system manages them. To prevent the programmer from accidentally releasing a shared image, the .Image object tracks which images are loaded as shared and does not release a shared image if the programmer requests it.

Details

Sets the .SystemErrorCode variable.

Arguments:

There are no arguments.

Return value:

The possible return values are:

0

No error detected.

non-zero

The operating system error code.

Example:

In this example, when the user presses the Test button, the current image for the static control is replaced by the system question mark image. The old image is no longer needed and it is released.

      
      ::method onTest

        iconControl = self~getStaticControl(IDC_ICON_QUESTION)
        if iconControl <> .nil then do
          hQuestion = .Image~getImage(.Image~toID(IDI_QUESTION), .Image~toID(IMAGE_ICON))
          say 'Question icon:' hQuestion~handle
          if hQuestion~isNull then do
            say 'errror code:' .SystemErrorCode
          end
          else do
            image = iconControl~setImage(hQuestion)
            say 'Swapped images:'
            say '  new icon:' hQuestion~handle
            say '  old icon:' image~handle

            -- The old image is no longer needed.
            ret = image~release
            say 'Released old image return:' ret '.SystemErrorCode:' .SystemErrorCode
          end
        end

        /* Output on the screen, on a 64-bit system, might be:

         Question icon: 0x000000000001002B
         Swapped images:
           new icon: 0x000000000001002B
           old icon: 0x0000000001120639
         Released old image return: 0 .SystemErrorCode: 0

        */
      
      

isNull

>>--isNull----------------------------------------------------><

The isNull() method tests if the image object is valid. The image will be null if an error occurred when it was loaded, or if the image has been released.

Arguments:

There are no arguments.

Return value:

The method returns .true if the image is not valid, is null, and .false if the image is not null.

Example:

In this example the programmer wanted to load the system question mark icon, but used the wrong image type. (He uses IMAGE_BITMAP instead of IMAGE_ICON.) The system will refuse to load the image. To test if the image was loaded okay, use the isNull() method. Note that the getImage() method set the .SystemErrorCode. As in all software, the error codes are not always that informative.

      
      hQuestion = .Image~getImage(.Image~toID(IDI_QUESTION), .Image~toID(IMAGE_BITMAP))
      if hQuestion~isNull then do
        say 'System Errror code:' .SystemErrorCode
        say '  System message:  ' SysGetErrorText(.SystemErrorCode)
      end

      /* Output would be:

      System Errror code: 1814
        System message:   The specified resource name cannot be found in the image file.

      */
      
      

systemErrorCode

>>--systemErrorCode----------------------------------------------------><

The systemErrorCode attribute of the image object will reflect any system error codes that are detected while working with an image object. This is the same error code as the .SystemErrorCode is set to. This can be useful if the programmer wants to check for an error code at some point when it is possible that .SystemErrorCode has been reset. (See the example below.)

Like the isNull() method this method can be invoked even when the image is not valid. However, it should not be used to check if the image is valid because it is possible for the value to be 0 and the image to be null.

Arguments:

There are no arguments to this method

Return value:

The value is usually 0, but may be a system error code if one was detected.

Example:

In this example the system error code attribute for an image is checked at a point in the life-cycle of the application when checking the .SystemErrorCode would be meaningless:

      
      ::method onYes
        expose hIcon

        -- Need to release the icon image, but it is not always valid.
        if \ hIcon~isNull then hIcon~release
        else do
          say 'The icon image is not valid.'
          say '  Error when it was loaded:' hIcon~systemErrorCode
          say '  System message:          ' SysGetErrorText(hIcon~systemErrorCode)
        end
        return self~ok:super

      /* Output might be:

      The icon image is not valid.
        Error when it was loaded: 2
        System message:           The system cannot find the file specified.

      */

      /* Note that the below was the error that caused the above.  The file name
       * is actually 'shaveIce.ico' not shavedIce.ico.
       */

        if iconControl <> .nil then do
          hIcon = .Image~getImage("shavedIce.ico", .Image~toID(IMAGE_ICON))
          ...