rexx-things/modules/windows/oodialog/userGuide/exercises/Exercise05/ProductView.rex

481 lines
21 KiB
Rexx
Raw Normal View History

2025-03-12 20:50:48 +00:00
/*----------------------------------------------------------------------------*/
/* */
/* Copyright (c) 2011-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. */
/* */
/*----------------------------------------------------------------------------*/
/* ooDialog User Guide
Exercise 05: ProductView.rex - The ProductView component v01-00 03Jun12
Contains: classes "ProductView" and "AboutDialog".
Pre-requisites: ProductView.dll, ProductView.h.
Support\NumberOnlyEditEx.cls (copied from ooDialog Samples)
Description: A sample Product View component - part of the sample
Order Management application.
Changes:
v01-00 03Jun12: First version.
------------------------------------------------------------------------------*/
::requires "ooDialog.cls"
::requires "Support\NumberOnlyEditEx.cls"
::requires "ProductModelData.rex"
/*//////////////////////////////////////////////////////////////////////////////
==============================================================================
ProductView v01-00 03Jun12
-----------
The "view" part of the Product business component.
Changes:
v01-00 03Jun12: First version.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
::CLASS ProductView SUBCLASS ResDialog PUBLIC
::ATTRIBUTE dialogState PRIVATE -- States are: 'closable' or 'inUpdate".
/*----------------------------------------------------------------------------
Class Methods:
--------------------------------------------------------------------------*/
::METHOD newInstance CLASS PUBLIC UNGUARDED
say ".ProductView-newInstance-01: Start."
-- Enable use of symbolic IDs in menu creation, and turn off AutoDetection
-- (the third parameter:
.Application~setDefaults("O", "ProductView.h", .false)
-- Create an instance of ProductView and show it:
dlg = .ProductView~new("res\ProductView.dll", IDD_PRODUCT_VIEW)
say ".ProductView-newInstance-02: dlg~Activate."
dlg~activate
/*----------------------------------------------------------------------------
Instance Methods:
--------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
Dialog Setup Methods:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD init
say "ProductView-init-01."
-- called first (result of .ProductView~new)
forward class (super) continue
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD activate UNGUARDED
say "ProductView-activate-01."
self~dialogState = "closable"
self~execute("SHOWTOP","IDB_PROD_ICON")
return
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD initDialog
expose menuBar prodControls prodData
say "ProductView-initDialog-01"
menuBar = .BinaryMenuBar~new(self, IDR_PRODUCT_VIEW_MENU, , self, .true)
prodControls = .Directory~new
prodControls[ecProdNo] = self~newEdit("IDC_PROD_NO")
prodControls[ecProdName] = self~newEdit("IDC_PROD_NAME")
prodControls[ecProdPrice] = self~newEdit("IDC_PROD_LIST_PRICE")
prodControls[ecUOM] = self~newEdit("IDC_PROD_UOM")
prodControls[ecProdDescr] = self~newEdit("IDC_PROD_DESCRIPTION")
prodControls[gbSizes] = self~newEdit("IDC_PROD_SIZE_GROUP") -- Do we ever need this?
prodControls[rbSmall] = self~newRadioButton("IDC_PROD_RADIO_SMALL")
prodControls[rbMedium] = self~newRadioButton("IDC_PROD_RADIO_MEDIUM")
prodControls[rbLarge] = self~newRadioButton("IDC_PROD_RADIO_LARGE")
prodControls[pbSaveChanges] = self~newPushButton("IDC_PROD_SAVE_CHANGES")
self~connectButtonEvent("IDC_PROD_SAVE_CHANGES","CLICKED",saveChanges)
-- Use NumberOnlyEditEx.cls to enforce numeric only entry for Price and UOM:
prodControls[ecProdPrice]~initDecimalOnly(2,.false) -- 2 decimal places, no sign.
prodControls[ecUOM]~initDecimalOnly(0,.false) -- 0 decimal places, no sign.
prodControls[ecProdPrice]~connectCharEvent(onChar)
prodControls[ecUOM]~connectCharEvent(onChar)
prodData = self~getData -- Gets data from ProductModel into prodData
self~showData -- Show the data
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*----------------------------------------------------------------------------
Event Handler Methods - MenuBar Events:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD updateProduct UNGUARDED
expose prodControls
say "ProductView-updateProduct-01."
-- Enable the controls to allow changes to the data:
prodControls[ecProdName]~setReadOnly(.false)
prodControls[ecProdPrice]~setReadOnly(.false)
prodControls[ecUOM]~setReadOnly(.false)
prodControls[ecProdDescr]~setReadOnly(.false)
prodControls[rbSmall]~enable
prodControls[rbMedium]~enable
prodControls[rbLarge]~enable
self~enableControl("IDC_PROD_SAVE_CHANGES")
prodControls[pbSaveChanges]~state = "FOCUS" -- Put input focus on the button
self~tabToNext() -- put text cursor on Product Description
-- (as if the user had pressed tab)
self~dialogState = "inUpdate"
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD refreshData UNGUARDED
self~disableControl("IDC_PROD_SAVE_CHANGES")
self~showData
self~dialogState = "closable"
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD print UNGUARDED
say "ProductView-print-01"
ret = InfoDialog(.HRS~printMsg)
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD close UNGUARDED
say "ProductView-close-01"
self~cancel
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD about UNGUARDED
say "ProductView-about-01"
dlg = .AboutDialog~new("ProductView.dll", IDD_PRODUCT_VIEW_ABOUT)
dlg~execute("SHOWTOP")
/*----------------------------------------------------------------------------
Event Handler Methods - PushButton Events:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"Save Changes" - Collects new data, checks if there has indeed been a
change, and if not, issues a warning msg. Disables the
button when valid changes made. */
::METHOD saveChanges UNGUARDED
expose prodControls prodData
-- Transform data from view format (as in controls) to app format (a directory):
newProdData = self~xformView2App(prodControls)
-- Check if anything's changed; if not, show msgbox and exit with controls enabled.
-- If changed, go on to validate data.
result = self~checkForChanges(newProdData)
if result = .false then do
msg = .HRS~nilSaved
hwnd = self~dlgHandle
answer = MessageDialog(msg,hwnd,.HRS~updateProd,"OK","WARNING","DEFBUTTON2 APPLMODAL")
return
end
-- Now validate data:
result = self~validate(newProdData) -- returns a null string or error messages.
-- Better would be a set of error numbers.
-- If no problems, then show msgbox and go on to disable controls.
if result = "" then do
msg = .HRS~saved
hwnd = self~dlgHandle
answer = MessageDialog(msg,hwnd,.HRS~updateProd,"OK","INFORMATION","DEFBUTTON1 APPLMODAL")
end
-- If problems, then show msgbox and leave user to try again or refresh or exit.
else do
msg = result||.EndOfLine||.HRS~notSaved
hwnd = self~dlgHandle
answer = MessageDialog(msg,hwnd,.HRS~updateProd,"OK","ERROR","DEFBUTTON1 APPLMODAL")
return
end
-- Send new data to be checked by CustomerModel object (not implemented).
-- Disable controls that were enabled by menu "ActionsFile-->Update" selection:
prodControls[ecProdName]~setReadOnly(.true)
prodControls[ecProdDescr]~setReadOnly(.true)
prodControls[ecProdPrice]~setReadOnly(.true)
prodControls[ecUom]~setReadOnly(.true)
if newProdData~size \= "S" then prodControls[rbSmall]~disable
if newProdData~size \= "M" then prodControls[rbMedium]~disable
if newProdData~size \= "L" then prodControls[rbLarge]~disable
self~disableControl("IDC_PROD_SAVE_CHANGES")
self~dialogState = "closable"
prodData = newProdData
prodData~list
return
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*----------------------------------------------------------------------------
Event Handler Methods - Keyboard Events:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD onChar UNGUARDED
-- called for each character entered in the price or UOM fields.
forward to (arg(6))
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD cancel
-- If in the process of updating, then ask whether any changes should be
-- thrown away and dialog closed. If yes then close by calling the superclass,
-- else nop. If not in update, then close immediately
say "ProductView-cancel-01."
if self~dialogState = "inUpdate" then do
ans = MessageDialog(.HRS~closeInUpdate, self~dlgHandle, .HRS~updateIP, "YESNO", "WARNING", "DEFBUTTON2")
if ans = .PlainBaseDialog~IDYES then return self~cancel:super
else nop
end
else return self~cancel:super
/*----------------------------------------------------------------------------
Application Methods:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD getData
-- Get data from the ProductModel:
--expose prodData
say "ProductView-getData-01."
idProductModel = .local~my.idProductModel
prodData = idProductModel~query -- prodData is of type ProductDT
return prodData
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD showData
-- Transfrom data (where necessary) to display format, and then disable controls.
expose prodControls prodData
say "ProductView-showData-01."
-- Set data in controls:
prodControls[ecProdNo]~setText( prodData~number )
prodControls[ecProdName]~setText( prodData~name )
-- Price in prodData has no decimal point - 2 decimal places are implied - hence /100 for display.
prodControls[ecProdPrice]~setText(prodData~price/100 )
prodControls[ecUOM]~settext( proddata~uom )
prodControls[ecProdDescr]~setText(prodData~description )
size = prodData~size
-- Disable controls
prodControls[ecProdName]~setReadOnly(.true)
prodControls[ecProdPrice]~setReadOnly(.true)
prodControls[ecUOM]~setReadOnly(.true)
prodControls[ecProdDescr]~setReadOnly(.true)
prodControls[rbSmall]~disable
prodControls[rbMedium]~disable
prodControls[rbLarge]~disable
-- But check correct button and enable it to highlight it to the user:
select
when size = "S" then do
.RadioButton~checkInGroup(self,"IDC_PROD_RADIO_SMALL","IDC_PROD_RADIO_LARGE","IDC_PROD_RADIO_SMALL")
prodcontrols[rbSmall]~enable
end
when size = "M" then do
.RadioButton~checkInGroup(self,"IDC_PROD_RADIO_SMALL","IDC_PROD_RADIO_LARGE","IDC_PROD_RADIO_MEDIUM")
prodcontrols[rbMedium]~enable
end
otherwise do
.RadioButton~checkInGroup(self,"IDC_PROD_RADIO_SMALL","IDC_PROD_RADIO_LARGE","IDC_PROD_RADIO_LARGE")
prodcontrols[rbLarge]~enable
end
end
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD checkForChanges
-- Check whether any data has actually changed when "Save Changes" button
-- has been pressed. Return .true if data changed, else returns .false.
expose prodData
use arg newProdData
changed = .false
select
when newProdData~name \= prodData~name then changed = .true
when newProdData~price \= prodData~price then changed = .true
when newProdData~uom \= prodData~uom then changed = .true
when newProdData~description \= prodData~description then changed = .true
when newProdData~size \= ProdData~size then changed = .true
otherwise nop
end
return changed
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD validate
-- Validation: 1. Check price/UOM not changed > 50% up or down.
-- 2. Cannot change from Large to Small without UOM increasing
-- by at least 100%; nor from Small to Large without
-- decreasing by more than 50%.
-- 3. Product Description <= 100 characters.
-- 4. Product Name <= 30 characters.
-- Returns string of messages - the null string if all OK.
expose prodData
use arg newProdData
msg = ""
-- Check Price (catches decimal point errors also):
price = prodData~price; newPrice = newProdData~price
oldUom = prodData~uom; newUom = newProdData~uom -- 'oldUom - avoids name conflict with 'uom' in newProddata~uom.
if ((price/oldUom)*1.5 < newPrice/newUom) | (newPrice/newUom < (price/oldUom)/2) then do
msg = msg||.HRS~badRatio||" "
end
-- Check Size vs UOM:
if prodData~size = "L" & newProdData~size = "S" - -- Large to Small
& prodData~uom/2 < newProdData~uom then do
msg = msg||.HRS~uomTooBig||" "
end
if prodData~size = "S" & newProdData~size = "L" - -- Small to Large
& prodData~uom*2 > newProdData~uom then do
msg = msg||.HRS~uomTooSmall||" "
end
-- Check Product Description length:
if newProdData.description~length > 80 then do
msg = msg||.HRS~descrTooBig||" "
end
-- Check Product Name length:
if newProdData~name~length > 30 then do
msg = msg||.HRS~prodNameTooBig
end
return msg
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::METHOD xformView2App
-- Transforms Product Data from View form (in the GUI controls) to
-- App form (a directory with address as an array).
expose prodControls
prodData = .ProductDT~new
prodData~number = prodControls[ecProdNo]~gettext()
prodData~name = prodControls[ecProdName]~getText()
price = prodControls[ecProdPrice]~getText()
-- Data entered has or assumes a decimal point; but data in "application"
-- is a whole number (e.g. $42.42 is recorded in the database as "4242").
-- So re-format data from decimal to whole number:
priceTwoDecs = price~format(,2) -- force 2 dec positions
-- Re-display price properly formatted (in case the user did not format correctly - e.g. entered "42" or "38.4"):
prodControls[ecProdPrice]~setText(priceTwoDecs)
-- Now format price to "application" format:
price = (priceTwoDecs*100)~format(,0) -- multiply by 100 and then force whole number.
prodData~price = price
prodData~uom = prodControls[ecUOM]~getText()
prodData~description = prodControls[ecProdDescr]~getText()
select
when prodControls[rbSmall]~checked then prodData~size = "S"
when prodControls[rbMedium]~checked then prodData~size = "M"
otherwise prodData~size = "L"
end
return prodData
/*============================================================================*/
/*//////////////////////////////////////////////////////////////////////////////
==============================================================================
AboutDialog v01-00 03Jun12
-------------
The "About" class - shows a dialog box that includes a bitmap - part of the
ProductView component.
Changes:
v01-00 03Jun12: First version.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
::CLASS AboutDialog SUBCLASS ResDialog
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::method initDialog
expose font image
resImage = .ResourceImage~new(self) -- Create an instance of a resource image
image = resImage~getImage(IDB_PROD_ICON) -- Create an image from the Product bitmap
stImage = self~newStatic(IDC_PRODABT_ICON_PLACE)~setImage(image) -- Create a static text control and set the image in it
font = self~createFontEx("Ariel", 12) -- Create up a largish font with which to display text and ...
self~newStatic(IDC_PRODABT_STATIC2)~setFont(font) -- ... set the static text to use that font.
-- Provide for a double-click in Product icon:
self~connectStaticNotify("IDC_PRODABT_ICON_PLACE", "DBLCLK", showMsgBox)
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::method showMsgBox
say "AboutDialog-showMsgBox-01."
ans = MessageDialog(.HRS~aboutDblClick)
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
::method leaving
expose font image
self~deleteFont(font)
image~release()
/*============================================================================*/
/*//////////////////////////////////////////////////////////////////////////////
==============================================================================
Human-Readable Strings (HRS) v01-00 03Jun12
--------
The HRS class provides constant character strings for user-visible messages.
Changes:
v01-00 03Jun12: First version.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
::CLASS HRS PRIVATE -- Human-Readable Strings
::CONSTANT aboutDblClick "You double-clicked!"
::CONSTANT badRatio "The new price/UOM ratio cannot be changed more than 50% up or down."
::CONSTANT closeInUpdate "Any changes made will be lost. Exit anyway?"
::CONSTANT descrTooBig "The Product Description is too long."
::CONSTANT nilSaved "Nothing was changed! Data not saved."
::CONSTANT notSaved "Changes Not Saved!"
::CONSTANT prodNameTooBig "The Product Name is too long."
::CONSTANT saved "Changes saved."
::CONSTANT uomTooBig "The new UOM is too large."
::CONSTANT uomTooSmall "The new UOM is too small."
::CONSTANT updateIP "Update in process"
::CONSTANT updateProd "Update Product"
::CONSTANT printMsg "The 'Print...' menu item is not yet implemented."
/*============================================================================*/