#!/usr/bin/env ruby # wxRuby2 Sample Code. Copyright (c) 2004-2008 wxRuby development team # Freely reusable code: see SAMPLES-LICENSE.TXT for details begin require 'rubygems' rescue LoadError end require 'wx' require 'wx' include Wx DIALOGS_CHOOSE_COLOUR = 1 DIALOGS_CHOOSE_COLOUR_GENERIC = 2 DIALOGS_CHOOSE_FONT = 3 DIALOGS_CHOOSE_FONT_GENERIC = 4 DIALOGS_MESSAGE_BOX = 5 DIALOGS_SINGLE_CHOICE = 6 DIALOGS_MULTI_CHOICE = 7 DIALOGS_TEXT_ENTRY = 8 DIALOGS_PASSWORD_ENTRY = 9 DIALOGS_FILE_OPEN = 10 DIALOGS_FILE_OPEN2 = 11 DIALOGS_FILES_OPEN = 12 DIALOGS_FILE_SAVE = 13 DIALOGS_DIR_CHOOSE = 14 DIALOGS_GENERIC_DIR_CHOOSE = 15 DIALOGS_TIP = 16 DIALOGS_NUM_ENTRY = 17 DIALOGS_LOG_DIALOG = 18 DIALOGS_MODAL = 19 DIALOGS_MODELESS = 20 DIALOGS_MODELESS_BTN = 21 DIALOGS_PROGRESS = 22 DIALOGS_BUSYINFO = 23 DIALOGS_FIND = 24 DIALOGS_REPLACE = 25 DIALOGS_PREFS = 26 $my_canvas = nil class MyModalDialog < Dialog def initialize(parent) super(parent, -1, "Modal dialog") sizer_top = BoxSizer.new(HORIZONTAL) @btn_focused = Button.new(self, -1, "Default button") @btn_delete = Button.new(self, -1, "&Delete button") btn_ok = Button.new(self, ID_CANCEL, "&Close") sizer_top.add(@btn_focused, 0, ALIGN_CENTER | ALL, 5) sizer_top.add(@btn_delete, 0, ALIGN_CENTER | ALL, 5) sizer_top.add(btn_ok, 0, ALIGN_CENTER | ALL, 5) set_auto_layout(true) set_sizer(sizer_top) sizer_top.set_size_hints(self) sizer_top.fit(self) @btn_focused.set_focus() @btn_focused.set_default() evt_button(-1) {|event| on_button(event) } end def on_button(event) id = event.get_id if id == @btn_delete.get_id @btn_focused.destroy @btn_focused = nil @btn_delete.disable() elsif @btn_focused && id == @btn_focused.get_id get_text_from_user("Dummy prompt", "Modal dialog called from dialog", "", self) else event.skip() end end end class MyModelessDialog < Dialog def initialize(parent) super(parent, -1, "Modeless dialog") sizer_top = BoxSizer.new(VERTICAL) btn = Button.new(self, DIALOGS_MODELESS_BTN, "Press me") check = CheckBox.new(self, -1, "Should be disabled") check.disable() sizer_top.add(btn, 1, EXPAND | ALL, 5) sizer_top.add(check, 1, EXPAND | ALL, 5) set_auto_layout(true) set_sizer(sizer_top) sizer_top.set_size_hints(self) sizer_top.fit(self) evt_button(DIALOGS_MODELESS_BTN) {|event| on_button(event) } evt_close() {|event| on_close(event) } end def on_button(event) message_box("Button pressed in modeless dialog", "Info", OK | ICON_INFORMATION, self) end def on_close(event) if event.can_veto() message_box("Use the menu item to close self dialog", "Modeless dialog", OK | ICON_INFORMATION, self) event.veto() end end end # PropertySheetDialog is specialised for doing preferences dialogs; it # contains a BookCtrl of some sort class MyPrefsDialog < Wx::PropertySheetDialog def initialize(parent) # Using Book type other than Notebook needs two-step construction super() self.sheet_style = Wx::PROPSHEET_BUTTONTOOLBOOK self.sheet_outer_border = 1 self.sheet_inner_border = 2 img_list = Wx::ImageList.new(32, 32) img_list << std_bitmap(Wx::ART_NORMAL_FILE) img_list << std_bitmap(Wx::ART_CDROM) img_list << std_bitmap(Wx::ART_REPORT_VIEW) create(parent, -1, "Preferences") create_buttons(Wx::ID_OK|Wx::ID_CANCEL) book_ctrl.image_list = img_list book_ctrl.add_page(file_panel(book_ctrl), "File", false, 0) book_ctrl.add_page(cdrom_panel(book_ctrl), "CD ROM", false, 1) layout_dialog end # Gets one of the rather ugly standard bitmaps from ArtProvider def std_bitmap(art_id) Wx::ArtProvider.bitmap(art_id, Wx::ART_TOOLBAR, [32, 32]) end def file_panel(book) panel = Wx::Panel.new(book) panel.sizer = Wx::VBoxSizer.new cb1 = Wx::CheckBox.new(panel, :label => 'Show hidden files') panel.sizer.add(cb1, 0, Wx::ALL, 5) cb2 = Wx::CheckBox.new(panel, :label => 'Always show extensions') panel.sizer.add(cb2, 0, Wx::ALL, 5) cb3 = Wx::CheckBox.new(panel, :label => 'Show icons') panel.sizer.add(cb3, 0, Wx::ALL, 5) cb4 = Wx::CheckBox.new(panel, :label => 'Show owner') panel.sizer.add(cb4, 0, Wx::ALL, 5) st = Wx::StaticText.new(panel, :label => "Sort by:") panel.sizer.add(st, 0, Wx::ALL, 5) cb1 = Wx::Choice.new(panel, :choices => %w|Name Created Modified Size|) panel.sizer.add(cb1, 0, Wx::ALL, 5) panel end def cdrom_panel(book) panel = Wx::Panel.new(book) panel.sizer = Wx::VBoxSizer.new choices = [ 'Show files', 'Play media', 'Run CD', 'Do nothing' ] rb = Wx::RadioBox.new( panel, :label => 'When opening CD', :choices => choices, :major_dimension => 1) panel.sizer.add(rb, 0, Wx::GROW|Wx::ALL, 5) panel end end class MyCanvas < ScrolledWindow def initialize(parent) super(parent,-1,DEFAULT_POSITION,DEFAULT_SIZE, NO_FULL_REPAINT_ON_RESIZE) evt_paint { |event| on_paint(event) } end def clear end def on_paint(event) paint do |dc| dc.set_text_foreground( get_app.canvas_text_colour ) dc.set_font( get_app.canvas_font ) dc.draw_text("Windows common dialogs test application", 10, 10) end end end class MyFrame < Frame def initialize(parent, title, pos, size) super(parent, -1, title, pos, size) @dialog = nil @dlg_find = nil @dlg_replace = nil @find_data = FindReplaceData.new @ext_def = "" @index = -1 @max = 10 create_status_bar() evt_menu(DIALOGS_CHOOSE_COLOUR) {|event| on_choose_colour(event) } evt_menu(DIALOGS_CHOOSE_FONT) {|event| on_choose_font(event) } evt_menu(DIALOGS_LOG_DIALOG) {|event| on_log_dialog(event) } evt_menu(DIALOGS_MESSAGE_BOX) {|event| on_message_box(event) } evt_menu(DIALOGS_TEXT_ENTRY) {|event| on_text_entry(event) } evt_menu(DIALOGS_PASSWORD_ENTRY) {|event| on_password_entry(event) } evt_menu(DIALOGS_NUM_ENTRY) {|event| on_numeric_entry(event) } evt_menu(DIALOGS_SINGLE_CHOICE) {|event| on_single_choice(event) } evt_menu(DIALOGS_MULTI_CHOICE) {|event| on_multi_choice(event) } evt_menu(DIALOGS_FILE_OPEN) {|event| on_file_open(event) } evt_menu(DIALOGS_FILE_OPEN2) {|event| on_file_open2(event) } evt_menu(DIALOGS_FILES_OPEN) {|event| on_files_open(event) } evt_menu(DIALOGS_FILE_SAVE) {|event| on_file_save(event) } evt_menu(DIALOGS_DIR_CHOOSE) {|event| on_dir_choose(event) } evt_menu(DIALOGS_MODAL) {|event| on_modal_dlg(event) } evt_menu(DIALOGS_MODELESS) {|event| on_modeless_dlg(event) } evt_menu(DIALOGS_TIP) {|event| on_show_tip(event) } evt_menu(DIALOGS_PROGRESS) {|event| on_show_progress(event) } evt_menu(DIALOGS_BUSYINFO) {|event| on_show_busy_info(event) } evt_menu(DIALOGS_PREFS) {|event| on_show_prefs(event) } evt_menu(DIALOGS_FIND) {|event| on_show_find_dialog(event) } evt_menu(DIALOGS_REPLACE) {|event| on_show_replace_dialog(event) } evt_find(-1) {|event| on_find_dialog(event) } evt_find_next(-1) {|event| on_find_dialog(event) } evt_find_replace(-1) {|event| on_find_dialog(event) } evt_find_replace_all(-1) {|event| on_find_dialog(event) } evt_find_close(-1) {|event| on_find_dialog(event) } evt_menu(ID_EXIT) {|event| on_exit(event) } end def on_choose_colour(event) col = $my_canvas.get_background_colour() data = ColourData.new data.set_colour(col) data.set_choose_full(true) for i in 0 ... 16 colour = Colour.new(i*16, i*16, i*16) data.set_custom_colour(i, colour) end dialog = ColourDialog.new(self, data) dialog.set_title("Choose the background colour (not OS X)") if dialog.show_modal() == ID_OK retData = dialog.get_colour_data() col = retData.get_colour() $my_canvas.set_background_colour(col) #$my_canvas.clear() $my_canvas.refresh() end end def on_choose_font(event) data = FontData.new data.set_initial_font(Wx::get_app.canvas_font) data.set_colour(Wx::get_app.canvas_text_colour) dialog = FontDialog.new(self, data) if dialog.show_modal() == ID_OK ret_data = dialog.get_font_data() Wx::get_app.canvas_font = ret_data.get_chosen_font() Wx::get_app.canvas_text_colour = ret_data.get_colour() font = ret_data.get_chosen_font msg = "Font = %s, %i pt" % [ font.get_face_name, font.get_point_size ] dialog2 = MessageDialog.new(self, msg, "Got font") dialog2.show_modal() # $my_canvas.refresh() end #else: cancelled by the user, don't change the font end def on_log_dialog(event) # calling yield() (as ~BusyCursor does) shouldn't result in messages # being flushed -- test it BusyCursor.busy() do log_message("This is some message - everything is ok so far.") log_message("Another message...\n... self one is on multiple lines") log_warning("And then something went wrong!") # and if ~BusyCursor doesn't do it, then call it manually Wx::get_app.yield() log_error("Intermediary error handler decided to abort.") log_error("DEMO: The top level caller detected an unrecoverable error.") Log::flush_active() log_message("And this is the same dialog but with only one message.") end end def on_message_box(event) dialog = MessageDialog.new(nil, "This is a message box\nA long, long string to test out the message box properly", "Message box text", NO_DEFAULT|YES_NO|CANCEL|ICON_INFORMATION) case dialog.show_modal() when ID_YES log_status("You pressed \"Yes\"") when ID_NO log_status("You pressed \"No\"") when ID_CANCEL log_status("You pressed \"Cancel\"") else log_error("Unexpected MessageDialog return code!") end end def on_numeric_entry(event) res = get_number_from_user( "This is some text, actually a lot of text.\n" + "Even two rows of text.", "Enter a number:", "Numeric input test", 50, 0, 100, self ) if res == -1 msg = "Invalid number entered or dialog cancelled." icon = ICON_HAND else msg = sprintf("You've entered %d", res ) icon = ICON_INFORMATION end message_box(msg, "Numeric test result", OK | icon, self) end def on_password_entry(event) pwd = get_password_from_user("Enter password:", "Password entry dialog", "", self) if pwd message_box(sprintf("Your password is '%s'", pwd), "Got password", OK | ICON_INFORMATION, self) end end def on_text_entry(event) dialog = TextEntryDialog.new(self, "This is a small sample\n" + "A long, long string to test out the text entrybox", "Please enter a string", "Default value", OK | CANCEL) if dialog.show_modal() == ID_OK dialog2 = MessageDialog.new(self, dialog.get_value(), "Got string") dialog2.show_modal() end end def on_single_choice(event) choices = ["One", "Two", "Three", "Four", "Five"] dialog = SingleChoiceDialog.new(self, "This is a small sample\n" + "A single-choice convenience dialog", "Please select a value", choices, nil, OK | CANCEL) dialog.set_selection(2) if dialog.show_modal() == ID_OK dialog2 = MessageDialog.new(self, dialog.get_string_selection(), "Got string") dialog2.show_modal() end end def on_multi_choice(event) choices = [ "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Seventeen"] selections = get_multiple_choices("This is a small sample\n" + "A multi-choice convenience dialog", "Please select a value", choices, self) if selections msg = sprintf("You selected %d items:\n", selections.length) for n in 0 ... selections.length msg += sprintf("\t%d: %d (%s)\n", n, selections[n], choices[selections[n]]) end log_message(msg) end #else: cancelled or nothing selected end def on_file_open(event) dialog = FileDialog.new( self, "Testing open file dialog", "", "", "C++ files (*.h;*.cpp)|*.h;*.cpp" ) dialog.set_directory(get_home_dir()) if dialog.show_modal() == ID_OK info = sprintf("Full file name: %s\n" + "Path: %s\n" + "Name: %s", dialog.get_path(), dialog.get_directory(), dialog.get_filename()) dialog2 = MessageDialog.new(self, info, "Selected file") dialog2.show_modal() end end # this shows how to take advantage of specifying a default extension in the # call to FileSelector: it is remembered after each new call and the next # one will use it by default def on_file_open2(event) path = file_selector( "Select the file to load", "", "", @ext_def, "Waveform (*.wav)|*.wav|Plain text (*.txt)|*.txt|All files (*.*)|*.*", CHANGE_DIR, self ) if path == nil return nil end # it is just a sample, would use SplitPath in real program @ext_def = path[/[^\.]*$/] log_message("You selected the file '%s', remembered extension '%s'", path, @ext_def) end def on_files_open(event) dialog = FileDialog.new(self, "Testing open multiple file dialog", "", "", FILE_SELECTOR_DEFAULT_WILDCARD_STR, MULTIPLE) if dialog.show_modal() == ID_OK paths = dialog.get_paths() filenames = dialog.get_filenames() count = paths.length msg = "" for n in 0 ... count s = sprintf("File %d: %s (%s)\n", n, paths[n], filenames[n]) msg += s end dialog2 = MessageDialog.new(self, msg, "Selected files") dialog2.show_modal() end end def on_file_save(event) dialog = FileDialog.new(self, "Testing save file dialog", "", "myletter.doc", "Text files (*.txt)|*.txt|Document files (*.doc)|*.doc", SAVE|OVERWRITE_PROMPT) dialog.set_filter_index(1) if dialog.show_modal() == ID_OK log_message("%s, filter %d", dialog.get_path(), dialog.get_filter_index()) end end def on_dir_choose(event) # pass some initial dir to DirDialog dir_home = get_home_dir() dialog = DirDialog.new(self, "Testing directory picker", dir_home) if dialog.show_modal() == ID_OK log_message("Selected path: %s", dialog.get_path()) end end def on_modal_dlg(event) dlg = MyModalDialog.new(self) dlg.show_modal() end def on_modeless_dlg(event) show = get_menu_bar().is_checked(event.get_id()) if show if !@dialog @dialog = MyModelessDialog.new(self) end @dialog.show(true) else # hide @dialog.hide() end end def on_show_tip(event) if @index == -1 @index = rand(5) end tip_src = File.join( File.dirname(__FILE__), 'tips.txt') tip_provider = create_file_tip_provider(tip_src, @index) show_at_startup = show_tip(self, tip_provider) if show_at_startup message_box("Will show tips on startup", "Tips dialog", OK | ICON_INFORMATION, self) end @index = tip_provider.get_current_tip() end def on_exit(event) close(true) end def on_show_prefs(event) dialog = MyPrefsDialog.new(self) dialog.show_modal end def on_show_progress(event) dialog = ProgressDialog.new("Progress dialog example", "An informative message", @max, # range self, # parent PD_CAN_ABORT|PD_APP_MODAL| PD_ELAPSED_TIME|PD_ESTIMATED_TIME| PD_REMAINING_TIME) cont = true for i in 0 .. @max sleep(1) if i == @max cont = dialog.update(i, "That's all, folks!") elsif i == @max / 2 cont = dialog.update(i, "Only a half left (very long message)!") else cont = dialog.update(i) end if !cont if message_box("Do you really want to cancel?", "Progress dialog question", # caption YES_NO | ICON_QUESTION) == YES dialog.end_modal(ID_CANCEL) break end dialog.resume() end end if !cont log_status("Progress dialog aborted!") else log_status("Countdown from %d finished", @max) end end def on_show_busy_info(event) WindowDisabler.disable(self) do info = BusyInfo.busy("Working, please wait...", self) do for i in 0 ... 18 Wx::get_app.yield() end sleep(2) end end end def on_show_replace_dialog(event) if @dlg_replace #@dlg_replace.destroy @dlg_replace = nil else @dlg_replace = FindReplaceDialog.new( self, @find_data, "Find and replace dialog", FR_REPLACEDIALOG ) @dlg_replace.show(true) end end def on_show_find_dialog(event) if @dlg_find @dlg_find.destroy @dlg_find = nil else @dlg_find = FindReplaceDialog.new( self, @find_data, "Find dialog", # just for testing FR_NOWHOLEWORD ) @dlg_find.show(true) end end def decode_find_dialog_event_flags(flags) str = "" str << ((flags & FR_DOWN) != 0 ? "down" : "up") << ", " \ << ((flags & FR_WHOLEWORD) != 0 ? "whole words only, " : "") \ << ((flags & FR_MATCHCASE) != 0 ? "" : "not ") \ << "case sensitive" return str end def on_find_dialog(event) type = event.get_event_type() if type == EVT_COMMAND_FIND || type == EVT_COMMAND_FIND_NEXT log_message("Find %s'%s' (flags: %s)", type == EVT_COMMAND_FIND_NEXT ? "next " : "", event.get_find_string(), decode_find_dialog_event_flags(event.get_flags())) elsif type == EVT_COMMAND_FIND_REPLACE || type == EVT_COMMAND_FIND_REPLACE_ALL log_message("Replace %s'%s' with '%s' (flags: %s)", type == EVT_COMMAND_FIND_REPLACE_ALL ? "all " : "", event.get_find_string(), event.get_replace_string(), decode_find_dialog_event_flags(event.get_flags())) elsif type == EVT_COMMAND_FIND_CLOSE dlg = event.get_dialog() if dlg == @dlg_find txt = "Find" id_menu = DIALOGS_FIND @dlg_find = nil elsif dlg == @dlg_replace txt = "Replace" id_menu = DIALOGS_REPLACE @dlg_replace = nil else txt = "Unknown" id_menu = -1 log_error("unexpected event") end log_message("%s dialog is being closed.", txt) if id_menu != -1 get_menu_bar().check(id_menu, false) end dlg.destroy() else log_error("Unknown find dialog event!") end end end class MyApp < App attr_accessor :canvas_text_colour, :canvas_font def on_init() self.canvas_text_colour = Wx::Colour.new("BLACK") self.canvas_font = Wx::NORMAL_FONT # Create the main frame window frame = MyFrame.new(nil, "Windows dialogs example", Point.new(20, 20), Size.new(400, 300)) # Make a menubar file_menu = Menu.new file_menu.append(DIALOGS_CHOOSE_COLOUR, "&Choose colour") file_menu.append_separator() file_menu.append(DIALOGS_CHOOSE_FONT, "Choose &font") file_menu.append_separator() file_menu.append(DIALOGS_LOG_DIALOG, "&Log dialog\tCtrl-L") file_menu.append(DIALOGS_MESSAGE_BOX, "&Message box\tCtrl-M") file_menu.append(DIALOGS_TEXT_ENTRY, "Text &entry\tCtrl-E") file_menu.append(DIALOGS_PASSWORD_ENTRY, "&Password entry\tCtrl-P") file_menu.append(DIALOGS_NUM_ENTRY, "&Numeric entry\tCtrl-N") file_menu.append(DIALOGS_SINGLE_CHOICE, "&Single choice\tCtrl-C") file_menu.append(DIALOGS_MULTI_CHOICE, "M&ultiple choice\tCtrl-U") file_menu.append_separator() file_menu.append(DIALOGS_TIP, "&Tip of the day\tCtrl-T") file_menu.append_separator() file_menu.append(DIALOGS_FILE_OPEN, "&Open file\tCtrl-O") file_menu.append(DIALOGS_FILE_OPEN2, "&Second open file\tCtrl-2") file_menu.append(DIALOGS_FILES_OPEN, "Open &files\tShift-Ctrl-O") file_menu.append(DIALOGS_FILE_SAVE, "Sa&ve file\tCtrl-S") file_menu.append(DIALOGS_DIR_CHOOSE, "&Choose a directory\tCtrl-D") file_menu.append(DIALOGS_PROGRESS, "Pro&gress dialog\tCtrl-G") file_menu.append(DIALOGS_BUSYINFO, "&Busy info dialog\tCtrl-B") file_menu.append(DIALOGS_PREFS, "Propert&y sheet dialog\tCtrl-Y") file_menu.append(DIALOGS_FIND, "&Find dialog\tCtrl-F", "", ITEM_CHECK) file_menu.append(DIALOGS_REPLACE, "Find and &replace dialog\tShift-Ctrl-F", "", ITEM_CHECK) file_menu.append_separator() file_menu.append(DIALOGS_MODAL, "Mo&dal dialog\tCtrl-W") file_menu.append(DIALOGS_MODELESS, "Modeless &dialog\tCtrl-Z", "", ITEM_CHECK) file_menu.append_separator() file_menu.append(ID_EXIT, "E&xit\tAlt-X") menu_bar = MenuBar.new menu_bar.append(file_menu, "&File") frame.set_menu_bar(menu_bar) $my_canvas = MyCanvas.new(frame) $my_canvas.set_background_colour(WHITE) frame.centre(BOTH) # Show the frame frame.show() end end app = MyApp.new() app.main_loop()