rbcurse-experimental 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,69 @@
1
+ require 'rbcurse/core/widgets/rtree'
2
+ #require 'forwardable'
3
+ # we can extend from Tree but lets just try forwarding
4
+ module RubyCurses
5
+ # this class shows a tree of directories. Pressing ENTER expands or collapses
6
+ # the node. Pressing ENTER selects a node.
7
+ # Should we give options for displaying filenames also ? TODO
8
+ class DirectoryTree < Tree
9
+ attr_reader :selected_path
10
+ #@t = tree :data => model, :height => ht, :border_attrib => borderattrib, :suppress_borders => true
11
+ def _directories wd
12
+ d = Dir.new(wd)
13
+ ent = d.entries.reject{|e| !File.directory? File.join(wd,e)}
14
+ ent.delete(".");ent.delete("..")
15
+ return ent
16
+ end
17
+
18
+ def init_vars
19
+ super
20
+
21
+ one_key_selection = false
22
+ bind :TREE_WILL_EXPAND_EVENT do |node|
23
+ will_expand_action node
24
+ end
25
+
26
+ bind :TREE_SELECTION_EVENT do |ev|
27
+ selection_event ev
28
+ end
29
+ end # init_v
30
+
31
+ # populate this node with child directories
32
+ # this gives user application a chance to override or extend this action
33
+ def will_expand_action node
34
+ path = File.join(*node.user_object_path)
35
+ dirs = _directories path
36
+ ch = node.children
37
+ # add only children that may not be there
38
+ ch.each do |e|
39
+ o = e.user_object
40
+ if dirs.include? o
41
+ dirs.delete o
42
+ else
43
+ # delete this child since its no longer present TODO
44
+ end
45
+ end
46
+ node.add dirs
47
+ path_expanded path
48
+ end
49
+ # notify applications of path expanded so they may do any
50
+ # related action
51
+ # # NOTE: this is not the cleanest way, since you will need objects from your app
52
+ # scope here. User will have to add objects into the config hash after object creation
53
+ # and access them here. (See appdirtree.rb in examples)
54
+ def path_expanded path
55
+ end
56
+ def selection_event ev
57
+ if ev.state == :SELECTED
58
+ node = ev.node
59
+ path = File.join(*node.user_object_path)
60
+ @selected_path = path
61
+ selected_path_changed path
62
+ end
63
+ end
64
+ # inform applications that path has changed
65
+ # gives the user application a place to effect changes elsewhere in app
66
+ def selected_path_changed path
67
+ end
68
+ end # class
69
+ end
@@ -0,0 +1,166 @@
1
+ require 'rbcurse/extras/widgets/rvimsplit'
2
+ require 'forwardable'
3
+ # A convenience class that implements a 3 way Master Detail like form
4
+ # as in some email clients. See appemail.rb for usage.
5
+ # You may use this class or extend it. It takes care of expanding,
6
+ # increasing etc the 3 splits.
7
+ # This class is not fully tested beyond appemail.rb, and can change
8
+ # quite a bit. Users may want to copy this to prevent from major changes
9
+ # that could take place.
10
+ class MasterDetail < Widget
11
+ dsl_property :weight
12
+ attr_reader :vim # the vimsplit for any further configuration such as min_weight etc
13
+ extend Forwardable
14
+ def_delegators :@vim, :on_enter, :on_leave, :handle_key, :current_component
15
+ def initialize form, config={}, &block
16
+ @focusable = true
17
+ @height = Ncurses.LINES-2
18
+ @weight = 0.25
19
+ super
20
+ _create_vimsplit
21
+ init_vars
22
+ end
23
+ def init_vars #:nodoc:
24
+ @first_time = true
25
+ @repaint_required = true
26
+ end
27
+ def repaint
28
+ if @first_time
29
+ @first_time = nil
30
+ [@vim, @left, @right1, @right2].each { |e|
31
+ e.set_buffering(:target_window => @target_window || @form.window, :form => @form) # removed on 2011-09-29
32
+ }
33
+ end
34
+ @vim.repaint
35
+ end
36
+ # set the single component on the left side/pane, typically a +Listbox+.
37
+ # If an array is passed, the Listbox created is returned for further
38
+ # manipulation.
39
+ # @param [Widget] component to set on left
40
+ # @return [Widget] component added
41
+ def set_left_component comp, weight=nil
42
+ @left = @vim.add comp, :FIRST, weight
43
+ _add_component comp
44
+ @left
45
+ end
46
+ # set the first component on the right side/pane, typically a +Listbox+.
47
+ # @param [Widget] component to set on right
48
+ # @return [Widget] component added
49
+ def set_right_top_component comp, weight=0.5
50
+ @added_top = true
51
+ @right1 = @vim.add comp, :SECOND, weight
52
+ _add_component comp
53
+ @right1
54
+ end
55
+ # set the second component on the right side/pane, typically a
56
+ # +TextView+
57
+ # @param [Widget] component to set on right
58
+ # @return [Widget] component added
59
+ def set_right_bottom_component comp, weight=nil
60
+ raise "Please add top component first!" unless @added_top
61
+ # what if user gives in wrong order !!
62
+ @gb = @vim.add :divider, :SECOND, 0
63
+ @right2 = @vim.add comp, :SECOND, weight
64
+ @gb.next_component(@right2)
65
+ _add_component comp
66
+ @right2
67
+ end
68
+ def focus comp
69
+ case comp
70
+ when :left
71
+ @vim.goto_component @left
72
+ when :top_right
73
+ @vim.goto_component @right1
74
+ when :bottom_right
75
+ @vim.goto_component @right2
76
+ else
77
+ @vim.goto_component comp
78
+ end
79
+ end
80
+ private
81
+ # does nothing at present
82
+ def _add_component comp #:nodoc:
83
+ end
84
+ # creates a Vimplit containing 3 panes. Sets events in order to
85
+ # increase and decrease panes/windows.
86
+ def _create_vimsplit #:nodoc:
87
+ @vim = VimSplit.new nil, :row => @row, :col => @col, :width => @width, :height => @height, :weight => @weight, :orientation => :VERTICAL, :suppress_borders => true do |s|
88
+ s.parent_component = self
89
+ #s.target_window = @form.window
90
+ #s.add @left, :FIRST
91
+ #s.add @right1, :SECOND
92
+ #s.add @right2, :SECOND
93
+ s.bind :COMPONENT_RESIZE_EVENT do |e|
94
+ alert "masterdetail got a resize event #{e.type}, #{e.source} "
95
+ case e.type
96
+ when :INCREASE
97
+ case e.source
98
+ when @right2
99
+ increase_body
100
+ when @right1
101
+ increase_headers
102
+ when @left
103
+ alert "masterdetail left increase - not handled"
104
+
105
+ end
106
+ when :DECREASE
107
+ case e.source
108
+ when @right2
109
+ increase_headers
110
+ when @right1
111
+ increase_body
112
+ when @left
113
+ @left.width -= 1
114
+ @right2.col -=1
115
+ @right1.col -=1
116
+ @right1.width +=1
117
+ @right2.width +=1
118
+ @right2.repaint_required true
119
+ @right1.repaint_required true
120
+ @left.repaint_required true
121
+ alert "masterdetail left decrease"
122
+ end
123
+ when :EXPAND
124
+ case e.source
125
+ when @right2
126
+ h = 3
127
+ @right2.row(@right1.row + h)
128
+ oldh = @right1.height
129
+ @right1.height = h
130
+ @right1.current_index = 0
131
+ @right2.height += (oldh - h)
132
+ @right2.repaint_required true
133
+ @right1.repaint_required true
134
+ when @right1
135
+ h = 3
136
+ @right2.row(@right2.row + (@right2.height - 3))
137
+ oldh = @right2.height
138
+ @right2.height = h
139
+ #@right1.current_index = 0
140
+ @right1.height += (oldh - h)
141
+ @right2.repaint_required true
142
+ @right1.repaint_required true
143
+ end
144
+ end
145
+ end # bind
146
+ end
147
+ end # def
148
+ # increase the top right pane and reduces lower one
149
+ # TODO: to take into account multiplier
150
+ def increase_headers #:nodoc:
151
+ @right2.row @right2.row()+1
152
+ @right1.height +=1
153
+ @right2.height -=1
154
+ @right2.repaint_required true
155
+ @right1.repaint_required true
156
+ end
157
+ # decrease the top right pane and increase lower one
158
+ # TODO: to take into account multiplier
159
+ def increase_body #:nodoc:
160
+ @right2.row @right2.row()-1
161
+ @right1.height -=1
162
+ @right2.height +=1
163
+ @right2.repaint_required true
164
+ @right1.repaint_required true
165
+ end
166
+ end # class
@@ -0,0 +1,330 @@
1
+ =begin
2
+ * Name: Multiform.rb
3
+ * Description View (cycle) multiple forms
4
+ * Author: rkumar (arunachalesha)
5
+ * file created 2011-10-17 8:18 PM
6
+ --------
7
+ * License:
8
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
9
+
10
+ =end
11
+ require 'rbcurse'
12
+
13
+ include RubyCurses
14
+ module RubyCurses
15
+ extend self
16
+
17
+ # Trying to implement TabbedPanes using a multiform approach, but no more windows
18
+ # or pads. Try to keep it as simple as possible
19
+ class MultiForm < Widget
20
+ dsl_accessor :title
21
+
22
+
23
+ def initialize form = nil, config={}, &block
24
+ @focusable = true
25
+ @window = form.window
26
+ @row_offset = @col_offset = 1
27
+ @bmanager = BufferManager.new self
28
+ @forms = []
29
+ super
30
+ init_vars
31
+
32
+ end
33
+ def init_vars
34
+ super
35
+ # the following allows us to navigate buffers with :bn :bp etc (with Alt pressed)
36
+ bind_key(?\M-:, :buffer_menu)
37
+ bind_key(?\M-;, :buffer_menu)
38
+ # bind_key([?\C-x, ?f], :file_edit)
39
+ bind_key([?\C-x, ?k], :delete_component)
40
+ bind_key([?\C-x, ?\C-b], :list_components)
41
+ bind_key(?\M-n, :goto_next_component)
42
+ bind_key(?\M-p, :goto_prev_component)
43
+ bind_key(?\M-1, :goto_first_component)
44
+ # easily cycle using p. n is used for next search.
45
+ #bind_key(?p, :buffer_previous)
46
+ @suppress_borders = false
47
+ @repaint_all = true
48
+ @name ||= "multiform"
49
+ end
50
+ ## returns current buffer
51
+ # @return [RBuffer] current buffer
52
+ def current_component
53
+ @bmanager.current
54
+ end
55
+ ##
56
+ # multi-container
57
+ def handle_key ch #:nodoc:
58
+ @current_component.repaint
59
+ @current_component.window.wrefresh
60
+ $log.debug " MULTI handle_key #{ch}, #{@current_component}"
61
+ ret = :UNHANDLED
62
+ return :UNHANDLED unless @current_component
63
+ ret = @current_component.handle_key(ch)
64
+ $log.debug " MULTI comp #{@current_component} returned #{ret} "
65
+ if ret == :UNHANDLED
66
+ # check for bindings, these cannot override above keys since placed at end
67
+ begin
68
+ ret = process_key ch, self
69
+ $log.debug " MULTI = process_key returned #{ret} "
70
+ if ch > 177 && ch < 187
71
+ n = ch - 177
72
+ component_at(n)
73
+ # go to component n
74
+ end
75
+ rescue => err
76
+ # $error_message = err # changed 2010 dts
77
+ $error_message.value = err.to_s
78
+ #@form.window.print_error_message PLEASE CREATE LABEL
79
+ $log.error " Multicomponent process_key #{err} "
80
+ $log.debug(err.backtrace.join("\n"))
81
+ alert err.to_s
82
+ end
83
+ return :UNHANDLED if ret == :UNHANDLED
84
+ end
85
+ @current_component.repaint
86
+ @current_component.window.wrefresh
87
+ FFI::NCurses.update_panels
88
+
89
+ # check for any keys not handled and check our own ones
90
+ return ret #
91
+ end
92
+ def repaint
93
+ print_border if (@suppress_borders == false && @repaint_all) # do this once only, unless everything changes
94
+ return unless @current_component
95
+ $log.debug " MULTIFORM REPAINT "
96
+ ret = @current_component.repaint
97
+ end
98
+ def print_border #:nodoc:
99
+ $log.debug " #{@name} print_borders, #{@graphic.name} "
100
+ color = $datacolor
101
+ @graphic.print_border_only @row, @col, @height-1, @width, color #, Ncurses::A_REVERSE
102
+ print_title
103
+ end
104
+ def print_title #:nodoc:
105
+ $log.debug " print_title #{@row}, #{@col}, #{@width} #{@title} "
106
+ @graphic.printstring( @row, @col+(@width-@title.length)/2, @title, $datacolor, @title_attrib) unless @title.nil?
107
+ end
108
+ # this is just a test of the simple "most" menu
109
+ # can use this for next, prev, first, last, new, delete, overwrite etc
110
+ def buffer_menu
111
+ menu = PromptMenu.new self
112
+ menu.add(menu.create_mitem( 'l', "list buffers", "list buffers ", :list_components ))
113
+ item = menu.create_mitem( 'b', "Buffer Options", "Buffer Options" )
114
+ menu1 = PromptMenu.new( self, "Buffer Options")
115
+ menu1.add(menu1.create_mitem( 'n', "Next", "Switched to next buffer", :goto_next_component ))
116
+ menu1.add(menu1.create_mitem( 'p', "Prev", "Switched to previous buffer", :goto_prev_component ))
117
+ menu1.add(menu1.create_mitem( 'f', "First", "Switched to first buffer", :goto_first_component ))
118
+ menu1.add(menu1.create_mitem( 'l', "Last", "Switched to last buffer", :goto_last_component ))
119
+ menu1.add(menu1.create_mitem( 'd', "Delete", "Deleted buffer", :delete_component ))
120
+ item.action = menu1
121
+ menu.add(item)
122
+ # how do i know what's available. the application or window should know where to place
123
+ menu.display @form.window, $error_message_row, $error_message_col, $datacolor #, menu
124
+ end
125
+
126
+
127
+ def goto_next_component
128
+ perror "No other buffer" and return if @bmanager.size < 2
129
+
130
+ @current_component = @bmanager.next
131
+ set_current_component
132
+ # set_form_row and shit
133
+ end
134
+
135
+ def goto_prev_component
136
+ perror "No other buffer" and return if @bmanager.size < 2
137
+
138
+ @current_component = @bmanager.previous
139
+ $log.debug " buffer_prev got #{@current_component} "
140
+ set_current_component
141
+ end
142
+ def goto_first_component
143
+ @current_component = @bmanager.first
144
+ $log.debug " buffer_first got #{@current_component} "
145
+ set_current_component
146
+ end
147
+ def goto_last_component
148
+ @current_component = @bmanager.last
149
+ $log.debug " buffer_last got #{@current_component} "
150
+ set_current_component
151
+ end
152
+ def delete_component
153
+ if @bmanager.size > 1
154
+ @bmanager.delete_at
155
+ @current_component = @bmanager.previous
156
+ set_current_component
157
+ else
158
+ perror "Only one buffer. Cannot delete."
159
+ end
160
+ end
161
+
162
+ def component_at index
163
+ cc = @bmanager.element_at index
164
+ return unless cc
165
+ @current_component = cc
166
+ #$log.debug " buffer_last got #{@current_component} "
167
+ set_current_component
168
+ end
169
+ def add_form title, config={}, &blk
170
+ $log.debug "XXX: add_form window coords were hwtl #{@height} , #{@width} , #{@top} , #{@left} "
171
+ #w = VER::Window.new 16, 30, 2, 2 #@height-1, @width-1, @top, @left
172
+ w = VER::Window.new @height-2, @width-2, @row+1, @col+1
173
+ #w.box(0,0)
174
+ frm = Form.new w
175
+ @forms << frm
176
+ frm.parent_form = @form
177
+ frm.add_cols=@col+@col_offset
178
+ frm.add_rows=@row+@row_offset
179
+ @current_component = @bmanager.add frm, title
180
+ set_current_component
181
+ yield frm if block_given?
182
+ return @current_component # not a form but a Rcomponent containing component and title
183
+ end
184
+ def _add_to(index, component)
185
+ raise ArgumentError, "index out of bounds" unless @forms[index]
186
+ component.set_form(@forms[index])
187
+ $log.debug "XXX: comp r c #{component.row} #{component.col} "
188
+ component.row += @row
189
+ component.col += @col
190
+ $log.debug "XXX: after comp r c #{component.row} #{component.col} "
191
+ end
192
+ ##
193
+ # Add a component with a title
194
+ # @param [Widget] component
195
+ # @param [String] title
196
+ def add component, title
197
+ component.row = @row+@row_offset+0 # FFI changed 1 to 0 2011-09-12
198
+ component.col = @col+@col_offset+0 # FFI changed 1 to 0 2011-09-12
199
+ component.width = @width-2
200
+ component.height = @height-2
201
+ component.form = @form
202
+ component.override_graphic(@graphic)
203
+ @current_component = @bmanager.add component, title
204
+ set_current_component
205
+ set_form_row ## FFI added 2011-09-12 to get cursor at start when adding
206
+ $log.debug " ADD got cb : #{@current_component} "
207
+ end
208
+ def set_current_component
209
+ @title = @current_component.title
210
+ @current_component = @current_component.component
211
+ @current_component.repaint #2011-10-17 need to set all comps to repaint XXX
212
+ @current_component.window.wrefresh
213
+ @current_component.window.show
214
+ FFI::NCurses.update_panels
215
+ set_form_row
216
+ #@current_title = @current_component.title
217
+ #@current_component.repaint_all true 2011-10-17 need to set all comps to repaint XXX
218
+ end
219
+ def perror errmess
220
+ alert errmess
221
+ #@form.window.print_error_message errmess
222
+ end
223
+ def list_components
224
+ $log.debug " TODO buffers_list: #{@bmanager.size} "
225
+ menu = PromptMenu.new self
226
+ @bmanager.each_with_index{ |b, ix|
227
+ aproc = Proc.new { component_at(ix) }
228
+ name = b.title
229
+ num = ix + 1
230
+ menu.add(menu.create_mitem( num.to_s, name, "Switched to buffer #{ix}", aproc ))
231
+ }
232
+ menu.display @form.window, $error_message_row, $error_message_col, $datacolor
233
+ end
234
+ # required otherwise some components may not get correct cursor position on entry
235
+ # e.g. table
236
+ def on_enter
237
+ set_form_row # 2011-10-17
238
+ end
239
+ # nothing happening, the cursor goes to 1,1 and does not move
240
+ # alhough the fields can be edited XXX
241
+ def set_form_row #:nodoc:
242
+ if !@current_component.nil?
243
+ #$log.debug " #{@name} set_form_row calling sfr for #{@current_component.name} "
244
+ #fc = @current_component.widgets[0]
245
+ fc = @current_component.get_current_field
246
+ fc ||= @current_component.widgets[0]
247
+ fc.set_form_row if fc
248
+ #fc.set_form_col
249
+ end
250
+ end
251
+ # ADD HERE
252
+ end # class multicontainer
253
+ ##
254
+ # Handles multiple buffers, navigation, maintenance etc
255
+ # Instantiated at startup of
256
+ #
257
+ class BufferManager
258
+ include Enumerable
259
+ def initialize source
260
+ @source = source
261
+ @buffers = [] # contains RBuffer
262
+ @counter = 0
263
+ # for each buffer i need to store data, current_index (row), curpos (col offset) and title (filename).
264
+ end
265
+ def element_at index
266
+ @buffers[index]
267
+ end
268
+ def each
269
+ @buffers.each {|k| yield(k)}
270
+ end
271
+
272
+ ##
273
+ # @return [RBuffer] current buffer/file
274
+ ##
275
+ def current
276
+ @buffers[@counter]
277
+ end
278
+ ##
279
+ # Would have liked to just return next buffer and not get lost in details of caller
280
+ #
281
+ # @return [RBuffer] next buffer/file
282
+ ##
283
+ def next
284
+ @counter += 1
285
+ @counter = 0 if @counter >= @buffers.size
286
+ @buffers[@counter]
287
+ end
288
+ ##
289
+ # @return [RBuffer] previous buffer/file
290
+ ##
291
+ def previous
292
+ $log.debug " previous bs: #{@buffers.size}, #{@counter} "
293
+ @counter -= 1
294
+ return last() if @counter < 0
295
+ $log.debug " previous ctr #{@counter} "
296
+ @buffers[@counter]
297
+ end
298
+ def first
299
+ @counter = 0
300
+ @buffers[@counter]
301
+ end
302
+ def last
303
+ @counter = @buffers.size - 1
304
+ @buffers[@counter]
305
+ end
306
+ ##
307
+ def delete_at index=@counter
308
+ @buffers.delete_at index
309
+ end
310
+ def delete_by_name name
311
+ @buffers.delete_if {|b| b.filename == name }
312
+ end
313
+ def insert component, position, title=nil
314
+ anew = RComponents.new(component, title)
315
+ @buffers.insert position, anew
316
+ @counter = position
317
+ return anew
318
+ end
319
+ def add component, title=nil
320
+ #$log.debug " ADD H: #{component.height} C: #{component.width} "
321
+ insert component, @buffers.size, title
322
+ end
323
+ def size
324
+ @buffers.size
325
+ end
326
+ alias :count :size
327
+ end
328
+ RComponents = Struct.new(:component, :title)
329
+
330
+ end # modul