ncumbra 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,130 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: button.rb
3
+ # Description: button widget that has an action associated with :PRESS event
4
+ # which by default is the SPACE key.
5
+ # Author: j kepler http://github.com/mare-imbrium/canis/
6
+ # Date: 2018-03-16
7
+ # License: MIT
8
+ # Last update: 2018-04-07 23:07
9
+ # ----------------------------------------------------------------------------- #
10
+ # button.rb Copyright (C) 2012-2018 j kepler
11
+ # == TODO
12
+ # - mnemonics with highlighting
13
+ # - default button
14
+ require 'umbra/widget'
15
+ # ----------------
16
+ module Umbra
17
+ class Button < Widget
18
+ attr_accessor :surround_chars # characters to use to surround the button, def is square brackets
19
+ # char to be underlined, and bound to Alt-char
20
+ attr_accessor :mnemonic
21
+ def initialize config={}, &block
22
+ @focusable = true
23
+ @editable = false
24
+ @highlight_attr = REVERSE
25
+ # hotkey denotes we should bind the key itself not alt-key (for menulinks)
26
+ #@hotkey = config.delete(:hotkey) 2018-03-22 -
27
+ # 2018-03-18 - FORM_ATTACHED deprecated to keep things simple
28
+ register_events([:PRESS])
29
+ @default_chars = ['> ', ' <'] # a default button is painted differently
30
+ super
31
+
32
+
33
+ @surround_chars ||= ['[ ', ' ]']
34
+ @col_offset = @surround_chars[0].length
35
+ @text_offset = 0 # used to determine where underline should fall TODO ???
36
+ map_keys
37
+ end
38
+ ##
39
+ # set button based on Action
40
+ # 2018-03-22 - is this still used ?
41
+ # This allows action objects to be used in multiple places such as buttons, menus, popups etc.
42
+ def action a
43
+ text a.name
44
+ mnemonic a.mnemonic unless a.mnemonic.nil?
45
+ command { a.call }
46
+ end
47
+
48
+ def getvalue
49
+ @text
50
+ end
51
+
52
+ # ensure text has been passed or action
53
+ def getvalue_for_paint
54
+ ret = getvalue
55
+ @text_offset = @surround_chars[0].length
56
+ @surround_chars[0] + ret + @surround_chars[1]
57
+ end
58
+
59
+ def repaint # button
60
+ return unless @repaint_required
61
+
62
+ $log.debug("BUTTON repaint : #{self} r:#{@row} c:#{@col} , cp:#{@color_pair}, st:#{@state}, #{getvalue_for_paint}" )
63
+ r,c = @row, @col
64
+ _attr = @attr || NORMAL
65
+ _color = @color_pair
66
+ if @state == :HIGHLIGHTED
67
+ _color = @highlight_color_pair || @color_pair
68
+ _attr = @highlight_attr || _attr
69
+ elsif selected? # only for certain buttons lie toggle and radio
70
+ _color = @selected_color_pair || @color_pair
71
+ end
72
+ #$log.debug "XXX: button #{text} STATE is #{@state} color #{_color} , attr:#{_attr}"
73
+ value = getvalue_for_paint
74
+ #$log.debug("button repaint :#{self} r:#{r} c:#{c} col:#{_color} v: #{value} ul #{@underline} mnem #{@mnemonic} ")
75
+ #len = @width || value.length # 2018-04-07 - width is not serving a purpose right now
76
+ # # since surround chars still come where they do, and only highlight uses the width
77
+ # which looks wrong.
78
+ len = value.length
79
+ @graphic.printstring r, c, "%-*s" % [len, value], _color, _attr
80
+
81
+ # if a mnemonic character has been defined, then locate the index and highlight it.
82
+ # TODO a mnemonic can also be defined in the text with an ampersand.
83
+ if @mnemonic
84
+ index = value.index(@mnemonic) || value.index(@mnemonic.swapcase)
85
+ if index
86
+ y = c + index
87
+ x = r
88
+ @graphic.mvchgat(x, y, max=1, FFI::NCurses::A_BOLD|UNDERLINE, FFI::NCurses.COLOR_PAIR(_color || 1), nil)
89
+ end
90
+ end
91
+ @repaint_required = false
92
+ end
93
+
94
+ ## command of button (invoked on press, hotkey, space)
95
+ # added args 2008-12-20 19:22
96
+ def command *args, &block
97
+ bind_event :PRESS, *args, &block
98
+ end
99
+ ## fires PRESS event of button
100
+ def fire
101
+ #$log.debug "firing PRESS #{text}"
102
+ fire_handler :PRESS, ActionEvent.new(self, :PRESS, text)
103
+ end
104
+ # for campatibility with all buttons, will apply to radio buttons mostly
105
+ def selected?; false; end
106
+
107
+ def map_keys
108
+ return if @keys_mapped
109
+ bind_key(32, "fire") { fire } if respond_to? :fire
110
+ end
111
+
112
+ # Button
113
+ def handle_key ch
114
+ super
115
+ end
116
+
117
+ # layout an array of buttons horizontally {{{
118
+ def self.button_layout buttons, row, startcol=0, cols=FFI::NCurses.COLS-1, gap=5
119
+ col = startcol
120
+ buttons.each_with_index do |b, ix|
121
+ $log.debug " BUTTON #{b}: #{b.col} "
122
+ b.row = row
123
+ b.col col
124
+ $log.debug " after BUTTON #{b}: #{b.col} "
125
+ len = b.text.length + gap
126
+ col += len
127
+ end
128
+ end
129
+ end #BUTTON # }}}
130
+ end # module
@@ -0,0 +1,96 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: buttongroup.rb
3
+ # Description: Manages a group of radio buttons
4
+ # Author: j kepler http://github.com/mare-imbrium/canis/
5
+ # Date: 2018-04-02 - 08:47
6
+ # License: MIT
7
+ # Last update: 2018-04-02 23:30
8
+ # ----------------------------------------------------------------------------- #
9
+ # buttongroup.rb Copyright (C) 2012-2018 j kepler
10
+ # This is not a visual class or a widget.
11
+ # This class allows us to attach several RadioButtons to it, so it can maintain which one is the
12
+ # selected one. It also allows for assigning of commands to be executed whenever a button is pressed,
13
+ # akin to binding to the +fire+ of the button, except that one would not have to bind to each button,
14
+ # but only once here.
15
+ #
16
+ # @example
17
+ # group = ButtonGroup.new
18
+ # group.add(r1).add(r2).add(r3)
19
+ # group.command(somelabel) do |grp, label| label.text = grp.value; end
20
+ #
21
+ class ButtonGroup
22
+
23
+ # Array of buttons that have been added.
24
+ attr_reader :elements
25
+ # name for group, can be used in messages
26
+ attr_accessor :name
27
+
28
+ # the value of the radio button that is selected. To get the button itself, use +selection+.
29
+ attr_reader :value
30
+
31
+ def initialize name="Buttongroup"
32
+ @elements = []
33
+ @hash = {}
34
+ @name = name
35
+ end
36
+
37
+ # add a radio button to the group.
38
+ def add e
39
+ @elements << e
40
+ @hash[e.value] = e
41
+ e.button_group=(self)
42
+ self
43
+ end
44
+ # remove button from group
45
+ def remove e
46
+ @elements.delete e
47
+ @hash.delete e.value
48
+ self
49
+ end
50
+
51
+ # @return the radiobutton that is selected
52
+ def selection
53
+ @hash[@value]
54
+ end
55
+
56
+ # @param [String, RadioButton] +value+ of a button, or +Button+ itself to check if selected.
57
+ # @return [true or false] for whether the given value or button is the selected one
58
+ def selected? val
59
+ if val.is_a? String
60
+ @value == val
61
+ else
62
+ @hash[@value] == val
63
+ end
64
+ end
65
+ # install trigger to call whenever a value is updated
66
+ # @public called by user components
67
+ def command *args, &block
68
+ @commands ||= []
69
+ @args ||= []
70
+ @commands << block
71
+ @args << args
72
+ end
73
+ # select the given button or value.
74
+ # This may be called by user programs to programmatically select a button
75
+ def select button
76
+ if button.is_a? String
77
+ ;
78
+ else
79
+ button = button.value
80
+ end
81
+ self.value = button
82
+ end
83
+ # whenever a radio button is pressed, it updates the value of the group with it;s value.
84
+ # since only one is true at a time.
85
+ def value=(value)
86
+ @value = value
87
+ # 2018-04-02 - need to repaint all the radio buttons so they become off
88
+ @elements.each {|e| e.repaint_required = true }
89
+
90
+ return unless @commands
91
+ @commands.each_with_index do |comm, ix|
92
+ comm.call(self, *@args[ix]) unless comm.nil?
93
+ end
94
+ end
95
+
96
+ end
@@ -0,0 +1,42 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: checkbox.rb
3
+ # Description:
4
+ # Author: j kepler http://github.com/mare-imbrium/canis/
5
+ # Date: 2018-04-01 - 16:08
6
+ # License: MIT
7
+ # Last update: 2018-04-01 16:21
8
+ # ----------------------------------------------------------------------------- #
9
+ # checkbox.rb Copyright (C) 2012-2018 j kepler
10
+ module Umbra
11
+ ##
12
+ # A checkbox, may be selected or unselected
13
+ #
14
+ class Checkbox < ToggleButton
15
+ attr_accessor :align_right # the button will be on the right 2008-12-09 23:41
16
+ # if a variable has been defined, off and on value will be set in it (default 0,1)
17
+ def initialize config={}, &block
18
+ @surround_chars = ['[', ']'] # 2008-12-23 23:16 added space in Button so overriding
19
+ super
20
+ end
21
+ def getvalue
22
+ @value
23
+ end
24
+
25
+ def getvalue_for_paint
26
+ buttontext = getvalue() ? "X" : " "
27
+ dtext = @width.nil? ? @text : "%-*s" % [@width, @text]
28
+ dtext = "" if @text.nil? # added 2009-01-13 00:41 since cbcellrenderer prints no text
29
+ if @align_right
30
+ @text_offset = 0
31
+ @col_offset = dtext.length + @surround_chars[0].length + 1
32
+ return "#{dtext} " + @surround_chars[0] + buttontext + @surround_chars[1]
33
+ else
34
+ pretext = @surround_chars[0] + buttontext + @surround_chars[1]
35
+ @text_offset = pretext.length + 1
36
+ @col_offset = @surround_chars[0].length
37
+ #@surround_chars[0] + buttontext + @surround_chars[1] + " #{@text}"
38
+ return pretext + " #{dtext}"
39
+ end
40
+ end
41
+ end # class
42
+ end # module
@@ -0,0 +1,214 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: dialog.rb
3
+ # Description: A simple dialog box that only depends on window.
4
+ # This does not have forms or buttons, fields etc. That would introduce a circular
5
+ # dependence that I would like to avoid. This way widgets can include this to print an error
6
+ # or other message.
7
+ # NOTE: to check similar behavior, load "links" and press "q", its displays a dialog box with yes/no.
8
+ # Also check midnight-commander, press F10 to quit and see dialog box.
9
+ # Author: j kepler http://github.com/mare-imbrium/canis/
10
+ # Date: 2018-03-27 - 12:09
11
+ # License: MIT
12
+ # Last update: 2018-04-20 11:05
13
+ # ----------------------------------------------------------------------------- #
14
+ # dialog.rb Copyright (C) 2012-2018 j kepler
15
+ #
16
+ require 'umbra/window'
17
+ module Umbra
18
+
19
+ # A simple dialog box that only displays a line of text, centered.
20
+ # It can take an array of button labels (just strings) and display them, and return the index
21
+ # of the button pressed, when closed.
22
+ # If no buttons are supplied, an "Ok" button is displayed.
23
+ # Minimum requirements are `text` and `title`
24
+ class Dialog
25
+
26
+ attr_accessor :text # text or message to print centered
27
+ attr_accessor :title # title of dialog
28
+ attr_accessor :title_color_pair # color pair of title
29
+ attr_accessor :title_attr # attribute of title
30
+ attr_accessor :window_color_pair # color pair of window
31
+ attr_accessor :window_attr # attribute of window
32
+ attr_accessor :border_color_pair # color pair of border
33
+ attr_accessor :border_attr # attribute of border
34
+ attr_accessor :buttons # button text array
35
+
36
+ ## currently color and attr for text is missing. I think it should be what window has.
37
+
38
+ def initialize config={}, &block
39
+ config.each_pair { |k,v| variable_set(k,v) }
40
+ if block_given?
41
+ if block.arity > 0
42
+ yield self
43
+ else
44
+ self.instance_eval(&block)
45
+ end
46
+ end
47
+ @title ||= "Alert"
48
+ @buttons ||= ["Ok"]
49
+ end
50
+
51
+ private def variable_set var, val
52
+ send("#{var}=", val)
53
+ end
54
+ private def _create_window
55
+ text = @text || "Warning! Did not get text"
56
+ #h = 7
57
+ # increase height to 9 so we can put a fake button below text
58
+ h = 9
59
+ w = text.size + 20
60
+ # don't exceed max
61
+ w = [FFI::NCurses.COLS-10, w].min
62
+ @window_color_pair ||= CP_BLACK
63
+ @window_attr ||= REVERSE
64
+ win = create_centered_window h, w, @window_color_pair, @window_attr
65
+
66
+ ## ---- border section --- {{{
67
+ row = 1
68
+ col = 2
69
+ borderatt = @border_attr || NORMAL
70
+ bordercolor = @border_color_pair || CP_BLACK
71
+ win.wattron(bordercolor | borderatt)
72
+ print_border_mb win, row, col, win.height, win.width, nil, nil
73
+ win.wattroff(bordercolor | borderatt)
74
+ ## ---- border section --- }}}
75
+
76
+ ## ---- title section ---- {{{
77
+ @title ||= "No title"
78
+ @title_color_pair ||= CP_CYAN
79
+ @title_attr ||= REVERSE
80
+ title = " "+@title+" "
81
+ # normalcolor gives a white on black stark title like links and elinks
82
+ # You can also do 'acolor' to give you a sober title that does not take attention away, like mc
83
+ win.printstring(row=1,col=(w-title.length)/2,title, color=@title_color_pair, @title_attr)
84
+ ## ---- title section ---- }}}
85
+
86
+ # text can be longer than window. truncate or wrap
87
+ # tet.size can be longer than w
88
+ _col = (w-text.size)/2
89
+ _col = 3 if _col < 3
90
+ win.printstring 3, _col, text
91
+ ## ---- button section ---- {{{
92
+ paint_buttons win, @buttons, 0
93
+ ## ---- button section ---- }}}
94
+ @window = win
95
+ win.wrefresh
96
+ end
97
+ def paint_buttons win, buttons, active_index
98
+ brow = 6
99
+ bcol = (win.width-(buttons.size*10))/2
100
+ origbcol = bcol
101
+ FFI::NCurses.mvwhline(win.pointer, brow-1, 3, FFI::NCurses::ACS_HLINE, win.width-6)
102
+ #@button_color ||= create_color_pair(COLOR_BLACK, COLOR_MAGENTA)
103
+ @button_color ||= CP_BLACK
104
+ active_color = create_color_pair(COLOR_BLACK, COLOR_MAGENTA)
105
+ #active_color = create_color_pair(COLOR_MAGENTA, COLOR_BLACK)
106
+ active_col = bcol
107
+ buttons.each_with_index do |button, ix|
108
+ button_attr = NORMAL
109
+ button_color = @button_color
110
+ _button = "[ #{button} ]"
111
+ if ix == active_index
112
+ button_attr = BOLD
113
+ button_color = active_color
114
+ active_col = bcol
115
+ _button = "> #{button} <"
116
+ end
117
+ win.printstring brow, bcol, _button, button_color, button_attr
118
+ bcol += 10
119
+ end
120
+ FFI::NCurses.wmove(win.pointer, brow, active_col+2)
121
+ end
122
+
123
+ # convenience func to get int value of a key {{{
124
+ # added 2014-05-05
125
+ # instead of ?\C-a.getbyte(0)
126
+ # use key(?\C-a)
127
+ # or key(?a) or key(?\M-x)
128
+ def key ch
129
+ ch.getbyte(0)
130
+ end # }}}
131
+ def run
132
+ _create_window unless @window
133
+ win = @window
134
+ buttoncount = @buttons.count
135
+ buttonindex = 0
136
+ begin
137
+ while (ch = win.getkey) != FFI::NCurses::KEY_RETURN
138
+ begin
139
+ next if ch == -1
140
+ break if ch == 32 or key(?q) == ch
141
+ # go to next button if right or down or TAB pressed
142
+ if ch == FFI::NCurses::KEY_TAB or ch == FFI::NCurses::KEY_RIGHT or FFI::NCurses::KEY_DOWN
143
+ buttonindex += 1
144
+ elsif ch == FFI::NCurses::KEY_LEFT or FFI::NCurses::KEY_UP
145
+ buttonindex -= 1
146
+ else
147
+ # should check against first char of buttons TODO
148
+ #puts "Don't know #{ch}"
149
+ end
150
+ buttonindex = 0 if buttonindex > buttoncount-1
151
+ buttonindex = buttoncount-1 if buttonindex < 0
152
+ paint_buttons win, @buttons, buttonindex
153
+ rescue => e
154
+ puts e
155
+ puts e.backtrace.join("\n")
156
+ end
157
+ win.wrefresh
158
+ end
159
+ ensure
160
+ win.destroy
161
+ end
162
+ #FFI::NCurses.endwin # don't think this should be here if popped up by another window
163
+ return buttonindex
164
+ end
165
+ end # module
166
+
167
+ # create a centered window. # {{{
168
+ # NOTE: this should probably go into window class, or some util class.
169
+ # TODO: it hardcodes background color. fix this.
170
+ def create_centered_window height, width, color_pair=0, attrib=REVERSE
171
+ row = ((FFI::NCurses.LINES-height)/2).floor
172
+ col = ((FFI::NCurses.COLS-width)/2).floor
173
+ win = Window.new height, width, row, col
174
+ #FFI::NCurses.wbkgd(win.pointer, FFI::NCurses.COLOR_PAIR(0) | REVERSE); # does not work on xterm-256color
175
+ FFI::NCurses.wbkgd(win.pointer, FFI::NCurses.COLOR_PAIR(color_pair) | attrib); # does not work on xterm-256color
176
+ #FFI::NCurses.wbkgd(win.pointer, color_pair | attrib)
177
+ return win
178
+ end # }}}
179
+ private def print_border_mb window, row, col, height, width, color, attr # {{{
180
+ win = window.pointer
181
+ #att = attr
182
+ len = width
183
+ len = FFI::NCurses.COLS if len == 0
184
+ space_char = " ".codepoints.first
185
+ (row-1).upto(row+height-1) do |r|
186
+ # this loop clears the screen, printing spaces does not work since ncurses does not do anything
187
+ FFI::NCurses.mvwhline(win, r, col, space_char, len)
188
+ end
189
+
190
+ FFI::NCurses.mvwaddch win, row, col, FFI::NCurses::ACS_ULCORNER
191
+ FFI::NCurses.mvwhline( win, row, col+1, FFI::NCurses::ACS_HLINE, width-6)
192
+ FFI::NCurses.mvwaddch win, row, col+width-5, FFI::NCurses::ACS_URCORNER
193
+ FFI::NCurses.mvwvline( win, row+1, col, FFI::NCurses::ACS_VLINE, height-4)
194
+
195
+ FFI::NCurses.mvwaddch win, row+height-3, col, FFI::NCurses::ACS_LLCORNER
196
+ FFI::NCurses.mvwhline(win, row+height-3, col+1, FFI::NCurses::ACS_HLINE, width-6)
197
+ FFI::NCurses.mvwaddch win, row+height-3, col+width-5, FFI::NCurses::ACS_LRCORNER
198
+ FFI::NCurses.mvwvline( win, row+1, col+width-5, FFI::NCurses::ACS_VLINE, height-4)
199
+ end # }}}
200
+ end
201
+
202
+ if __FILE__ == $0
203
+ include Umbra
204
+ ch = nil
205
+ begin
206
+ init_curses
207
+ cp = create_color_pair( COLOR_BLUE, COLOR_WHITE )
208
+ m = Dialog.new text: ARGV[0], title: ARGV[1]||"Alert", buttons: ["Yes", "No"], window_color_pair: cp, window_attr: NORMAL
209
+ ch = m.run
210
+ ensure
211
+ FFI::NCurses.endwin
212
+ end
213
+ puts "got key: #{ch}"
214
+ end
@@ -0,0 +1,134 @@
1
+ module Umbra
2
+ # module containing methods to enable widgets and forms to handle events.
3
+ # Included by form and widget
4
+ module EventHandler
5
+ # register_events: widgets may register their events prior to calling super {{{
6
+ # This ensures that caller programs don't use wrong event names.
7
+ #
8
+ def register_events eves
9
+ @_events ||= []
10
+ case eves
11
+ when Array
12
+ @_events.push(*eves)
13
+ when Symbol
14
+ @_events << eves
15
+ else
16
+ raise ArgumentError "register_events: Don't know how to handle #{eves.class}"
17
+ end
18
+ end # }}}
19
+ ##
20
+ # bind_event: bind an event to a block, optional args will also be passed when calling {{{
21
+ # 2018-04-01 - renamed +bind+ to +bind_event+
22
+ def bind_event event, *xargs, &blk
23
+ #$log.debug "#{self} called EventHandler BIND #{event}, args:#{xargs} "
24
+ if @_events
25
+ $log.warn "bind: #{self.class} does not support this event: #{event}. #{@_events} " if !event? event
26
+ #raise ArgumentError, "#{self.class} does not support this event: #{event}. #{@_events} " if !event? event
27
+ else
28
+ # it can come here if bind in initial block, since widgets add to @_event after calling super
29
+ # maybe we can change that.
30
+ $log.warn "BIND #{self.class} (#{event}) XXXXX no events defined in @_events. Please do so to avoid bugs and debugging. This will become a fatal error soon."
31
+ end
32
+ @handler ||= {}
33
+ @event_args ||= {}
34
+ @handler[event] ||= []
35
+ @handler[event] << blk
36
+ @event_args[event] ||= []
37
+ @event_args[event] << xargs
38
+ end # }}}
39
+
40
+ ##
41
+ # fire_handler: Fire all bindings for given event {{{
42
+ # e.g. fire_handler :ENTER, self
43
+ # The first parameter passed to the calling block is either self, or some action event
44
+ # The second and beyond are any objects you passed when using `bind` or `command`.
45
+ # Exceptions are caught here itself, or else they prevent objects from updating, usually the error is
46
+ # in the block sent in by application, not our error.
47
+ # TODO: if an object throws a subclass of VetoException we should not catch it and throw it back for
48
+ # caller to catch and take care of, such as prevent LEAVE or update etc.
49
+ def fire_handler event, object
50
+ $log.debug "inside def fire_handler evt:#{event}, o: #{object.class}"
51
+ if !@handler.nil?
52
+ if @_events
53
+ raise ArgumentError, "fire_handler: #{self.class} does not support this event: #{event}. #{@_events} " if !event? event
54
+ else
55
+ $log.debug "fire_handler #{self.class} XXXXX no events defined in @_events "
56
+ end
57
+ ablk = @handler[event]
58
+ if !ablk.nil?
59
+ aeve = @event_args[event]
60
+ ablk.each_with_index do |blk, ix|
61
+ #$log.debug "#{self} called EventHandler firehander #{@name}, #{event}, obj: #{object},args: #{aeve[ix]}"
62
+ $log.debug "#{self} called EventHandler firehander #{@name}, #{event}"
63
+ begin
64
+ blk.call object, *aeve[ix]
65
+ rescue FieldValidationException => fve
66
+ # added 2011-09-26 1.3.0 so a user raised exception on LEAVE
67
+ # keeps cursor in same field.
68
+ raise fve
69
+ #rescue PropertyVetoException => pve
70
+ # 2018-03-18 - commented off
71
+ # added 2011-09-26 1.3.0 so a user raised exception on LEAVE
72
+ # keeps cursor in same field.
73
+ #raise pve
74
+ rescue => ex
75
+ ## some don't have name
76
+ # FIXME this should be displayed somewhere. It just goes into log file quietly.
77
+ $log.error "======= Error ERROR in block event #{self}: #{event}"
78
+ $log.error ex
79
+ $log.error(ex.backtrace.join("\n"))
80
+ alert ex.to_s # added 2018-04-08 - 08:55 so it shows up
81
+ FFI::NCurses.beep # doesn't do anything, maybe switched off in preferences
82
+ end
83
+ end
84
+ else
85
+ # there is no block for this key/event
86
+ # we must behave exactly as processkey
87
+ # NOTE this is too risky since then buttons and radio buttons
88
+ # that don't have any command don;t update,so removing 2011-12-2
89
+ #return :UNHANDLED
90
+ return :NO_BLOCK
91
+ end # if
92
+ else
93
+ # there is no handler
94
+ # I've done this since list traps ENTER but rarely uses it.
95
+ # For buttons default, we'd like to trap ENTER even when focus is elsewhere
96
+ # we must behave exactly as processkey
97
+ # NOTE this is too risky since then buttons and radio buttons
98
+ # that don't have any command don;t update,so removing 2011-12-2
99
+ #return :UNHANDLED
100
+ # If caller wants, can return UNHANDLED such as list and ENTER.
101
+ return :NO_BLOCK
102
+ end # if
103
+ end # }}}
104
+
105
+ # event? : returns boolean depending on whether this widget has registered the given event {{{
106
+ def event? eve
107
+ @_events.include? eve
108
+ end # }}}
109
+
110
+ # ActionEvent # {{{
111
+ # source - as always is the object whose event has been fired
112
+ # id - event identifier (seems redundant since we bind events often separately.
113
+ # event - is :PRESS
114
+ # action_command - command string associated with event (such as title of button that changed
115
+ ActionEvent = Struct.new(:source, :event, :action_command) do
116
+ # This should always return the most relevant text associated with this object
117
+ # so the user does not have to go through the source object's documentation.
118
+ # It should be a user-friendly string
119
+ # @return text associated with source (label of button)
120
+ def text
121
+ source.text
122
+ end
123
+
124
+ # This is similar to text and can often be just an alias.
125
+ # However, i am putting this for backward compatibility with programs
126
+ # that received the object and called it's getvalue. It is better to use text.
127
+ # @return text associated with source (label of button)
128
+ def getvalue
129
+ raise "getvalue in eventhandler. remove if does not happen in 2018"
130
+ source.getvalue
131
+ end
132
+ end # }}}
133
+ end # module eventh
134
+ end # module