canis 0.0.4
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.
- checksums.yaml +7 -0
- data/.gitignore +45 -0
- data/CHANGES +52 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +24 -0
- data/Rakefile +2 -0
- data/canis.gemspec +25 -0
- data/examples/alpmenu.rb +46 -0
- data/examples/app.sample +19 -0
- data/examples/appemail.rb +191 -0
- data/examples/atree.rb +105 -0
- data/examples/bline.rb +181 -0
- data/examples/common/devel.rb +319 -0
- data/examples/common/file.rb +93 -0
- data/examples/data/README.markdown +9 -0
- data/examples/data/brew.txt +38 -0
- data/examples/data/color.2 +37 -0
- data/examples/data/gemlist.txt +59 -0
- data/examples/data/lotr.txt +12 -0
- data/examples/data/ports.txt +136 -0
- data/examples/data/table.txt +37 -0
- data/examples/data/tasks.csv +88 -0
- data/examples/data/tasks.txt +27 -0
- data/examples/data/todo.txt +16 -0
- data/examples/data/todocsv.csv +28 -0
- data/examples/data/unix1.txt +21 -0
- data/examples/data/unix2.txt +11 -0
- data/examples/dbdemo.rb +506 -0
- data/examples/dirtree.rb +177 -0
- data/examples/newtabbedwindow.rb +100 -0
- data/examples/newtesttabp.rb +92 -0
- data/examples/tabular.rb +212 -0
- data/examples/tasks.rb +179 -0
- data/examples/term2.rb +88 -0
- data/examples/testbuttons.rb +307 -0
- data/examples/testcombo.rb +102 -0
- data/examples/testdb.rb +182 -0
- data/examples/testfields.rb +208 -0
- data/examples/testflowlayout.rb +43 -0
- data/examples/testkeypress.rb +98 -0
- data/examples/testlistbox.rb +187 -0
- data/examples/testlistbox1.rb +199 -0
- data/examples/testmessagebox.rb +144 -0
- data/examples/testprogress.rb +116 -0
- data/examples/testree.rb +107 -0
- data/examples/testsplitlayout.rb +53 -0
- data/examples/testsplitlayout1.rb +49 -0
- data/examples/teststacklayout.rb +48 -0
- data/examples/testwsshortcuts.rb +68 -0
- data/examples/testwsshortcuts2.rb +129 -0
- data/lib/canis.rb +16 -0
- data/lib/canis/core/docs/index.txt +104 -0
- data/lib/canis/core/docs/list.txt +16 -0
- data/lib/canis/core/docs/style_help.yml +34 -0
- data/lib/canis/core/docs/tabbedpane.txt +15 -0
- data/lib/canis/core/docs/table.txt +31 -0
- data/lib/canis/core/docs/textpad.txt +48 -0
- data/lib/canis/core/docs/tree.txt +23 -0
- data/lib/canis/core/include/.DS_Store +0 -0
- data/lib/canis/core/include/action.rb +83 -0
- data/lib/canis/core/include/actionmanager.rb +49 -0
- data/lib/canis/core/include/appmethods.rb +179 -0
- data/lib/canis/core/include/bordertitle.rb +49 -0
- data/lib/canis/core/include/canisparser.rb +100 -0
- data/lib/canis/core/include/colorparser.rb +437 -0
- data/lib/canis/core/include/defaultfilerenderer.rb +64 -0
- data/lib/canis/core/include/io.rb +320 -0
- data/lib/canis/core/include/layouts/SplitLayout.rb +161 -0
- data/lib/canis/core/include/layouts/abstractlayout.rb +213 -0
- data/lib/canis/core/include/layouts/flowlayout.rb +104 -0
- data/lib/canis/core/include/layouts/stacklayout.rb +109 -0
- data/lib/canis/core/include/listbindings.rb +89 -0
- data/lib/canis/core/include/listeditable.rb +319 -0
- data/lib/canis/core/include/listoperations.rb +61 -0
- data/lib/canis/core/include/listselectionmodel.rb +388 -0
- data/lib/canis/core/include/multibuffer.rb +173 -0
- data/lib/canis/core/include/ractionevent.rb +73 -0
- data/lib/canis/core/include/rchangeevent.rb +27 -0
- data/lib/canis/core/include/rhistory.rb +95 -0
- data/lib/canis/core/include/rinputdataevent.rb +47 -0
- data/lib/canis/core/include/textdocument.rb +111 -0
- data/lib/canis/core/include/vieditable.rb +175 -0
- data/lib/canis/core/include/widgetmenu.rb +66 -0
- data/lib/canis/core/system/colormap.rb +165 -0
- data/lib/canis/core/system/keydefs.rb +32 -0
- data/lib/canis/core/system/ncurses.rb +237 -0
- data/lib/canis/core/system/panel.rb +129 -0
- data/lib/canis/core/system/window.rb +1081 -0
- data/lib/canis/core/util/ansiparser.rb +119 -0
- data/lib/canis/core/util/app.rb +696 -0
- data/lib/canis/core/util/basestack.rb +412 -0
- data/lib/canis/core/util/defaultcolorparser.rb +84 -0
- data/lib/canis/core/util/extras/README +5 -0
- data/lib/canis/core/util/extras/bottomline.rb +1815 -0
- data/lib/canis/core/util/extras/padreader.rb +192 -0
- data/lib/canis/core/util/focusmanager.rb +31 -0
- data/lib/canis/core/util/helpmanager.rb +160 -0
- data/lib/canis/core/util/oldwidgetshortcuts.rb +304 -0
- data/lib/canis/core/util/promptmenu.rb +235 -0
- data/lib/canis/core/util/rcommandwindow.rb +933 -0
- data/lib/canis/core/util/rdialogs.rb +520 -0
- data/lib/canis/core/util/textutils.rb +74 -0
- data/lib/canis/core/util/viewer.rb +238 -0
- data/lib/canis/core/util/widgetshortcuts.rb +508 -0
- data/lib/canis/core/widgets/applicationheader.rb +103 -0
- data/lib/canis/core/widgets/box.rb +58 -0
- data/lib/canis/core/widgets/divider.rb +310 -0
- data/lib/canis/core/widgets/extras/README.md +12 -0
- data/lib/canis/core/widgets/extras/rtextarea.rb +960 -0
- data/lib/canis/core/widgets/extras/stackflow.rb +474 -0
- data/lib/canis/core/widgets/keylabelprinter.rb +194 -0
- data/lib/canis/core/widgets/listbox.rb +326 -0
- data/lib/canis/core/widgets/listfooter.rb +86 -0
- data/lib/canis/core/widgets/rcombo.rb +210 -0
- data/lib/canis/core/widgets/rcontainer.rb +415 -0
- data/lib/canis/core/widgets/rlink.rb +30 -0
- data/lib/canis/core/widgets/rmenu.rb +970 -0
- data/lib/canis/core/widgets/rmenulink.rb +30 -0
- data/lib/canis/core/widgets/rmessagebox.rb +400 -0
- data/lib/canis/core/widgets/rprogress.rb +118 -0
- data/lib/canis/core/widgets/rtabbedpane.rb +631 -0
- data/lib/canis/core/widgets/rtabbedwindow.rb +70 -0
- data/lib/canis/core/widgets/rwidget.rb +3634 -0
- data/lib/canis/core/widgets/scrollbar.rb +147 -0
- data/lib/canis/core/widgets/statusline.rb +113 -0
- data/lib/canis/core/widgets/table.rb +1072 -0
- data/lib/canis/core/widgets/tabular.rb +264 -0
- data/lib/canis/core/widgets/textpad.rb +1674 -0
- data/lib/canis/core/widgets/tree.rb +690 -0
- data/lib/canis/core/widgets/tree/treecellrenderer.rb +150 -0
- data/lib/canis/core/widgets/tree/treemodel.rb +432 -0
- data/lib/canis/version.rb +3 -0
- metadata +229 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
require 'canis'
|
|
2
|
+
|
|
3
|
+
module Canis
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# A vim-like application status bar that can display time and various other statuses
|
|
7
|
+
# at the bottom, typically above the dock (3rd line from last).
|
|
8
|
+
#
|
|
9
|
+
class ListFooter
|
|
10
|
+
attr_accessor :config
|
|
11
|
+
attr_accessor :color_pair
|
|
12
|
+
attr_accessor :attrib
|
|
13
|
+
|
|
14
|
+
def initialize config={}, &block
|
|
15
|
+
@config = config
|
|
16
|
+
@color_pair = get_color($datacolor, config[:color], config[:bgcolor])
|
|
17
|
+
@attrib = config[:attrib]
|
|
18
|
+
instance_eval &block if block_given?
|
|
19
|
+
end
|
|
20
|
+
#
|
|
21
|
+
# command that returns a string that populates the status line (left aligned)
|
|
22
|
+
# @see :right
|
|
23
|
+
# e.g.
|
|
24
|
+
# @l.command { "%-20s [DB: %-s | %-s ]" % [ Time.now, $current_db || "None", $current_table || "----"] }
|
|
25
|
+
#
|
|
26
|
+
def command *args, &blk
|
|
27
|
+
@command_text = blk
|
|
28
|
+
@command_args = args
|
|
29
|
+
end
|
|
30
|
+
alias :left :command
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# Procudure for text to be right aligned in statusline
|
|
34
|
+
def command_right *args, &blk
|
|
35
|
+
@right_text = blk
|
|
36
|
+
@right_args = args
|
|
37
|
+
end
|
|
38
|
+
def text(comp)
|
|
39
|
+
@command_text.call(comp, @command_args)
|
|
40
|
+
end
|
|
41
|
+
def right_text(comp)
|
|
42
|
+
@right_text.call(comp, @right_args)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# supply a default print function. The user program need not call this. It may be overridden
|
|
46
|
+
# The idea of passing a component at this stage is that one footer can be created for several
|
|
47
|
+
# tables or text components, each will pass +self+ when calling print.
|
|
48
|
+
# @param comp : the calling textpad component (usually passed as +self+)
|
|
49
|
+
def print comp
|
|
50
|
+
config = @config
|
|
51
|
+
row = comp.row + comp.height - 1
|
|
52
|
+
col = comp.col + 2
|
|
53
|
+
len = comp.width - col
|
|
54
|
+
g = comp.form.window
|
|
55
|
+
# we check just in case user nullifies it deliberately, since he may have changed config values
|
|
56
|
+
@color_pair ||= get_color($datacolor, config[:color], config[:bgcolor])
|
|
57
|
+
@attrib ||= config[:attrib] || Ncurses::A_REVERSE
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# first print dashes through
|
|
61
|
+
#g.printstring row, col, "%s" % "-" * len, @color_pair, Ncurses::A_REVERSE
|
|
62
|
+
|
|
63
|
+
# now call the block to get current values for footer text on left
|
|
64
|
+
ftext = nil
|
|
65
|
+
if @command_text
|
|
66
|
+
ftext = text(comp)
|
|
67
|
+
else
|
|
68
|
+
if !@right_text
|
|
69
|
+
# user has not specified right or left, so we use a default on left
|
|
70
|
+
ftext = "#{comp.current_index} of #{comp.size} "
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
g.printstring(row, col, ftext, @color_pair, @attrib) if ftext
|
|
74
|
+
|
|
75
|
+
# user has specified text for right, print it
|
|
76
|
+
if @right_text
|
|
77
|
+
len = comp.width
|
|
78
|
+
ftext = right_text(comp)
|
|
79
|
+
c = len - ftext.length - 2
|
|
80
|
+
g.printstring row, c, ftext, @color_pair, @attrib
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
end # class
|
|
86
|
+
end # module
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# ----------------------------------------------------------------------------- #
|
|
2
|
+
# File: rcombo.rb
|
|
3
|
+
# Description: Non-editable combo box.
|
|
4
|
+
# Make it dead-simple to use.
|
|
5
|
+
# This is a simpler version of the original ComboBox which allowed
|
|
6
|
+
# editing and used rlistbox. This simpler class is meant for the canis
|
|
7
|
+
# core package and will only depend on a core class if at all.
|
|
8
|
+
# Author: jkepler http://github.com/mare-imbrium/canis/
|
|
9
|
+
# Date: 2011-11-11 - 21:42
|
|
10
|
+
# License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
|
|
11
|
+
# Last update: 2014-07-10 00:27
|
|
12
|
+
# ----------------------------------------------------------------------------- #
|
|
13
|
+
#
|
|
14
|
+
require 'canis'
|
|
15
|
+
|
|
16
|
+
include Canis
|
|
17
|
+
module Canis
|
|
18
|
+
extend self
|
|
19
|
+
|
|
20
|
+
# the quick approach would be to use field, and just add a popup.
|
|
21
|
+
# Or since we are not editing, we could use a Label and a popup
|
|
22
|
+
# Or just display a label and a popup without using anything else.
|
|
23
|
+
# Thre is an undocumented variable +width+ which is the size of the label
|
|
24
|
+
# This is used to position the combo symbol and the popup. This can be calculated
|
|
25
|
+
# based on the label. 2014-03-24 - 16:42
|
|
26
|
+
|
|
27
|
+
class ComboBox < LabeledField
|
|
28
|
+
include Canis::EventHandler
|
|
29
|
+
dsl_accessor :list_config
|
|
30
|
+
|
|
31
|
+
attr_accessor :current_index
|
|
32
|
+
# the symbol you want to use for combos
|
|
33
|
+
attr_accessor :combo_symbol
|
|
34
|
+
attr_accessor :show_symbol # show that funny symbol after a combo to signify its a combo
|
|
35
|
+
dsl_accessor :arrow_key_policy # :IGNORE :NEXT_ROW :POPUP
|
|
36
|
+
|
|
37
|
+
def initialize form, config={}, &block
|
|
38
|
+
@arrow_key_policy = :ignore
|
|
39
|
+
@editable = false
|
|
40
|
+
#@combo_symbol = "v".ord # trying this out
|
|
41
|
+
# thanks hramrach for fix
|
|
42
|
+
if RUBY_VERSION < "1.9" then
|
|
43
|
+
@combo_symbol = "v"[0] # trying this out
|
|
44
|
+
else
|
|
45
|
+
@combo_symbol = "v".ord # trying this out
|
|
46
|
+
end
|
|
47
|
+
@current_index = 0
|
|
48
|
+
super
|
|
49
|
+
## this was getting overridden this making the combo editable 2014-03-24 - 16:24
|
|
50
|
+
@editable = false
|
|
51
|
+
# added if check since it was overriding text in creation. 2009-01-18 00:03
|
|
52
|
+
text @list[@current_index].dup if @buffer.nil? or @buffer.empty?
|
|
53
|
+
init_vars
|
|
54
|
+
@_events.push(*[:CHANGE, :ENTER_ROW, :LEAVE_ROW])
|
|
55
|
+
end
|
|
56
|
+
def init_vars
|
|
57
|
+
super
|
|
58
|
+
@show_symbol = true if @show_symbol.nil? # if set to false don't touch
|
|
59
|
+
#@show_symbol = false if @label # 2011-11-13
|
|
60
|
+
@combo_symbol ||= FFI::NCurses::ACS_DARROW #GEQUAL
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
def selected_item
|
|
64
|
+
@list[@current_index]
|
|
65
|
+
end
|
|
66
|
+
def selected_index
|
|
67
|
+
@current_index
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
##
|
|
71
|
+
# convert given list to datamodel
|
|
72
|
+
def list alist=nil
|
|
73
|
+
return @list if alist.nil?
|
|
74
|
+
#@list = Canis::ListDataModel.new(alist)
|
|
75
|
+
@list = alist
|
|
76
|
+
end
|
|
77
|
+
##
|
|
78
|
+
# combo edit box key handling
|
|
79
|
+
# removed UP and DOWN and bound it, so it can be unbound
|
|
80
|
+
def handle_key(ch)
|
|
81
|
+
@current_index ||= 0
|
|
82
|
+
# added 2009-01-18 22:44 no point moving horiz or passing up to Field if not edit
|
|
83
|
+
if !@editable
|
|
84
|
+
if ch == KEY_LEFT or ch == KEY_RIGHT
|
|
85
|
+
return :UNHANDLED
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
case @arrow_key_policy
|
|
89
|
+
when :ignore
|
|
90
|
+
if ch == KEY_DOWN or ch == KEY_UP
|
|
91
|
+
return :UNHANDLED
|
|
92
|
+
end
|
|
93
|
+
when :popup
|
|
94
|
+
if ch == KEY_DOWN or ch == KEY_UP
|
|
95
|
+
popup
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
case ch
|
|
99
|
+
#when KEY_UP # show previous value
|
|
100
|
+
# previous_row
|
|
101
|
+
#when KEY_DOWN # show previous value
|
|
102
|
+
# next_row
|
|
103
|
+
# adding spacebar to popup combo, as in microemacs 2010-10-01 13:21
|
|
104
|
+
when 32, KEY_DOWN+ META_KEY # alt down
|
|
105
|
+
popup # pop up the popup
|
|
106
|
+
else
|
|
107
|
+
super
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
##
|
|
111
|
+
# calls a popup list
|
|
112
|
+
# TODO: should not be positioned so that it goes off edge
|
|
113
|
+
# user's customizations of list should be passed in
|
|
114
|
+
# The dup of listconfig is due to a tricky feature/bug.
|
|
115
|
+
# I try to keep the config hash and instance variables in synch. So
|
|
116
|
+
# this config hash is sent to popuplist which updates its row col and
|
|
117
|
+
# next time we pop up the popup row and col are zero.
|
|
118
|
+
#
|
|
119
|
+
#
|
|
120
|
+
# added dup in PRESS since editing edit field mods this
|
|
121
|
+
# on pressing ENTER, value set back and current_index updated
|
|
122
|
+
def popup
|
|
123
|
+
@list_config ||= {}
|
|
124
|
+
@list_config[:row] ||= @row
|
|
125
|
+
#@list_config[:col] ||= @col
|
|
126
|
+
#@list_config[:col] ||= @col + @width
|
|
127
|
+
# after introducing LabeledField which starts with lcol and uses col for field
|
|
128
|
+
@list_config[:col] ||= @col
|
|
129
|
+
@list_config[:relative_to] ||= self
|
|
130
|
+
# this does not allow us to bind to events in the list
|
|
131
|
+
index = popuplist @list, @list_config
|
|
132
|
+
if index
|
|
133
|
+
text @list[index].dup
|
|
134
|
+
set_modified(true) if @current_index != index
|
|
135
|
+
@current_index = index
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Field putc advances cursor when it gives a char so we override this
|
|
140
|
+
def putc c
|
|
141
|
+
if c >= 0 and c <= 127
|
|
142
|
+
ret = putch c.chr
|
|
143
|
+
if ret == 0
|
|
144
|
+
addcol 1 if @editable
|
|
145
|
+
set_modified
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
return -1 # always ??? XXX
|
|
149
|
+
end
|
|
150
|
+
##
|
|
151
|
+
# field does not give char to non-editable fields so we override
|
|
152
|
+
def putch char
|
|
153
|
+
@current_index ||= 0
|
|
154
|
+
if @editable
|
|
155
|
+
raise "how is it editable here in combo"
|
|
156
|
+
super
|
|
157
|
+
return 0
|
|
158
|
+
else
|
|
159
|
+
match = next_match(char)
|
|
160
|
+
text match unless match.nil?
|
|
161
|
+
fire_handler :ENTER_ROW, self
|
|
162
|
+
end
|
|
163
|
+
@modified = true
|
|
164
|
+
fire_handler :CHANGE, self # 2008-12-09 14:51 ???
|
|
165
|
+
0
|
|
166
|
+
end
|
|
167
|
+
##
|
|
168
|
+
# the sets the next match in the edit field
|
|
169
|
+
def next_match char
|
|
170
|
+
start = @current_index
|
|
171
|
+
start.upto(@list.length-1) do |ix|
|
|
172
|
+
if @list[ix][0,1].casecmp(char) == 0
|
|
173
|
+
return @list[ix] unless @list[ix] == @buffer
|
|
174
|
+
end
|
|
175
|
+
@current_index += 1
|
|
176
|
+
end
|
|
177
|
+
## could not find, start from zero
|
|
178
|
+
@current_index = 0
|
|
179
|
+
start = [@list.length()-1, start].min
|
|
180
|
+
0.upto(start) do |ix|
|
|
181
|
+
if @list[ix][0,1].casecmp(char) == 0
|
|
182
|
+
return @list[ix] unless @list[ix] == @buffer
|
|
183
|
+
end
|
|
184
|
+
@current_index += 1
|
|
185
|
+
end
|
|
186
|
+
@current_index = [@list.length()-1, @current_index].min
|
|
187
|
+
return nil
|
|
188
|
+
end
|
|
189
|
+
##
|
|
190
|
+
# on leaving the listbox, update the combo/datamodel.
|
|
191
|
+
# we are using methods of the datamodel. Updating our list will have
|
|
192
|
+
# no effect on the list, and wont trigger events.
|
|
193
|
+
# Do not override.
|
|
194
|
+
def on_leave
|
|
195
|
+
fire_handler :LEAVE, self
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def repaint
|
|
199
|
+
super
|
|
200
|
+
c = @col + @width
|
|
201
|
+
if @show_symbol # 2009-01-11 18:47
|
|
202
|
+
# i have changed c +1 to c, since we have no right to print beyond width
|
|
203
|
+
@form.window.mvwaddch @row, c, @combo_symbol # Ncurses::ACS_GEQUAL
|
|
204
|
+
@form.window.mvchgat(y=@row, x=c, max=1, Ncurses::A_REVERSE|Ncurses::A_UNDERLINE, $datacolor, nil)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
end # class ComboBox
|
|
209
|
+
|
|
210
|
+
end # module
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
* Name: A container that manages components placed in it but
|
|
3
|
+
is not a form. Thus it can be safely placed as a widget
|
|
4
|
+
without all the complicatinos of a form embedded inside another.
|
|
5
|
+
NOTE: Still experimental
|
|
6
|
+
* Description
|
|
7
|
+
* Author: jkepler (http://github.com/mare-imbrium/canis/)
|
|
8
|
+
* Date: 21.10.11 - 00:29
|
|
9
|
+
* License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
|
|
10
|
+
|
|
11
|
+
* Last update: 2014-04-24 01:04
|
|
12
|
+
== CHANGES
|
|
13
|
+
Focusables so we don't focus on label
|
|
14
|
+
== TODO
|
|
15
|
+
How to put blank lines in stack - use a blank label
|
|
16
|
+
|
|
17
|
+
- The contaomers and multis need to do their own on_enter and on_leave
|
|
18
|
+
management, they cannot rely on some other container doing it.
|
|
19
|
+
We can only rely on handle_key being called. HK should determine
|
|
20
|
+
whether any set_form row etc needs to be done.
|
|
21
|
+
- Should have its own stack and flow
|
|
22
|
+
=end
|
|
23
|
+
|
|
24
|
+
require 'canis'
|
|
25
|
+
|
|
26
|
+
include Canis
|
|
27
|
+
module Canis
|
|
28
|
+
extend self
|
|
29
|
+
|
|
30
|
+
# This is an attempt at having a container which can contain multiple
|
|
31
|
+
# widgets without being a form itself. Having forms within forms
|
|
32
|
+
# complicates code too much, esp cursor positioning. e.g. tabbedpane
|
|
33
|
+
|
|
34
|
+
class Container < Widget
|
|
35
|
+
|
|
36
|
+
dsl_accessor :suppress_borders #to_print_borders
|
|
37
|
+
dsl_accessor :border_attrib, :border_color
|
|
38
|
+
dsl_accessor :title #set this on top
|
|
39
|
+
dsl_accessor :title_attrib #bold, reverse, normal
|
|
40
|
+
# should container stack objects ignoring users row col
|
|
41
|
+
# this is esp needed since App sets row and col which is too early
|
|
42
|
+
# This is now the default value, till i can redo things
|
|
43
|
+
#dsl_accessor :stack
|
|
44
|
+
dsl_accessor :positioning # absolute, relative, stack
|
|
45
|
+
attr_reader :current_component
|
|
46
|
+
|
|
47
|
+
def initialize form=nil, config={}, &block
|
|
48
|
+
@suppress_borders = false
|
|
49
|
+
@row_offset = @col_offset = 1
|
|
50
|
+
@_events ||= []
|
|
51
|
+
@stack = true
|
|
52
|
+
@positioning = :stack
|
|
53
|
+
super
|
|
54
|
+
@focusable = true
|
|
55
|
+
@editable = false
|
|
56
|
+
@components = [] # all components
|
|
57
|
+
@focusables = [] # focusable components, makes checks easier
|
|
58
|
+
|
|
59
|
+
init_vars
|
|
60
|
+
end
|
|
61
|
+
def init_vars
|
|
62
|
+
@repaint_required = true
|
|
63
|
+
@row_offset = @col_offset = 0 if @suppress_borders # FIXME supposed to use this !!
|
|
64
|
+
|
|
65
|
+
@internal_width = 2
|
|
66
|
+
@internal_width = 1 if @suppress_borders
|
|
67
|
+
@name ||= "AContainer"
|
|
68
|
+
@first_time = true
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# NOTE: since we are handling the traversal, we delink the object from any
|
|
73
|
+
# form's widgets array that might have been added. Whenever a form is available,
|
|
74
|
+
# we set it (without adding widget to it) so it can print using the form's window.
|
|
75
|
+
#
|
|
76
|
+
# @param [Widget] to add
|
|
77
|
+
def add *items
|
|
78
|
+
items.each do |c|
|
|
79
|
+
raise ArgumentError, "Nil component passed to add" unless c
|
|
80
|
+
if c.is_a? Widget
|
|
81
|
+
if c.form && c.form != @form
|
|
82
|
+
$log.debug " removing widget VIMSPLIT #{c.class} wr: #{c.row} row:#{@row} ht:#{@height} "
|
|
83
|
+
c.form.remove_widget c
|
|
84
|
+
c.form = nil
|
|
85
|
+
# or should i just stack them myself and screw what you've asked for
|
|
86
|
+
end
|
|
87
|
+
# take it out of form's control. We will control it.
|
|
88
|
+
if c.form
|
|
89
|
+
c.form.remove_widget c
|
|
90
|
+
end
|
|
91
|
+
# shoot, what if at this point the container does not have a form
|
|
92
|
+
attach_form c if @form
|
|
93
|
+
end
|
|
94
|
+
# most likely if you have created both container and widgets
|
|
95
|
+
# inside app, it would have given row after container
|
|
96
|
+
|
|
97
|
+
@components << c
|
|
98
|
+
if c.focusable
|
|
99
|
+
@focusables << c
|
|
100
|
+
@current_component ||= c # only the first else cursor falls on last on enter
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
end # items each
|
|
104
|
+
self
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# When we get a form, we silently attach it to this object, without the form
|
|
108
|
+
# knowing. We don't want form managing this object.
|
|
109
|
+
def attach_form c
|
|
110
|
+
c.form = @form
|
|
111
|
+
c.override_graphic @graphic
|
|
112
|
+
c.parent_component = self
|
|
113
|
+
end
|
|
114
|
+
alias :add_widget :add
|
|
115
|
+
def widgets; @components; end
|
|
116
|
+
# what of by_name
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# correct coordinates of comp esp if App has stacked them after this
|
|
120
|
+
# container
|
|
121
|
+
# It is best to use the simple stack feature. The rest could change at any time
|
|
122
|
+
# and is quite arbitrary. Some folks may set absolute locations if container
|
|
123
|
+
# is directly on a form, others may set relative locations if it is inside a
|
|
124
|
+
# tabbed pane or other container. Thus, stacks are best
|
|
125
|
+
def correct_component c
|
|
126
|
+
raise "Form is still not set in Container" unless @form
|
|
127
|
+
attach_form(c) unless c.form
|
|
128
|
+
@last_row ||= @row + 1
|
|
129
|
+
inset = 2
|
|
130
|
+
# 2011-10-20 current default behaviour is to stack
|
|
131
|
+
if @positioning == :stack
|
|
132
|
+
c.row = @last_row
|
|
133
|
+
c.col = @col + inset
|
|
134
|
+
|
|
135
|
+
# do not advance row, save col for next row
|
|
136
|
+
@last_row += 1
|
|
137
|
+
elsif @positioning == :relative # UNTESTED NOTE
|
|
138
|
+
if (c.row || 0) <= 0
|
|
139
|
+
$log.warn "c.row in CONTAINER is #{c.row} "
|
|
140
|
+
c.row = @last_row
|
|
141
|
+
@last_row += 1
|
|
142
|
+
elsif c.row > @row + @height -1
|
|
143
|
+
$log.warn "c.row in CONTAINER exceeds container. #{c.row} "
|
|
144
|
+
c.row -= @height - @row_offset
|
|
145
|
+
else
|
|
146
|
+
# this is where it should come
|
|
147
|
+
c.row += @row + @row_offset
|
|
148
|
+
@last_row = c.row + 1
|
|
149
|
+
end
|
|
150
|
+
if (c.col || 0) <= 0
|
|
151
|
+
c.col = @col + inset + @col_offset
|
|
152
|
+
elsif c.col > @col + @width -1
|
|
153
|
+
c.col -= @width
|
|
154
|
+
elsif c.col == @col
|
|
155
|
+
c.col += @col_offset + inset
|
|
156
|
+
else #f c.col < @col
|
|
157
|
+
c.col += @col+@col_offset
|
|
158
|
+
end
|
|
159
|
+
$log.debug "XXX: CORRECT #{c.name} r:#{c.row} c:#{c.col} "
|
|
160
|
+
end
|
|
161
|
+
@first_time = false
|
|
162
|
+
end
|
|
163
|
+
def check_component c
|
|
164
|
+
raise "row is less than container #{c.row} #{@row} " if c.row <= @row
|
|
165
|
+
raise "col is less than container #{c.col} #{@col} " if c.col <= @col
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
public
|
|
169
|
+
# repaint object
|
|
170
|
+
# called by Form, and sometimes parent component (if not form).
|
|
171
|
+
def repaint
|
|
172
|
+
my_win = @form ? @form.window : @target_window
|
|
173
|
+
@graphic = my_win unless @graphic
|
|
174
|
+
raise " #{@name} NO GRAPHIC set as yet CONTAINER paint " unless @graphic
|
|
175
|
+
@components.each { |e| correct_component e } if @first_time
|
|
176
|
+
#@components.each { |e| check_component e } # seeme one if printing out
|
|
177
|
+
|
|
178
|
+
#return unless @repaint_required
|
|
179
|
+
|
|
180
|
+
# if some major change has happened then repaint everything
|
|
181
|
+
if @repaint_required
|
|
182
|
+
$log.debug " VIM repaint graphic #{@graphic} "
|
|
183
|
+
print_borders unless @suppress_borders # do this once only, unless everything changes
|
|
184
|
+
@components.each { |e| e.repaint_all(true); e.repaint }
|
|
185
|
+
else
|
|
186
|
+
@components.each { |e| e.repaint }
|
|
187
|
+
end # if repaint_required
|
|
188
|
+
|
|
189
|
+
@repaint_required = false
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
private
|
|
193
|
+
def print_borders
|
|
194
|
+
width = @width
|
|
195
|
+
height = @height-1 # 2010-01-04 15:30 BUFFERED HEIGHT
|
|
196
|
+
window = @graphic # 2010-01-04 12:37 BUFFERED
|
|
197
|
+
startcol = @col
|
|
198
|
+
startrow = @row
|
|
199
|
+
@color_pair = get_color($datacolor)
|
|
200
|
+
#$log.debug "rlistb #{name}: window.print_border #{startrow}, #{startcol} , h:#{height}, w:#{width} , @color_pair, @attr "
|
|
201
|
+
window.print_border startrow, startcol, height, width, @color_pair, @attr
|
|
202
|
+
print_title
|
|
203
|
+
end
|
|
204
|
+
def print_title
|
|
205
|
+
$log.debug "CONTAINER PRINTING TITLE at #{row} #{col} "
|
|
206
|
+
@graphic.printstring( @row, @col+(@width-@title.length)/2, @title, @color_pair, @title_attrib) unless @title.nil?
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
public
|
|
210
|
+
# called by parent or form, otherwise its private
|
|
211
|
+
def handle_key ch
|
|
212
|
+
$log.debug " CONTAINER handle_key #{ch} "
|
|
213
|
+
return if @components.empty?
|
|
214
|
+
_multiplier = ($multiplier == 0 ? 1 : $multiplier )
|
|
215
|
+
|
|
216
|
+
# should this go here 2011-10-19
|
|
217
|
+
unless @_entered
|
|
218
|
+
$log.warn "XXX WARN: calling ON_ENTER since in this situation it was not called"
|
|
219
|
+
on_enter
|
|
220
|
+
end
|
|
221
|
+
if ch == KEY_TAB
|
|
222
|
+
$log.debug "CONTAINER GOTO NEXT TAB"
|
|
223
|
+
return goto_next_component
|
|
224
|
+
elsif ch == KEY_BTAB
|
|
225
|
+
return goto_prev_component
|
|
226
|
+
end
|
|
227
|
+
comp = @current_component
|
|
228
|
+
$log.debug " CONTAINER handle_key #{ch}: #{comp}"
|
|
229
|
+
if comp
|
|
230
|
+
ret = comp.handle_key(ch)
|
|
231
|
+
$log.debug " CONTAINER handle_key#{ch}: #{comp} returned #{ret} "
|
|
232
|
+
if ret != :UNHANDLED
|
|
233
|
+
comp.repaint # NOTE: if we don;t do this, then it won't get repainted. I will have to repaint ALL
|
|
234
|
+
# in repaint of this.
|
|
235
|
+
return ret
|
|
236
|
+
end
|
|
237
|
+
$log.debug "XXX CONTAINER key unhandled by comp #{comp.name} "
|
|
238
|
+
else
|
|
239
|
+
$log.warn "XXX CONTAINER key unhandled NULL comp"
|
|
240
|
+
end
|
|
241
|
+
case ch
|
|
242
|
+
when ?\C-c.getbyte(0)
|
|
243
|
+
$multiplier = 0
|
|
244
|
+
return 0
|
|
245
|
+
when ?0.getbyte(0)..?9.getbyte(0)
|
|
246
|
+
$log.debug " VIM coming here to set multiplier #{$multiplier} "
|
|
247
|
+
$multiplier *= 10 ; $multiplier += (ch-48)
|
|
248
|
+
return 0
|
|
249
|
+
end
|
|
250
|
+
ret = process_key ch, self
|
|
251
|
+
# allow user to map left and right if he wants
|
|
252
|
+
if ret == :UNHANDLED
|
|
253
|
+
case ch
|
|
254
|
+
when KEY_UP
|
|
255
|
+
# form will pick this up and do needful
|
|
256
|
+
return goto_prev_component #unless on_first_component?
|
|
257
|
+
when KEY_LEFT
|
|
258
|
+
# if i don't check for first component, key will go back to form,
|
|
259
|
+
# but not be processes. so focussed remain here, but be false.
|
|
260
|
+
# In case of returnign an unhandled TAB, on_leave will happen and cursor will move to
|
|
261
|
+
# previous component outside of this.
|
|
262
|
+
return goto_prev_component unless on_first_component?
|
|
263
|
+
when KEY_RIGHT
|
|
264
|
+
return goto_next_component #unless on_last_component?
|
|
265
|
+
when KEY_DOWN
|
|
266
|
+
return goto_next_component #unless on_last_component?
|
|
267
|
+
else
|
|
268
|
+
@_entered = false
|
|
269
|
+
return :UNHANDLED
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
$multiplier = 0
|
|
274
|
+
return 0
|
|
275
|
+
end
|
|
276
|
+
# Actually we should only go to current component if it accepted
|
|
277
|
+
# a key stroke. if user tabbed thru it, then no point going back to
|
|
278
|
+
# it. Go to first or last depending on TAB or BACKTAB otherwise.
|
|
279
|
+
# NOTE: if user comes in using DOWN or UP, last traversed component will get the focus
|
|
280
|
+
#
|
|
281
|
+
def on_enter
|
|
282
|
+
# if BTAB, the last comp XXX they must be focusable FIXME
|
|
283
|
+
if $current_key == KEY_BTAB || $current_key == KEY_UP
|
|
284
|
+
@current_component = @focusables.last
|
|
285
|
+
else
|
|
286
|
+
@current_component = @focusables.first
|
|
287
|
+
end
|
|
288
|
+
return unless @current_component
|
|
289
|
+
$log.debug " CONTAINER came to ON_ENTER #{@current_component} "
|
|
290
|
+
set_form_row
|
|
291
|
+
@_entered = true
|
|
292
|
+
end
|
|
293
|
+
# we cannot be sure that this will be called especially if this is embedded
|
|
294
|
+
# inside some other component
|
|
295
|
+
def on_leave
|
|
296
|
+
@_entered = false
|
|
297
|
+
super
|
|
298
|
+
end
|
|
299
|
+
def goto_next_component
|
|
300
|
+
if @current_component != nil
|
|
301
|
+
leave_current_component
|
|
302
|
+
if on_last_component?
|
|
303
|
+
#@_entered = false
|
|
304
|
+
return :UNHANDLED
|
|
305
|
+
end
|
|
306
|
+
@current_index = @focusables.index(@current_component)
|
|
307
|
+
index = @current_index + 1
|
|
308
|
+
f = @focusables[index]
|
|
309
|
+
if f
|
|
310
|
+
@current_index = index
|
|
311
|
+
@current_component = f
|
|
312
|
+
return set_form_row
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
@_entered = false
|
|
316
|
+
return :UNHANDLED
|
|
317
|
+
end
|
|
318
|
+
def goto_prev_component
|
|
319
|
+
if @current_component != nil
|
|
320
|
+
leave_current_component
|
|
321
|
+
if on_first_component?
|
|
322
|
+
@_entered = false
|
|
323
|
+
return :UNHANDLED
|
|
324
|
+
end
|
|
325
|
+
@current_index = @focusables.index(@current_component)
|
|
326
|
+
index = @current_index -= 1
|
|
327
|
+
f = @focusables[index]
|
|
328
|
+
if f
|
|
329
|
+
@current_index = index
|
|
330
|
+
@current_component = f
|
|
331
|
+
return set_form_row
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
return :UNHANDLED
|
|
335
|
+
end
|
|
336
|
+
# private
|
|
337
|
+
# XXX why are we calling 3 methods in a row, why not OE manages these 3
|
|
338
|
+
# There's double calling going on.
|
|
339
|
+
def set_form_row
|
|
340
|
+
return :UNHANDLED if @current_component.nil?
|
|
341
|
+
cc = @current_component
|
|
342
|
+
$log.debug "CONT #{@name} set_form_row calling sfr for #{cc.name}, r #{cc.row} c: #{cc.col} "
|
|
343
|
+
$log.debug " CONTAINER on enter sfr #{@current_component.name} #{@current_component} "
|
|
344
|
+
|
|
345
|
+
# bug caught here. we were printing a field before it had been set, so it printed out
|
|
346
|
+
@components.each { |e| correct_component e } if @first_time
|
|
347
|
+
@current_component.on_enter
|
|
348
|
+
@current_component.set_form_row # why was this missing in vimsplit. is it
|
|
349
|
+
$log.debug "CONT2 #{@name} set_form_row calling sfr for #{cc.name}, r #{cc.row} c: #{cc.col} "
|
|
350
|
+
# that on_enter does a set_form_row
|
|
351
|
+
@current_component.set_form_col # XXX
|
|
352
|
+
@current_component.repaint # OMG this could happen before we've set row and col
|
|
353
|
+
# XXX compo should do set_form_row and col if it has that
|
|
354
|
+
end
|
|
355
|
+
#
|
|
356
|
+
def set_form_col
|
|
357
|
+
return if @current_component.nil?
|
|
358
|
+
$log.debug " #{@name} CONTAINER EMPTY set_form_col calling sfc for #{@current_component.name} "
|
|
359
|
+
# already called from above.
|
|
360
|
+
#@current_component.set_form_col
|
|
361
|
+
end
|
|
362
|
+
# leave the component we are on.
|
|
363
|
+
# This should be followed by all containers, so that the on_leave action
|
|
364
|
+
# of earlier comp can be displayed, such as dimming components selections
|
|
365
|
+
def leave_current_component
|
|
366
|
+
@current_component.on_leave
|
|
367
|
+
# NOTE this is required, since repaint will just not happen otherwise
|
|
368
|
+
# Some components are erroneously repainting all, after setting this to true so it is
|
|
369
|
+
# working there.
|
|
370
|
+
@current_component.repaint_required true
|
|
371
|
+
$log.debug " after on_leave RCONT XXX #{@current_component.focussed} #{@current_component.name}"
|
|
372
|
+
@current_component.repaint
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# is focus on first component FIXME check for focusable
|
|
376
|
+
def on_first_component?
|
|
377
|
+
@current_component == @focusables.first
|
|
378
|
+
end
|
|
379
|
+
# is focus on last component FIXME check for focusable
|
|
380
|
+
def on_last_component?
|
|
381
|
+
@current_component == @focusables.last
|
|
382
|
+
end
|
|
383
|
+
# set focus on given component
|
|
384
|
+
# Sometimes you have the handle to component, and you want to move focus to it
|
|
385
|
+
def goto_component comp
|
|
386
|
+
return if comp == @current_component
|
|
387
|
+
leave_current_component
|
|
388
|
+
@current_component = comp
|
|
389
|
+
set_form_row
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# ADD HERE ABOVe
|
|
393
|
+
end # class
|
|
394
|
+
end # module
|
|
395
|
+
|
|
396
|
+
if __FILE__ == $PROGRAM_NAME
|
|
397
|
+
require 'canis/core/util/app'
|
|
398
|
+
App.new do
|
|
399
|
+
f1 = field "name", :maxlen => 20, :width => 20, :bgcolor => :white,
|
|
400
|
+
:color => :black, :text => "abc", :label => " Name: ", :label_color_pair => @datacolor
|
|
401
|
+
f2 = field "email", :width => 20, :bgcolor => :white,
|
|
402
|
+
:color => :blue, :text => "me@google.com", :label => "Email: ", :label_color_pair => @datacolor
|
|
403
|
+
f3 = radio :group => :grp, :text => "red", :value => "RED", :color => :red
|
|
404
|
+
f4 = radio :group => :grp, :text => "blue", :value => "BLUE", :color => :blue
|
|
405
|
+
f5 = radio :group => :grp, :text => "green", :value => "GREEN", :color => :green
|
|
406
|
+
stack :margin_top => 2, :margin => 2 do
|
|
407
|
+
r = container :row => 1, :col => 2, :width => 80, :height => 20, :title => "A container"
|
|
408
|
+
r.add(f1)
|
|
409
|
+
r.add(f2)
|
|
410
|
+
r.add(f3,f4,f5)
|
|
411
|
+
sl = status_line
|
|
412
|
+
end # stack
|
|
413
|
+
|
|
414
|
+
end # app
|
|
415
|
+
end # if
|