/*----------------------------------------------------------------------------*/ /* */ /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */ /* Copyright (c) 2005-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. */ /* */ /*----------------------------------------------------------------------------*/ /** Propery Sheet / Wizard97 example * * This example demonstrates how to use the PropertySheetDialog to create a * Wizard. There are two main wizard styles for a property sheet, the Wizard97 * style and the Aero style. Wizard97 will run on any Windows version supported * by ooRexx. The Aero wizard needs Windows Vista or later. * * This example creates all the pages as UserDialogs where the programmer lays * each dialog template in the defineDialog() method. This is really rather * tedious. It is much easier to use a resource editor, especially here where * the programmer wants to create a number of page dialogs that are all the same * size. * * However, it is a good opportunity to give examples of how to correctly * convert between pixels and dialog units in the defineDialog() method to get * the dialog template laid out correctly when different fonts are used for the * dialog. To facilate that part of the example, the user is allowed to pick * different fonts and font sizes for the wizard before it is displayed. This * may not be the best sample program for that, because it is not possible, at * this time, to change the font of the wizard itself. * * This is a rather complex program. The commenting is not 100%. Be sure to * read the ooDialog documentation on the PropertySheetDialog to fully under- * stand this program. * * Note: this program uses the public routine, locate(), to get the full path * name to the directory this source code file is located. In places, the * variable holding this value has been callously abbreviated to 'sd' which * stands for source directory. * */ sd = locate() .application~useGlobalConstDir('O', sd"rc\ticketWizard.h") -- Let the user select a font for the example program. oldFont = setFont() -- We show a timed message to explain what the upcoming example is about. -- This is a hold-over from the original ooRexx examples. The reality is, it -- gets rather annoying after the first 2 or 3 times you run the example. I -- would prefer to eliminate this code. msg = "The upcoming dialog demonstrates a Wizard 97 style dialog." title = "Wizard 97 Dialog" dlg = .TimedMessage~new(msg, title, 2000) dlg~execute -- First we create the dialogs for each page, and set any of the attributes -- we need to. We want each dialog to be the same size. outPageSize = .Size~new(267, 193) inPageSize = .Size~new(267, 143) intro = .IntroDlg~new( , , "", "ooRexx Movie Ticket Selectitron") intro~setSize(outPageSize) p1 = .MoviesDlg~new( , , "", "Movies") p1~setSize(inPageSize) p1~headerTitle = "Select the movie(s) you want tickets for." p1~headerSubtitle = "When a movie is selected, information on the movie, such as director, actors, " - "etc., will also be displayed." p2 = .CinemasDlg~new( , , "", "Cinemas" ) p2~setSize(inPageSize) p2~headerTitle = "Pick a movie theater for the movie(s)." p2~headerSubtitle = "Pick a theater for each movie you've selected. Use the drop down list to switch movies. " - "Theaters in your vicinity showing the movie(s) this week are listed. " p3 = .DaysDlg~new( , , "", "Days" ) p3~setSize(inPageSize) p3~headerTitle = "Choose a day to watch the movie(s)." p3~headerSubtitle = "Use the drop down list to switch movies. Pick the day most suitable for the movie." p4 = .TicketDlg~new( , , "", "Ticket Information" ) p4~setSize(inPageSize) p4~headerTitle = "Validate your selections and indicate the time desired." p4~headerSubtitle = "Pick a time for each movie selected. Ticket information for each movie can be displayed " - "using the drop down list. To change any selections use the Back button." complete = .CompletionDlg~new( , , "", "Enjoy Your Movie") complete~setSize(outPageSize) pages = .array~of(intro, p1, p2, p3, p4, complete) -- Create the property sheet using the dialog pages and set its attributes. wizDlg = .TicketWizard97~new(pages, "Wizard97", "Let's Go To The Movies") wizDlg~header = .Image~getImage(sd"rc\ticketWizardTheater.bmp") wizDlg~watermark = .Image~getImage(sd"rc\ticketWizardRexxLA.bmp") -- Execute the wizard. wizDlg~execute ret = restoreFont(oldFont) return ::requires "ooDialog.cls" -- TicketWizard97 -- The property sheet for the example program. Although the -- PropertySheetDialog has a number of methods, we do not need to over-ride -- any of the methods. So, we just need the class definition line here. ::class 'TicketWizard97' subclass PropertySheetDialog -- IntroDlg -- Dialog for first page (exterior page, introduction.) ::class 'IntroDlg' subclass UserPSPDialog ::method defineDialog -- Watermark bitmap is 164 x 316. The wizard does not stretch the bitmap, -- its size remains constant. So, the size taken up by the bitmap, in -- dialog units, changes as the font size changes. Use pixel2dlgUnit() to -- convert the pixel size of the watermark to the correct dialog units for -- the font this dialog is using. s = .Size~new(164, 314) self~pixel2dlgUnit(s) x = s~width + 6 y = 8 -- From the Wizard97 specification. cx = self~sizeX - x -- To the right side of the template, the wizard automatically adds 14 dlus. cy = 3 * 12 -- Using 12 point font, takes 3 lines for the msg. self~createStaticText(IDC_ST_INTRO_MSG, x, y, cx, cy, "", "") y += cy + 8 -- Space the next static 8 dlus below the first. cy = 8 * 8 -- Arbitray height to fit the info text. self~createStaticText(IDC_ST_INTRO_INFO, x, y, cx, cy, "", "") cy = 8 -- Normal font size height. y = self~sizeY - 7 - cy -- From the Wizard97 specification, leave 7 dlus margin on bottom. self~createStaticText(IDC_ST_INTRO_LAST_MSG, x, y, cx, cy, "", "") ::method setActive unguarded use arg propSheet -- Back button disabled, Next button enabled. propSheet~setWizButtons("NEXT") return 0 ::method initDialog msg = "Welcome to the ooRexx Movie Ticket Selectitron Wizard" font1 = self~createFontEx("Verdana", 12, "BOLD") -- From the Wizard97 specification. st1 = self~newStatic(IDC_ST_INTRO_MSG) st1~setFont(font1) st1~setText(msg) info = "This wizard will help you select a ticket, or tickets, for the movie(s) of your choice. For each" - "ticket you will specify the theater and time for the movie you wish to see." || .endOfLine~copies(2) || - "Tickets for any movies playing in the upcoming week are available. Select as many" - "movies as you wish to see." self~newStatic(IDC_ST_INTRO_INFO)~setText(info) msg = "Select the Next button to continue" self~newStatic(IDC_ST_INTRO_LAST_MSG)~setText(msg) ::method initAutoDetection self~noAutoDetection -- MoviesDlg -- Dialog for second page (interior page, movie selection.) ::class 'MoviesDlg' subclass UserPSPDialog ::method defineDialog x = 21 -- From the Wizard97 specification. y = 1 -- From the Wizard97 specification, the Wizard -- automatically adds a 7 dlu marging on all sides. self~createGroupBox(-1, x, y, 113, 125, , "The upcoming movies") self~createListBox(31, x + 10, y + 15, 93, 105, "MULTI NOTIFY GROUP", "MovieList") x += 125 + 10 self~createStaticText(-1, x, y, 60, 8, , "Produced by:") self~createEdit(32, x, y + 12, 84, 12, "READONLY GROUP", "Produced") y += 15 * 2 self~createStaticText(-1, x, y, 60, 8, , "Starring:") self~createEdit(33, x, y + 12, 84, 12, "READONLY GROUP", "Star") y += 15 * 2 self~createStaticText(-1, x, y, 60, 8, , "Director:") self~createEdit(34, x, y + 12, 84, 12, "READONLY GROUP", "Director") y += 15 * 2 self~createStaticText(-1, x, y, 60, 8, , "With:") self~createComboBox(35, x, y + 12, 84, 33, "LIST GROUP", "COMBOBOX") self~connectListBoxEvent(31, "SELCHANGE", "onMovieClick") ::method pageCreate unguarded self~setMovieData return .true ::method setMovieData private expose movieData films lastSelected films = .array~of("Vertigo", "Taxi Driver", "Superman", "Larger than Life", - "Hair", "Cinderella", "True Lies", "E.T.", "Twister", "Lawnmower Duel") movieData = .table~new do i = 1 to films~items d = .directory~new movieData[films[i]] = d end d = movieData[films[1]] d~produced = "Paramount"; d~star = "Kim Novak"; d~director = "Alfred Hitchcock" d~with = .array~of("James Stewart") d = movieData[films[2]] d~produced = "Universal"; d~star = "Robert De Niro"; d~director = "Martin Scorsese" d~with = .array~of("Jodie Foster", 'Albert Brooks', 'Harvey Keitel', 'Cybill Shepherd') d = movieData[films[3]] d~produced = "Warner Bros"; d~star = "Christopher Reeves"; d~director = "Richard Donner" d~with = .array~of('Marlon Brando', 'Gene Hackman', 'Margot Kidder') d = movieData[films[4]] d~produced = "United Artists"; d~star = "The Elephant"; d~director = "Howard Franklin" d~with = .array~of("Bill Murray") d = movieData[films[5]] d~produced = "Hippies"; d~star = "John Savage"; d~director = "Milos Forman" d~with = .array~of("Beverly D'Angelo", 'Dorsey Wright', 'Cheryl Barnes', 'Treat Williams', 'Annie Golden') d = movieData[films[6]] d~produced = "Disney"; d~star = "Cinderella"; d~director = "Walt" d~with = .array~of('Step mother', 'Witch', 'Prince', 'King') d = movieData[films[7]] d~produced = "20. Century Fox"; d~star = "Arnold Schwarzenegger"; d~director = "James Cameron" d~with = .array~of('Jamie Lee Curtis', 'Tom Arnold', 'Richard Harris') d = movieData[films[8]] d~produced = "United Artists"; d~star = "The Extra-Terrestrial"; d~director = "Steven Spielberg" d~with = .array~of('Henry Thomas', 'Drew Barrymore', 'Dee Wallace', 'Peter Coyote') d = movieData[films[9]] d~produced = "Warner Bros"; d~star = "Helen Hunt"; d~director = "Jan De Bont" d~with = .array~of("Bill Paxten") d = movieData[films[10]] d~produced= "Sunset Mowers"; d~star = "The Lawn Mower"; d~director = "Mower Pusher" d~with = .array~of('Lawn', 'Garden hose', 'Sprinkler', 'Shredder') self~movieList = 0 ::method setActive unguarded use arg propSheet propSheet~setWizButtons("BACK NEXT") return 0 -- wizNext() -- The user clicked the next button. If the user does not pick a movie, we -- don't let her proceed to the next page. There is no point in using the -- wizard without selecting a movie. We use wizNext() rather than -- killActive() because killActive is invoked both when the user clicks the -- Back and the Next buttons. If the user wants to go back to the intor page, -- we don't want to put up an annoying message box. ::method wizNext expose lb use arg propSheet count = lb~selectedItems if count < 1 then do msg = "To purchase a ticket, you must select at".endOfLine"least one movie." j = MessageDialog(msg, propSheet~hwnd, "ooRexx Movie Ticket Selectitron", "OK", "INFORMATION") return -1 end return 0 ::method initDialog expose films lb lb = self~newListBox(31) do i = 1 to films~items lb~add(films[i]) end lb~deselectIndex ::method onMovieClick unguarded expose movieData use arg , , id, nCode, listBox movieName = listBox~selected combo = self~newComboBox(35) combo~deleteAll -- If no movie in the list box is selected, set eveything blank, otherwise, -- set everything to match the selected movie. if movieName == "" then do self~newEdit(32)~setText("") self~newEdit(33)~setText("") self~newEdit(34)~setText("") combo~add("") end else do d = movieData[movieName] self~newEdit(32)~setText(d~produced) self~newEdit(33)~setText(d~star) self~newEdit(34)~setText(d~director) do n over d~with combo~add(n) end combo~selectIndex(1) end return 0 ::method queryFromSibling unguarded expose lb films use arg arg1, arg2, propSheet select when arg2 == "FILMS" then do -- arg1 is an array that we fill in with all the movies arg1~empty do i = 1 to films~items arg1[i] = films[i] end end when arg2 == "SELECTED" then do -- arg1 is a table, we set an index for each selected movie. Note that -- the item for the arg1~empty currentSelectedMovies = self~getSelectedMovies do i = 1 to currentSelectedMovies~items arg1[currentSelectedMovies[i]] = .true end end when arg2 == "DATA" then do -- arg1 is a table whose indexes are the films. Each item is a directory -- indexes are theater, date, and time. The other pages fill in the -- directory. We just make sure only the selected movies are in the -- table. arg1~empty currentSelectedMovies = self~getSelectedMovies do movie over currentSelectedMovies d = .directory~new arg1[movie] = d end end otherwise do -- We do not handle any other queries. nop end end -- End select return 0 ::method getSelectedMovies unguarded expose lb count = lb~selectedItems if count < 1 then return .array~new a = .array~new(count) indexes = lb~selectedIndexes do i over indexes~makeArray(" ") a~append(lb~getText(i)) end return a -- CinemasDlg -- Dialog for the third page (interior page, theater selection.) ::class 'CinemasDlg' subclass UserPSPdialog ::method defineDialog expose buttonTable x = 21; y = 1 self~createStaticText(-1, x, y, 100, 8, , "Selected movies:") self~createComboBox(IDC_COMBO_CINEMAS, x, y + 12, 170, 75, "LIST SORT", "COMBOBOX") y += 15 * 2 self~createGroupBox(-1, x, y, 170, 105, "", "Theaters") y += 10; x += 10 self~createRadioButton(IDC_RB_CINEMAS_UPTOWN, x, y, 145, 12, "TABSTOP", "&Uptown San Jose Cinemas") y += 15 self~createRadioButton(IDC_RB_CINEMAS_PALO, x, y, 145, 12, "", "&Palo Alto News Scene") y += 15 self~createRadioButton(IDC_RB_CINEMAS_CHARLIES, x, y, 145, 12, "", "&Charlie's Suburban Barn Theater") y += 15 self~createRadioButton(IDC_RB_CINEMAS_DRIVEIN, x, y, 145, 12, "", "&Drive-in above Highway 101") y += 15 self~createRadioButton(IDC_RB_CINEMAS_GRAND, x, y, 145, 12, "", "&Grand Black n White Movies Monterey") y += 15 self~createRadioButton(IDC_RB_CINEMAS_BROADWAY, x, y, 145, 12, "", "&Broadway Cinema San Francisco") first = self~resolveSymbolicID(IDC_RB_CINEMAS_FIRST) last = self~resolveSymbolicID(IDC_RB_CINEMAS_LAST) self~connectComboBoxEvent(IDC_COMBO_CINEMAS, "SELENDOK", "onComboUpdate") do i = first to last self~connectButtonEvent(i, "CLICKED", onRBClick) end buttonTable = .table~new buttonTable["Uptown San Jose Cinemas"] = IDC_RB_CINEMAS_UPTOWN buttonTable["Palo Alto News Scene"] = IDC_RB_CINEMAS_PALO buttonTable["Charlie's Suburban Barn Theater"] = IDC_RB_CINEMAS_CHARLIES buttonTable["Drive-in above Highway 101"] = IDC_RB_CINEMAS_DRIVEIN buttonTable["Grand Black n White Movies Monterey"] = IDC_RB_CINEMAS_GRAND buttonTable["Broadway Cinema San Francisco"] = IDC_RB_CINEMAS_BROADWAY ::method initDialog expose filmArray movieTheaters selectedMovies movieCombo filmArray = .array~new(20) self~propSheet~querySiblings(filmArray, "FILMS") movieTheaters = .table~new do movie over filmArray movieTheaters[movie] = .nil end selectedMovies = .array~new movieCombo = self~newComboBox(IDC_COMBO_CINEMAS) ::method setActive unguarded expose filmArray movieTheaters selectedMovies movieCombo use arg propSheet propSheet~setWizButtons("BACK NEXT") selected = .table~new selectedMovies~empty propSheet~querySiblings(selected, "SELECTED") movieCombo~deleteAll -- For every movie, check if it is now selected. If it is, we add it to the -- selected moveies combo box. If it isn't, we mark that movie as not having -- a movie theater selected. We don't care what the previous setting was. do movie over filmArray if selected~hasIndex(movie) then do movieCombo~add(movie) selectedMovies~append(movie) end else do movieTheaters[movie] = .nil end end if selected~items > 0 then do movieCombo~selectIndex(1) movie = movieCombo~selected self~setRadioButtons(movie) end return 0 -- wizNext() -- The user clicked the next button. If there is not a theater selected for -- each selected movie, we warn the user. We use wizNext() rather than -- killActive() because killActive is invoked both when the user clicks the -- Back and the Next buttons. If the user is going back, we don't want to put -- up an annoying message box. ::method wizNext unguarded expose selectedMovies movieTheaters movieCombo use arg propSheet do movie over selectedMovies if movieTheaters[movie] == .nil then do msg = "Warning. You have not selected a movie theater for all".endOfLine"selected movies." || - .endOfLine~copies(2) || "Are you sure you want to go to the next page?" title = "Theater Selection is not Complete" buttons = "YESNO" icon = "WARNING" style = "DEFBUTTON2 TASKMODAL" ans = MessageDialog(msg, propSheet~dlgHandle, title, buttons, icon, style) if ans == .PlainBaseDialog~IDYES then return 0 movieCombo~select(movie) self~setRadioButtons(movie) return -1 end end return 0 ::method queryFromSibling unguarded expose movieTheaters use arg arg1, arg2, propSheet select when arg2 == "DATA" then do -- arg1 is a table whose indexes are the films. Each item is a directory -- indexes are theater, date, and time. We only fill in the theater index -- for any movie in the table. We do not care what our status is for the -- theater, we just fill it in. do movie over arg1 d = arg1[movie] d~theater = movieTheaters[movie] end end otherwise do -- We do not handle any of the other queries. nop end end -- End select return 0 ::method onComboUpdate expose movieTheaters movieCombo movie = movieCombo~selected self~setRadioButtons(movie) ::method onRBClick expose movieTheaters movieCombo use arg id, hwnd movie = movieCombo~selected movieTheaters[movie] = self~newRadioButton(id)~getText~changestr("&", "") ::method setRadioButtons private expose movieTheaters buttonTable use arg movie movieTheater = movieTheaters[movie] if movieTheater == .nil then id = 0 else id = buttonTable[movieTheater] .RadioButton~checkInGroup(self, IDC_RB_CINEMAS_FIRST, IDC_RB_CINEMAS_LAST, id) -- DaysDlg -- Dialog for the fourth page (interior page, date selection.) ::class 'DaysDlg' subclass UserPSPdialog ::method defineDialog expose buttonTable dayNames = .array~of('&Monday', '&Tuesday', '&Wednesday', 'T&hursday', '&Friday', '&Saturday', 'S&unday') x = 21; y = 1 self~createStaticText(-1, x, y, 100, 8, , "Selected movies:") self~createComboBox(IDC_COMBO_DAYS, x, y + 12, 170, 75, "LIST SORT", "COMBOBOX") -- The space on this page is rather tight, so we try to make the group box as -- narrow as possible without clipping any of the radio buttons. I like a -- margin of 10 within the group box. y += 15 * 2 cx = self~calcWidthRadioButtons(dayNames) cy = self~sizeY - y self~createGroupBox(-1, x, y, cx + 20, cy, "", "Day for Movie") -- We create the radio buttons in a loop, so we take advantage of that to do -- the button event connections and other related tasks. buttonTable = .table~new first = self~resolveSymbolicID(IDC_RB_DAYS_FIRST) last = self~resolveSymbolicID(IDC_RB_DAYS_LAST) y += 10; x += 10 do id = first to last day = dayNames[id - first + 1] self~createRadioButton(id, x, y, cx, 12, "", day) self~connectButtonEvent(id, "CLICKED", onRBClick) buttonTable[day~changestr("&", "")] = id y += 14 end -- The movie bitmap is 238 X 178, convert that to dialog units and place at -- the lower right corner. s = .Size~new(238, 178) self~pixel2dlgUnit(s) x = self~sizeX - s~width y = self~sizeY - s~height self~createStaticImage(IDC_ST_MOVIE_BMP, x, y, s~width, s~height, "BITMAP SIZEIMAGE CENTERIMAGE") self~connectComboBoxEvent(IDC_COMBO_DAYS, "SELENDOK", "onComboUpdate") -- calcWidthRadioButtons() -- Given an array of the labels for a group of radio buttons, calculates the -- smallest width necessary for the buttons so that the widest label is not -- clipped. ::method calcWidthRadioButtons private use strict arg labels width = 0 do l over labels size = self~getTextSizeDu(l) if size~width > width then width = size~width end -- There doesn't seem to be any direct way to find out the width of the radio -- button bitmap, the little round circle. But, some of the MSDN doc -- *suggests* that it is 10 x 10 in dlus. width += 10 return width ::method initDialog expose filmArray movieDays selectedMovies movieCombo sd = locate() staticImage = self~newStatic(IDC_ST_MOVIE_BMP) size = staticImage~getRealSize image = .Image~getImage(sd'rc\ticketWizardMovie.bmp', BITMAP, size) staticImage~setImage(image) filmArray = .array~new(20) self~propSheet~querySiblings(filmArray, "FILMS") movieDays = .table~new do movie over filmArray movieDays[movie] = .nil end selectedMovies = .array~new movieCombo = self~newComboBox(IDC_COMBO_DAYS) ::method setActive unguarded expose movieCombo selectedMovies filmArray movieDays use arg propSheet propSheet~setWizButtons("BACK NEXT") selected = .table~new selectedMovies~empty propSheet~querySiblings(selected, "SELECTED") movieCombo~deleteAll do movie over filmArray if selected~hasIndex(movie) then do selectedMovies~append(movie) movieCombo~add(movie) end else do movieDays[movie] = .nil end end if selected~items > 0 then do movieCombo~selectIndex(1) movie = movieCombo~selected self~setRadioButtons(movie) end return 0 -- wizNext() -- We check if the user has set the day for each selected movie. If not, we -- warn the user, let the user decide if he wants to continue or not. ::method wizNext unguarded expose selectedMovies movieDays movieCombo use arg propSheet do movie over selectedMovies if movieDays[movie] == .nil then do msg = "Warning. You have not selected a day to attend the" || .endOfLine || - "movie for all selected movies." || .endOfLine~copies(2) || - "Are you sure you want to go to the next page?" title = "Date Selection is not Complete" buttons = "YESNO" icon = "WARNING" style = "DEFBUTTON2 TASKMODAL" ans = MessageDialog(msg, propSheet~dlgHandle, title, buttons, icon, style) if ans == .PlainBaseDialog~IDYES then return 0 movieCombo~select(movie) self~setRadioButtons(movie) return -1 end end return 0 ::method queryFromSibling unguarded expose movieDays use arg arg1, arg2, propSheet select when arg2 == "DATA" then do -- arg1 is a table whose indexes are the films. Each item is a directory -- indexes are theater, date, and time. We only fill in the date index -- for any movie in the table. We do not care what our status is for the -- theater, we just fill it in. do movie over arg1 d = arg1[movie] d~date = movieDays[movie] end end otherwise do -- We do not handle any of the other queries. nop end end -- End select return 0 ::method onComboUpdate expose movieCombo movie = movieCombo~selected self~setRadioButtons(movie) ::method onRBClick expose movieDays movieCombo use arg id, hwnd movie = movieCombo~selected movieDays[movie] = self~newRadioButton(id)~getText~changestr("&", "") ::method setRadioButtons private expose movieDays buttonTable use arg movie movieDay = movieDays[movie] if movieDay == .nil then id = 0 else id = buttonTable[movieDay] .RadioButton~checkInGroup(self, IDC_RB_DAYS_FIRST, IDC_RB_DAYS_LAST, id) -- TicketDlg -- Dialog for the fifth page (interior page, time selection and review.) ::class 'TicketDlg' subclass UserPSPdialog ::method defineDialog expose buttonTable x = 21; y = 1 self~createStaticText(-1, x, y, 100, 8, , "Selected movies:") self~createComboBox(IDC_COMBO_TIMES, x, y + 12, 170, 75, "LIST SORT", "COMBOBOX") label = "Theater:" size = self~getTextSizeDu(label) y += 15 *2 x1 = x + size~width + 1 cx1 = 170 - size~width - 1 self~createStaticText(-1, x, y + 2, size~width, size~height, "RIGHT", label) self~createBlackFrame(-1, x1, y, cx1, size~height + 6, "") self~createStaticText(IDC_ST_TIMES_THEATER, x1 + 4, y + 2, cx1 - 8, size~height, "", "") y += 20 self~createStaticText(-1, x, y + 2, size~width, size~height, "RIGHT", "Date:") self~createBlackFrame(-1, x1, y, cx1, size~height + 6, "") self~createStaticText(IDC_ST_TIMES_DAY, x1 + 4, y + 2, cx1 - 8, size~height, "", "") y += size~height + 6 + 4 cy = self~sizeY - y self~createGroupBox(-1, x, y, 60, cy, "", "Time:") times = .array~of("11:00", "17:00", "20:00", "22:00") buttonTable = .table~new first = self~resolveSymbolicID(IDC_RB_TIMES_FIRST) last = self~resolveSymbolicID(IDC_RB_TIMES_LAST) x += 10; y += 10; cx = 40; cy = 12 do id = first to last time = times[id - first + 1] self~createRadioButton(id, x, y, cx, cy, "", time) self~connectButtonEvent(id, "CLICKED", onRBClick) buttonTable[time] = id y +=15 end self~connectComboBoxEvent(IDC_COMBO_TIMES, "SELENDOK", "onComboUpdate") ::method initDialog expose filmArray selectedMovies movieTimes movieData movieCombo staticTheater staticDay filmArray = .array~new(20) self~propSheet~querySiblings(filmArray, "FILMS") movieTimes = .table~new do movie over filmArray movieTimes[movie] = .nil end -- We also need a table to keep track of the selected theaters and days sent -- to us from the Cinemas and Days pages. movieData = .table~new selectedMovies = .array~new movieCombo = self~newComboBox(IDC_COMBO_TIMES) staticTheater = self~newStatic(IDC_ST_TIMES_THEATER) staticDay = self~newStatic(IDC_ST_TIMES_DAY) ::method setActive unguarded expose filmArray selectedMovies movieCombo movieTimes movieData use arg propSheet propSheet~setWizButtons("BACK NEXT") propSheet~querySiblings(movieData, "DATA") selected = .table~new selectedMovies~empty movieCombo~deleteAll propSheet~querySiblings(selected, "SELECTED") do movie over filmArray if selected~hasIndex(movie) then do movieCombo~add(movie) selectedMovies~append(movie) end else do movieTimes[movie] = .nil end end if selected~items > 0 then do movieCombo~selectIndex(1) movie = movieCombo~selected self~setMovieData(movie) end return 0 -- wizNext() -- This is the last page before completion. Here we do not let the user -- proceed unless every selected movie has each item (theater, day, and time) -- specified. ::method wizNext unguarded expose selectedMovies movieTimes movieData movieCombo use arg propSheet -- We make two loops over the selected movies. The first is to force the -- user to select a time for very selected movie. Once that passes, we then -- loop over the movie data and check that the day and theater is specified -- for each selected movie. If we find a piece of information missing in the -- second loop, we return the user to the proper page to fill in the info. do movie over selectedMovies if movieTimes[movie] == .nil then do msg = "You have not selected a time to attend the" || .endOfLine || - "movie:" movie || .endOfLine~copies(2) || - "You must specify a time to attend each movie." title = "Time Selection is not Complete" buttons = "OK" icon = "EXCLAMATION" ans = MessageDialog(msg, propSheet~dlgHandle, title, buttons, icon) movieCombo~select(movie) self~setMovieData(movie) return -1 end end do movie over selectedMovies d = movieData[movie] if d~theater == .nil then do msg = "You have not selected a theater for the" || .endOfLine || - "movie:" movie || .endOfLine~copies(2) || - "You must specify a theater, day, and time" || .endOfLine || - "for each movie ticket." title = "Ticket Information is not Complete" buttons = "OK" icon = "EXCLAMATION" ans = MessageDialog(msg, propSheet~dlgHandle, title, buttons, icon) -- Return the page index of the Cinemas page to switch to that page return 3 end if d~date == .nil then do msg = "You have not selected a day to attend the" || .endOfLine || - "movie:" movie || .endOfLine~copies(2) || - "You must specify a theater, day, and time" || .endOfLine || - "for each movie ticket." title = "Ticket Information is not Complete" buttons = "OK" icon = "EXCLAMATION" ans = MessageDialog(msg, propSheet~dlgHandle, title, buttons, icon) -- Return the page index of the Days page to switch to that page return 4 end end return 0 ::method queryFromSibling unguarded expose movieTimes use arg arg1, arg2, propSheet select when arg2 == "DATA" then do do movie over arg1 d = arg1[movie] d~time = movieTimes[movie] end end otherwise do nop end end -- End select return 0 ::method onComboUpdate expose movieCombo movie = movieCombo~selected self~setMovieData(movie) ::method onRBClick expose movieTimes movieCombo use arg id, hwnd movie = movieCombo~selected movieTimes[movie] = self~newRadioButton(id)~getText~changestr("&", "") ::method setMovieData private expose movieTimes buttonTable movieData staticTheater staticDay use arg movie movieTime = movieTimes[movie] if movieTime == .nil then id = 0 else id = buttonTable[movieTime] .RadioButton~checkInGroup(self, IDC_RB_TIMES_FIRST, IDC_RB_TIMES_LAST, id) d = movieData[movie] if d~theater == .nil then text = "==> No theater was selected" else text = d~theater staticTheater~setText(text) if d~date == .nil then text = "==> No day selected" else text = d~date staticDay~setText(text) -- CompletionDlg -- Dialog for the sixth page (exterior page, review and finish.) ::class 'CompletionDlg' subclass UserPSPDialog ::method defineDialog expose sd sd = locate() -- Watermark bitmap is 164 x 316. The wizard does not stretch the bitmap, -- its size remains constant. So, the width of the bitmap, in dialog units, -- changes as the font changes. pixel2dlgUnit() will convert the pixel size -- of the bitmap to the correct dialog units for the font in use by this -- dialog. s = .Size~new(164, 314) self~pixel2dlgUnit(s) x = s~width + 6 -- 6 dlus right of the edge of the watermark bitmap. y = 8 -- From the Wizard97 specification. cx = self~sizeX - x -- To the right side of the template, the wizard automatically adds 14 dlus. cy = 3 * 12 -- Using 12 point font, takes 3 lines for the msg. self~createStaticText(IDC_ST_COMPLETE_MSG, x, y, cx, cy, "", "") -- Things are a little crowded on this page, so only add 4 dlus of space -- between the top message and the next control. (Contrast this to the -- IntroDlg.) y += cy + 4 self~createStaticText(-1, x, y, 165, 8, , "Ticket information for your selected movie(s):") self~createComboBox(IDC_COMBO_COMPLETE, x, y + 12, 165, 63, "LIST SORT PARTIAL", "COMBOBOX") label = "Theater:" size = self~getTextSizeDu(label) y += 15 * 2 x1 = x + size~width + 1 cx1 = 165 - size~width - 1 self~createStaticText(-1, x, y + 2, size~width, size~height, "", label) self~createBlackFrame(-1, x1, y, cx1, size~height + 6) self~createStaticText(IDC_ST_COMPLETE_THEATER, x1 + 4, y + 2, cx1 - 8, size~height) y += 20 self~createStaticText(-1, x, y + 2, size~width, size~height, "", "Date:") self~createBlackFrame(-1, x1, y, cx1, size~height + 6) self~createStaticText(IDC_ST_COMPLETE_DAY, x1 + 4, y + 2, cx1 - 8, size~height) y += 20 self~createStaticText(-1, x, y + 2, size~width, size~height, "", "Time:") self~createBlackFrame(-1, x1, y, cx1, size~height + 6) self~createStaticText(IDC_ST_COMPLETE_TIME, x1 + 4, y + 2, cx1 - 8, size~height) -- ticket.bmp is 204 x 80 pixels, convert to dialog units and place it in -- the lower left corner. At the x margin. s = .Size~new(204, 80) self~pixel2dlgUnit(s) y = self~sizeY - s~height - 4 if .DlgUtil~comCtl32Version < 6 then do self~createBitmapButton(IDC_PB_TICKET_BMP, x, y, s~width, s~height, "FRAME USEPAL STRETCH GROUP", "Get the Ticket", - 'onGetTicket', sd"rc\ticketWizardTicket.bmp") end else do self~createPushButton(IDC_PB_TICKET_BMP, x, y, s~width, s~height, "GROUP", "", 'onGetTicket') end self~connectComboBoxEvent(IDC_COMBO_COMPLETE, "SELENDOK", "onComboUpdate") ::method initDialog expose movieCombo staticTheater staticDay staticTime sd msg = "Thank You For Using the ooRexx Movie Ticket Selectitron Wizard" font1 = self~createFontEx("Verdana", 12, "BOLD") st1 = self~newStatic(IDC_ST_COMPLETE_MSG) st1~setFont(font1) st1~setText(msg) movieCombo = self~newComboBox(IDC_COMBO_COMPLETE) staticTheater = self~newStatic(IDC_ST_COMPLETE_THEATER) staticDay = self~newStatic(IDC_ST_COMPLETE_DAY) staticTime = self~newStatic(IDC_ST_COMPLETE_TIME) if .DlgUtil~comCtl32Version < 6 then return bmpButton = self~newPushButton(IDC_PB_TICKET_BMP) size = bmpButton~getRealSize size~width -= 10; size~height -= 10; sd = locate() image = .Image~getImage(sd'rc\ticketWizardTicket.bmp', BITMAP, size) imageList = .ImageList~create(size, COLOR8, 1, 0) imageList~add(image) align = CENTER margin = .Rect~new(5) bmpButton~setImageList(imageList, margin, align) ::method setActive unguarded expose movieCombo staticTheater staticDay staticTime info use arg propSheet propSheet~setWizButtons("BACK FINISH") info = .table~new movieCombo~deleteAll ret = propSheet~querySiblings(info, "DATA") do movie over info movieCombo~add(movie) end movieCombo~selectIndex(1) movie = movieCombo~selected self~setMovieData(movie) return 0 -- onGetTicket() -- This method is invoked when the user pushes the fancy bitmap button. We -- simply, programmatically, press the Finish button because the action to -- take is the same. ::method onGetTicket unguarded self~propSheet~pressButton("FINISH") -- wizFinish() -- Invoked when the user presses the Finish button. We use the ticket -- information to display a, (slightly,) fancy message to the user telling her -- where and when she can get her ticket(s). ::method wizFinish unguarded expose info use arg propSheet indexes = info~allIndexes count = indexes~items theaterFirst = info[indexes[1]]~theater smallTab = " "~copies(4) doubleSpace = .endOfLine~copies(2) if count == 1 then ticketWord = "ticket" else ticketWord = count "tickets" msg = "You can pick your" ticketWord "up now at the" || doubleSpace || - smallTab || theaterFirst || doubleSpace || - "box office. Or at" if count > 1 then do theaterList = "" do i = 2 to count index = indexes[i] theaterList ||= smallTab || info[index]~theater || .endOfLine end msg ||= " one of the following" || .endOfLine || - "theaters:" || doubleSpace || - theaterList || .endOfLine || - "within an hour from now." || doubleSpace || doubleSpace cost = "$" || (count * 15) "dollars." end else do msg ||= " any other convenient time." || doubleSpace || doubleSpace cost = "$15 dollars." end msg ||= "Your bank account is debited" cost title = "ooRexx Movie Ticket Selectitron" buttons = "OK" icon = "INFORMATION" ans = MessageDialog(msg, propSheet~dlgHandle, title, buttons, icon) return 0 ::method onComboUpdate expose movieCombo movie = movieCombo~selected self~setMovieData(movie) ::method setMovieData private expose staticTheater staticDay staticTime info use arg movie d = info[movie] staticTheater~setText(d~theater) staticDay~setText(d~date) staticTime~setText(d~time) -- FontPicker -- Dialog that allows the user to pick different fonts for the example program. -- This lets the user see how defineDialog() works with different fonts. ::class 'FontPicker' subclass RcDialog ::method initAutoDetection self~noAutoDetection ::method initDialog expose nameCB sizeCB nameCB = self~newComboBox(IDC_COMBO_NAME) sizeCB = self~newComboBox(IDC_COMBO_SIZE) names = .array~of("Default", "Tahoma", "Courier", "MS Sans Serif") sizes = .array~of("Default", '8 point', '10 point', '12 point', '16 point') do name over names nameCB~add(name) end nameCB~select("Default") do size over sizes sizeCB~add(size) end sizeCB~select("Default") ::method ok expose nameCB sizeCB fontName = nameCB~selected if fontName == "Default" then fontName = .PlainBaseDialog~getFontName fontSize = sizeCB~selected~word(1) if fontSize == "Default" then fontSize = .PlainBaseDialog~getFontSize .PlainBaseDialog~setDefaultFont(fontName, fontSize) return self~ok:super ::routine setFont oldFont = .directory~new oldFont~name = .PlainBaseDialog~getFontName oldFont~size = .PlainBaseDialog~getFontSize sd = locate() dlg = .FontPicker~new(sd"rc\ticketWizard.rc", IDD_FONT_PICKER, , , , 6) if dlg~initCode == 0 then do dlg~execute("SHOWTOP", IDI_DLG_OOREXX) end return oldFont ::routine restoreFont use strict arg oldFont .PlainBaseDialog~setDefaultFont(oldFont~name, oldFont~size) return oldFont