canis 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|