ncumbra 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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