# # $Id: button.icn,v 1.7 2006-07-09 23:43:07 rparlett Exp $ # # This file is in the public domain. # # Author: Robert Parlett (parlett@dial.pipex.com) # package gui link graphics $include "guih.icn" # # This is the parent class of the button classes, including # checkboxes. # # A {Button} produces a BUTTON_PRESS_EVENT when the button is # depressed, and code BUTTON_RELEASE_EVENT when it is released, # as well as an ACTION_EVENT. # # By default, when a button holds the keyboard focus a dashed # line appears just within the button. Then, when return is # pressed an ACTION_EVENT is generated. The method # {Dialog.set_initial_focus()} can be used to have the button # have the focus when the dialog is first displayed. # # Buttons also repeatedly produce a BUTTON_HELD_EVENT whilst they # are held down, rather like a repeating keyboard press. The # delay between the initial repeat event and subsequent repeat # events is set in the parent dialog (see above). # class Button : Toggle : Component( is_down, # is_held, # is_checked_flag, # label, img_up, # img_down, # img_w, # img_h, # parent_check_box_group, # parent_button_group, # repeat_delay, no_keyboard_flag, # toggles_flag ) method set_parent_button_group(x) return self.parent_button_group := x end # # Invoking this method disables the keyboard control over the # button described above. No dashed line will ever appear in # the button display and return will have no effect on the # button even if it has the focus. # method set_no_keyboard() self.no_keyboard_flag := 1 self.accepts_focus_flag := &null end # # Clear the no keyboard behaviour (the default) # method clear_no_keyboard() self.no_keyboard_flag := &null self.accepts_focus_flag := 1 end method tick() if dispatcher.curr_time_of_day() > self.repeat_delay then fire(BUTTON_HELD_EVENT) end method go_down() self.is_down := 1 set_ticker(self.parent_dialog.repeat_rate) end method go_up() self.is_down := &null stop_ticker() end method handle_press(e) local b if self.in_region() then { go_down() self.repeat_delay := dispatcher.curr_time_of_day() + self.parent_dialog.repeat_delay self.is_held := 1 every b := !(\self.parent_button_group).buttons do { if b.is_unhidden() then { b.is_held := 1 b.repeat_delay := self.repeat_delay } } self.invalidate() fire(BUTTON_PRESS_EVENT, e) } end method handle_drag(e) if \self.is_held then { # # Button held down; toggle on/off as it goes over the button # if self.in_region() then { if /self.is_down then { go_down() invalidate() } } else { if \self.is_down then { go_up() invalidate() } } } end method handle_release(e) if \self.is_held then { self.is_held := &null if \self.is_down then { go_up() fire(BUTTON_RELEASE_EVENT, e) on_action(e) } } end method on_action(e) if \self.toggles_flag then { if \self.parent_check_box_group then self.parent_check_box_group.set_which_one(self) else self.toggle_is_checked() } self.invalidate() fire(ACTION_EVENT, e) end method handle_accel(e) self.Component.handle_accel(e) on_action(e) end method handle_default(e) if \self.has_focus then { if /self.no_keyboard_flag & e == ("\r" | "\l" | " ") then { on_action(e) } } end method handle_event(e) if e === (&lpress | &rpress | &mpress) then { handle_press(e) } else if e === (&ldrag | &rdrag | &mdrag) then { handle_drag(e) } else if e === (&lrelease | &rrelease | &mrelease) then { handle_release(e) } else handle_default(e) end # # Set the up/down images (if any) to the strings provided, # which should be in Icon image format. # The two images must have the same dimensions. # @param x The up image # @param y The down image # method set_imgs(x, y) self.img_up := x self.img_w := img_width(x) = img_width(y) | fatal("Image widths differ") self.img_h := img_height(x) = img_height(y) | fatal("Image heights differ") self.img_down := y return end # # Set the image (if any) to the given string, which should be in Icon image # format. # @param x The image # method set_img(x) self.img_up := self.img_down := x self.img_w := img_width(x) self.img_h := img_height(x) return x end # # Toggle the checked status of the button. This method, and # the following two methods, may be # inappropriate for non-toggle styles of button. # method toggle_is_checked() self.Toggle.toggle_is_checked() self.invalidate() end # # Set the status to checked. # method set_is_checked() self.Toggle.set_is_checked() self.invalidate() end # # Set the status to unchecked. # method clear_is_checked() self.Toggle.clear_is_checked() self.invalidate() end # # Set the button so that when it is pressed, it toggles # between two states, as indicated by the is_checked # flag. # # Instances of Checkbox have this flag on by default, but # TextButton and IconButton do not. When the flag is on, # the latter classes indicate their checked status by # showing the button as being "down". # method set_toggles() self.toggles_flag := 1 self.invalidate() end # # Clear the toggles flag. # method clear_toggles() self.toggles_flag := &null self.invalidate() end # # Set the label of the button, if any. # @param x The label # method set_label(x) self.label := x self.invalidate() return x end method set_one(attr, val) case attr of { "label" : set_label(string_val(attr, val)) "is_checked" : if test_flag(attr, val) then set_is_checked() else clear_is_checked() "toggles" : if test_flag(attr, val) then set_toggles() else clear_toggles() "no_keyboard" : if test_flag(attr, val) then set_no_keyboard() else clear_no_keyboard() default: self.Component.set_one(attr, val) } end initially() self.Component.initially() self.accepts_focus_flag := 1 end