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,281 @@
1
+ require 'rbcurse/experimental/widgets/rscrollform'
2
+ require 'fileutils'
3
+
4
+ # See tabbed pane, and add_cols nad add_rows, on_entr and set_form_row to see cursor issue
5
+ # remove main form
6
+ # don't make a widget just an object
7
+ # let it create own form.
8
+
9
+ # NOTE: experimental, not yet firmed up
10
+ # If you use in application, please copy to some application folder in case i change this.
11
+ # Can be used for print_help_page
12
+ # SUGGESTIONS WELCOME.
13
+ # @since 1.4.1
14
+ module RubyCurses
15
+
16
+ class ResultsetBrowser #< Widget
17
+ #include EventHandler
18
+ include ConfigSetup
19
+ include RubyCurses::Utils
20
+ #dsl_property :xxx
21
+ # I don't think width is actually used, windows width matters. What of height ?
22
+ # THATS WRONG , i cannot eat up window. I should use object dimentsion for pad,
23
+ # not window dimensions
24
+ dsl_accessor :row, :col, :height, :width
25
+ dsl_accessor :should_print_border
26
+
27
+ def initialize win, config={}, &block
28
+ @should_print_border = true
29
+ @v_window = win #form.window
30
+ @window = @v_window
31
+ @focusable = true
32
+ @editable = true
33
+ @old_index = @current_index = 0
34
+ @fields = nil
35
+ config_setup config
36
+ instance_eval &block if block_given?
37
+ init_vars
38
+ end
39
+ def map_keys
40
+ @v_form.bind_key(?\C-n) { @current_index += 1 if @current_index < @rows.count-1 }
41
+ @v_form.bind_key(?\C-p) { @current_index -= 1 if @current_index > 0 }
42
+ @mapped = true
43
+ end
44
+ def init_vars
45
+ @row ||= 0
46
+ @col ||= 0
47
+ @height ||= 15
48
+ @width ||= 50
49
+ @field_offset = 14 # where actual Field should start (leaving space for label)
50
+ @row_offset ||= 0
51
+ @col_offset ||= 1
52
+ @border_offset = 0
53
+ if @should_print_border
54
+ @border_offset = 1
55
+ end
56
+ @v_form = RubyCurses::ScrollForm.new @v_window
57
+ @v_form.display_h(@height-1-@border_offset*2) if @height
58
+ @v_form.display_w(@width-1-@border_offset*2) if @width
59
+ end
60
+ def data=(rows)
61
+ @rows = rows
62
+ end
63
+ def columns=(columns)
64
+ @columns = columns
65
+ h = @columns.count + 2
66
+ w = 150
67
+ #row = 1
68
+ #col = 1
69
+ row = @row + @border_offset
70
+ col = @col + @border_offset
71
+ @v_form.set_pad_dimensions(row, col, h, w)
72
+ @v_form.should_print_border(false) # this should use dimensions of object not window. # 2011-10-12 15:48:14
73
+ # currently I don't have space for any buttons or anything. The form takes all space of the window
74
+ # not of the object defined.
75
+ # I should be able to tell Scrollform to use only thismuch of window.
76
+ end
77
+ def set_form_row
78
+ f = @v_form.get_current_field
79
+ f.set_form_row
80
+ end
81
+ def handle_key ch
82
+ map_keys unless @mapped
83
+ $log.debug "XXX: RB HK got ch "
84
+ ret = @v_form.handle_key ch
85
+ #set_form_row
86
+ if ret == :UNHANDLED
87
+ @v_form.process_key ch, self
88
+ end
89
+ repaint
90
+ @v_window.wrefresh
91
+ end
92
+ def repaint
93
+ @fields ||= _create_fields
94
+ #alert "old #{@old_index} , #{@current_index} "
95
+ if @old_index != @current_index
96
+ #alert "index change"
97
+ row = @rows[@current_index]
98
+ @columns.each_with_index { |e, i|
99
+ value = row[i]
100
+ len = value.to_s.length
101
+ type=@rows[0].types[i]
102
+ if type == "TEXT"
103
+ value = value.gsub(/\n/," ") if value
104
+ end
105
+ f = @fields[i]
106
+ @fields[i].set_buffer(value)
107
+ if f.display_length < len && len < (@width - @field_offset)
108
+ @fields[i].display_length len
109
+ end
110
+ }
111
+ @v_form.repaint
112
+ @window.wrefresh
113
+ Ncurses::Panel.update_panels
114
+ @old_index = @current_index
115
+ end
116
+ end
117
+ # maybe not required since we don't have 2 forms now
118
+ def unused_on_enter
119
+ if $current_key == KEY_BTAB
120
+ c = @v_form.widgets.count-1
121
+ @v_form.select_field c
122
+ else
123
+ @v_form.select_field 0
124
+ end
125
+ end
126
+ private
127
+ def _create_fields
128
+ color = $datacolor
129
+ if @should_print_border
130
+ @v_window.print_border @row, @col, @height-1, @width, color #, Ncurses::A_REVERSE
131
+ @row_offset += 1
132
+ @col_offset += 1
133
+ end
134
+ $log.debug "XXX: ROWS#{@rows}"
135
+ $log.debug "XXX: COLS#{@columns}"
136
+ $log.debug "XXX: row#{@rows[@current_index]}"
137
+ fields = []
138
+ r = @row + @row_offset # row was for where to print the total object, not this
139
+ c = @col + @col_offset + @field_offset # 14 is to leave space for labels
140
+ v_form = @v_form
141
+ @columns.each_with_index { |e, index|
142
+ #break if index >= @height-1 # create only as much space we have, SUCKS but just trying till be scroll
143
+ #$log.debug "XXX: #{r} #{c} EACH #{e}, #{index}, #{@rows[@current_index][index]}"
144
+ value=@rows[@current_index][index]
145
+ type=@rows[0].types[index]
146
+ if type == "TEXT"
147
+ value = value.gsub(/\n/," ") if value
148
+ end
149
+ len = [value.to_s.length, (@width - @field_offset)].min
150
+ f = Field.new v_form do
151
+ name e
152
+ row r
153
+ col c
154
+ bgcolor 'blue'
155
+ highlight_background 'cyan'
156
+ set_buffer value
157
+ display_length len
158
+ set_label Label.new v_form, {'text' => e, 'color'=>'cyan'}
159
+ end
160
+ fields << f
161
+ r += 1
162
+ }
163
+ @v_form.repaint
164
+ @window.wrefresh
165
+ Ncurses::Panel.update_panels
166
+ #$log.debug "XXX: created fields "
167
+ return fields
168
+ end
169
+
170
+ # ADD HERE
171
+
172
+ end # class
173
+ end # module
174
+ module RubyCurses
175
+ # a data viewer for viewing some text or filecontents
176
+ # view filename, :close_key => KEY_RETURN
177
+ # send data in an array
178
+ # view Array, :close_key => KEY_RETURN, :layout => [0,0,23,80]
179
+ # when passing layout reserve 4 rows for window and border. So for 2 lines of text
180
+ # give 6 rows.
181
+ class Browser
182
+ def self.browse_sql dbname, tablename, sql, config={} #:yield: ???
183
+ raise "file not found" unless File.exist? dbname
184
+ require 'sqlite3'
185
+ db = SQLite3::Database.new(dbname)
186
+ columns, *rows = db.execute2(sql)
187
+ #$log.debug "XXX COLUMNS #{sql} "
188
+ content = rows
189
+ return nil if content.nil? or content[0].nil?
190
+ datatypes = content[0].types
191
+ self.browse db, tablename, columns, rows, config
192
+ end
193
+ # @param filename as string or content as array
194
+ # @yield textview object for further configuration before display
195
+ # NOTE: i am experimentally yielding textview object so i could supress borders
196
+ # just for kicks, but on can also bind_keys or events if one wanted.
197
+ #def self.view what, config={} #:yield: textview
198
+ def self.browse dbconn, tablename, columns, rows, config={} #:yield: ???
199
+ wt = 0 # top margin
200
+ wl = 0 # left margin
201
+ wh = Ncurses.LINES-wt-3 # height, goes to bottom of screen
202
+ ww = Ncurses.COLS-wl-3 # width, goes to right end
203
+ wt, wl, wh, ww = config[:layout] if config.has_key? :layout
204
+
205
+ fp = config[:title] || ""
206
+ pf = config.fetch(:print_footer, true)
207
+ ta = config.fetch(:title_attrib, 'bold')
208
+ fa = config.fetch(:footer_attrib, 'bold')
209
+
210
+ wh = Ncurses.LINES-0
211
+ ww = Ncurses.COLS-0
212
+ layout = { :height => wh, :width => ww, :top => wt, :left => wl }
213
+ #v_window = config[:window] # using previous window cause crash seg fault
214
+ #v_window ||= VER::Window.new(layout) # copywin gives -1 and prints nothing
215
+ v_window = VER::Window.root_window
216
+ v_window.printstring 0, 30, "Database Browser Demo", $datacolor
217
+ @form = RubyCurses::Form.new v_window # only for some widgets that are not editable
218
+ header = app_header "rbcurse ", :text_center => "ResultsetBrowser Demo", :text_right =>"New Improved!", :color => :black, :bgcolor => :white, :attr => :bold
219
+ sl = status_line :row => v_window.height == 0 ? Ncurses.LINES-1 : v.window.height-1
220
+ sl.command { "Record Navigation: C-n C-p. Scrolling M-n, M-p, M-l, M-h" }
221
+
222
+ @form.repaint
223
+
224
+ #rb = ResultsetBrowser.new v_form, :row => 2, :col => 2
225
+ rb = ResultsetBrowser.new v_window, :row => 2, :col => 2, :height => 20, :width => 95
226
+ rb.columns = columns
227
+ rb.data = rows
228
+ rb.repaint
229
+
230
+
231
+ # yielding textview so you may further configure or bind keys or events
232
+ begin
233
+ #v_form.repaint
234
+ v_window.wrefresh
235
+ Ncurses::Panel.update_panels
236
+ # allow closing using q and Ctrl-q in addition to any key specified
237
+ # user should not need to specify key, since that becomes inconsistent across usages
238
+ while((ch = v_window.getchar()) != ?\C-q.getbyte(0) )
239
+ break if ch == config[:close_key]
240
+ rb.handle_key ch
241
+ #v_form.handle_key ch
242
+ end
243
+ rescue => err
244
+ $log.error err.to_s
245
+ $log.error err.backtrace.join("\n")
246
+ alert err.to_s
247
+
248
+ ensure
249
+ v_window.destroy if !v_window.nil?
250
+ end
251
+ end
252
+ end # class
253
+ end # module
254
+ if __FILE__ == $PROGRAM_NAME
255
+ require 'rbcurse/core/util/app'
256
+
257
+ App.new do
258
+ header = app_header "rbcurse ", :text_center => "ResultsetBrowser Demo", :text_right =>"New Improved!", :color => :black, :bgcolor => :white, :attr => :bold
259
+ message "Press F10 to exit from here"
260
+ columns = ["Name","Age","City", "Country"]
261
+ data = [
262
+ [ "Rahul",31, "Delhi","India"],
263
+ [ "Dev",35, "Mumbai","India"],
264
+ [ "Jobs",56, "L.A","U.S.A"],
265
+ [ "Matz",40, "Tokyo","Nippon"]
266
+ ]
267
+
268
+ #RubyCurses::Browser.browse("dummy", "atable", columns, data, :close_key => FFI::NCurses::KEY_F10, :title => "Enter to close") do |t|
269
+ sql = "select id, type, priority, title from bugs"
270
+ sql = "select * from bugs"
271
+ RubyCurses::Browser.browse_sql("../../../bugzy.sqlite", "bugs", sql, :close_key => FFI::NCurses::KEY_F10, :title => "Enter to close", :window => @window) do |t|
272
+ # you may configure textview further here.
273
+ #t.suppress_borders true
274
+ #t.color = :black
275
+ #t.bgcolor = :white
276
+ # or
277
+ #t.attr = :reverse
278
+ end
279
+
280
+ end # app
281
+ end
@@ -0,0 +1,586 @@
1
+ =begin
2
+ * Name: ResultsetTextView
3
+ * Description View text in this widget.
4
+ * Author: rkumar (arunachalesha)
5
+ * file created 2009-01-08 15:23
6
+ * major change: 2010-02-10 19:43 simplifying the buffer stuff.
7
+ * major change: 2011-10-14 reducing repaint, calling only if scrolled
8
+ also, printing row focus and selection outside of repaint so only 2 rows affected.
9
+ --------
10
+ * License:
11
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
12
+
13
+ =end
14
+ require 'rbcurse/core/widgets/rtextview'
15
+ require 'rbcurse/core/util/bottomline' # for ask()
16
+
17
+ include RubyCurses
18
+ module RubyCurses
19
+ extend self
20
+
21
+ ##
22
+ # An extension of textview that allows viewing a resultset, one record
23
+ # at a time, paging records.
24
+
25
+ class ResultsetTextView < TextView
26
+
27
+ # The offset of the current record in the resultset, starting 0
28
+ attr_accessor :current_record
29
+
30
+ # not used, and may not be used since i would have to reserve on more column for this
31
+ #dsl_accessor :row_selected_symbol
32
+ # Should the row focussed on show a highlight or not, default is false
33
+ # By default, the cursor will be placed on focussed row.
34
+ dsl_accessor :should_show_focus
35
+ #
36
+ # Attribute of selected row 'reverse' 'bold'
37
+ # By default, it is reverse.
38
+ dsl_accessor :selected_attrib
39
+ # Attribute of focussed row 'reverse' 'bold'
40
+ # By default, it is not set.
41
+ dsl_accessor :focussed_attrib # attribute of focussed row 'bold' 'underline'
42
+ dsl_accessor :editing_allowed # can user edit values and store in database
43
+
44
+ def initialize form = nil, config={}, &block
45
+ @row_offset = @col_offset = 1
46
+ @row = 0
47
+ @col = 0
48
+ @should_show_focus = false # don;t show focus and unfocus by default
49
+ @list = [] # this is only the currently visible record
50
+ @rows = nil # this is the entire resultset
51
+ @old_record = @current_record = 0 # index of currently displayed record from resultset
52
+ @editing_allowed = true
53
+ super
54
+ @win = @graphic
55
+ @datatypes = nil; # to be set when we query data, an array one for each column
56
+
57
+ @widget_scrolled = true
58
+ @record_changed = false
59
+
60
+ @help_text = "C to edit a column, Navigation: M-lhjk,jk gg G, Space. Next Record M-period or period, Previous M-comma or comma. Last Record '>' First Record '<' <Enter> View sortable tabular view"
61
+ bind(:PRESS){ |eve|
62
+ s = eve.source
63
+ r = s.current_record
64
+ col = @columns[@current_index]
65
+ #alert "You clicked on #{r} , #{col} , #{eve.text} "
66
+ #edit_record
67
+ }
68
+ #@selected_attrib = 'standout'
69
+ #@focussed_attrib = 'underline'
70
+ end
71
+ def edit_record
72
+ unless @editing_allowed
73
+ say "You clicked on #{r} , #{col} , #{eve.text}. If editing_allowed was true you could have modified the db "
74
+ return
75
+ end
76
+ col = @columns[@current_index]
77
+ text = @rows[@current_record][@current_index]
78
+ value = ask("Edit #{col}: "){ |q| q.default = text }
79
+ if value && value != "" && value != text
80
+ @rows[@current_record][@current_index] = value
81
+ @widget_scrolled = true # force repaint of data
82
+ begin
83
+ sql_update @tablename, id=@rows[@current_record][0], col, value
84
+ say_with_wait "Update to database successful"
85
+ rescue => err
86
+ alert "UPDATE ERROR:#{err.to_s} "
87
+ end
88
+ else
89
+ say_with_pause "Editing aborted", :color_pair => $errorcolor
90
+ end
91
+ end
92
+ ##
93
+ # update a row from bugs based on id, giving one fieldname and value
94
+ # @param [Fixnum] id unique key
95
+ # @param [String] fieldname
96
+ # @param [String] value to update
97
+ # @example sql_update "bugs", 9, :name, "Roger"
98
+ # I need to know keyfields for 2 reasons , disallow update and use in update XXX
99
+ def sql_update table, id, field, value
100
+ # 2010-09-12 11:42 added to_s to now, due to change in sqlite3 1.3.x
101
+ alert "No database connection" unless @db
102
+ return unless @db
103
+ ret = @db.execute( "update #{table} set #{field} = ? where id = ?", [value, id])
104
+ $log.debug "SQLITE ret value : #{ret}, #{table} #{field} #{id} #{value} "
105
+ end
106
+
107
+ def repaint_all tf
108
+ super
109
+ @widget_scrolled = true
110
+ end
111
+ def next_record num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
112
+ @old_record = @current_record
113
+ @record_changed = true
114
+ num.times {
115
+ @current_record += 1 if @current_record < @rows.count-1;
116
+ }
117
+
118
+ @repaint_required = true
119
+ $multiplier = 0
120
+ end
121
+ def previous_record num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
122
+ @old_record = @current_record
123
+ @record_changed = true
124
+ num.times {
125
+ @current_record -= 1 if @current_record > 0 ;
126
+ }
127
+ @repaint_required = true
128
+ end
129
+ def last_record
130
+ @old_record = @current_record
131
+ @record_changed = true
132
+ @current_record = @rows.count-1; @repaint_required = true
133
+ end
134
+ def first_record
135
+ @old_record = @current_record
136
+ @record_changed = true
137
+ @current_record = 0; @repaint_required = true
138
+ end
139
+ def map_keys
140
+ super
141
+ bind_key([?\C-x, ?6], :scroll_backward)
142
+ bind_key([?\C-x, ?v], :scroll_forward)
143
+ bind_key([?\C-x, ?n], :next_record)
144
+ bind_key([?\C-x, ?p], :previous_record)
145
+ bind_keys([?\M-,,?,], :previous_record )
146
+ bind_keys([?\M-.,?.], :next_record)
147
+ bind_keys([?\M-<,?<], :first_record )
148
+ bind_keys([?\M->,?>], :last_record)
149
+ bind_key('C', :edit_record)
150
+ #bind_key([?\C-x, ?>], :scroll_right)
151
+ #bind_key([?\C-x, ?<], :scroll_left)
152
+ #bind_key([?\C-x, ?\C-s], :saveas)
153
+ #bind_key(?r) { getstr("Enter a word: ") }
154
+ #bind_key(?m, :disp_menu)
155
+ end
156
+ # connect to database, run sql and set data, columns and datatypes
157
+ # Similar can be done with another database
158
+ def sqlite dbname, table, sql
159
+ raise "App error: file not found: #{dbname} " unless File.exist? dbname
160
+ require 'sqlite3'
161
+ db = SQLite3::Database.new(dbname)
162
+ columns, *rows = db.execute2(sql)
163
+ @db = db # for update
164
+ @dbname = dbname
165
+ @tablename = table
166
+ $log.debug "XXX sql #{sql}, #{rows.count} "
167
+ content = rows
168
+ return nil if content.nil? or content[0].nil?
169
+ self.datatypes = content[0].types
170
+ set_content rows, columns
171
+ end
172
+ ##
173
+ # send in a dataset (array of arrays) and array of column names
174
+ # e.g. set_content File.open("README.txt","r").readlines
175
+ # set wrap at time of passing :WRAP_NONE :WRAP_WORD
176
+ # XXX if we widen the textview later, as in a vimsplit that data
177
+ # will still be wrapped at this width !!
178
+ def set_content list, columns
179
+ @rows = list
180
+ @columns = columns
181
+ @current_record = 0
182
+ init_vars
183
+ self
184
+ end
185
+ def data=(list)
186
+ @rows = list
187
+ end
188
+ def columns=(list)
189
+ @columns = list
190
+ end
191
+ # set array of datatypes, one per column
192
+ def datatypes=(list)
193
+ @datatypes = list
194
+ end
195
+ def remove_all
196
+ $log.warn "ResultsetTextView remove_all not yet tested XXX"
197
+ @list = []
198
+ @rows = []
199
+ init_vars
200
+ @repaint_required = true
201
+ end
202
+
203
+ def repaint # textview :nodoc:
204
+ #$log.debug "TEXTVIEW repaint r c #{@row}, #{@col} "
205
+ $log.debug "RESULTSETTEXTVIEW repaint r c #{@row}, #{@col}, key: #{$current_key}, reqd #{@repaint_required} "
206
+
207
+ # TRYING OUT dangerous 2011-10-13
208
+ @repaint_required = false
209
+ @repaint_required = true if @widget_scrolled || @pcol != @old_pcol || @record_changed
210
+
211
+
212
+ paint if @repaint_required
213
+
214
+ print_foot if @print_footer && !@suppress_borders && @repaint_footer_required
215
+ end
216
+ def print_foot #:nodoc:
217
+ return unless @rows
218
+ @footer_attrib ||= Ncurses::A_DIM
219
+ gb = get_color($datacolor, 'green','black')
220
+ footer = "%15s" % " [#{@current_record+1}/ #{@rows.length} ]"
221
+ $log.debug " print_foot calling printstring with #{@row} + #{@height} -1, #{@col}+2"
222
+ pos = @col + 2
223
+ right = true
224
+ if right
225
+ pos = @col + @width - footer.length - 1
226
+ end
227
+ @graphic.printstring( @row + @height -1 , pos, footer, gb, @footer_attrib)
228
+ @repaint_footer_required = false # 2010-01-23 22:55
229
+ end
230
+ def getvalue
231
+ @list
232
+ end
233
+ # not sure what to return, returning data value
234
+ def current_value
235
+ return nil if @list.nil? || @rows.nil?
236
+ #@list[@current_record][@current_index]
237
+ @rows[@current_record][@current_index]
238
+ end
239
+ def fire_action_event
240
+ if @current_index == @selected_index
241
+ @old_selected_index = @current_index
242
+ unhighlight_row @current_index
243
+ color_field @current_index
244
+ @selected_index = nil
245
+ return
246
+ end
247
+ unhighlight_row @selected_index
248
+ color_field @selected_index
249
+ @selected_index = @current_index
250
+ highlight_selected_row
251
+ @old_selected_index = @selected_index
252
+ @repaint_required = true
253
+ super
254
+ tabular
255
+ end
256
+ def handle_key ch #:nodoc:
257
+ return :UNHANDLED if @rows.nil?
258
+ super
259
+ end
260
+ def tabular
261
+ require 'rbcurse/core/widgets/tabularwidget'
262
+ w = Ncurses.COLS
263
+ h = Ncurses.LINES-1
264
+ v_window = VER::Window.new(h,w,0,0)
265
+ v_form = RubyCurses::Form.new v_window
266
+ tabula = TabularWidget.new v_form, :width => w, :height => h-1, :print_footer => true
267
+ begin
268
+ tabula.set_content @rows, @columns
269
+ yield tabula if block_given?
270
+ v_form.repaint
271
+ v_window.wrefresh
272
+ Ncurses::Panel.update_panels
273
+ # allow closing using q and Ctrl-q in addition to any key specified
274
+ # user should not need to specify key, since that becomes inconsistent across usages
275
+ while((ch = v_window.getchar()) != ?\C-q.getbyte(0) )
276
+ break if ch == config[:close_key] || ch == ?q.ord
277
+ # if you've asked for RETURN then i also check for 10 and 13
278
+ break if (ch == 10 || ch == 13) && config[:close_key] == KEY_RETURN
279
+ v_form.handle_key ch
280
+ #v_form.repaint
281
+ v_window.wrefresh
282
+ end
283
+ rescue => err
284
+ $log.error " Tabularwidget Resultsetview ERROR #{err} "
285
+ $log.error(err.backtrace.join("\n"))
286
+ alert err.to_s
287
+
288
+ ensure
289
+ v_window.destroy if !v_window.nil?
290
+ end
291
+ end
292
+ # newly added to check curpos when moving up or down
293
+ # set cursor on correct column tview
294
+ def set_form_col col1=@curpos #:nodoc:
295
+ @cols_panned ||= 0
296
+ @pad_offset ||= 0 # added 2010-02-11 21:54 since padded widgets get an offset.
297
+ @curpos = col1
298
+ maxlen = @maxlen || @width-@internal_width
299
+ #@curpos = maxlen if @curpos > maxlen
300
+ if @curpos > maxlen
301
+ @pcol = @curpos - maxlen
302
+ @curpos = maxlen - 1
303
+ @repaint_required = true # this is required so C-e can pan screen
304
+ else
305
+ @pcol = 0
306
+ end
307
+ # the rest only determines cursor placement
308
+ win_col = 0 # 2010-02-07 23:19 new cursor stuff
309
+ col2 = win_col + @col + @col_offset + @curpos + @cols_panned + @pad_offset
310
+ $log.debug "TV SFC #{@name} setting c to #{col2} #{win_col} #{@col} #{@col_offset} #{@curpos} "
311
+ #@form.setrowcol @form.row, col
312
+ setrowcol nil, col2
313
+ @repaint_footer_required = true
314
+ end
315
+ #
316
+ # prepares row data for paint to print
317
+ # Creates a string for each row, which is great for textview operation, all of them
318
+ # work just fine. But does not allow paint to know what part is title and what is
319
+ # data
320
+ #
321
+ def get_content
322
+ return nil unless @rows
323
+ id = @current_record
324
+
325
+ row = @rows[id]
326
+ @lens = []
327
+ a = []
328
+ f = "%14s %-*s"
329
+ #f = "%14s %-20s"
330
+ @columns.each_with_index { |e, i|
331
+ value = row[i]
332
+ len = value.to_s.length
333
+ type = @datatypes[i]
334
+ if type == "TEXT"
335
+ value = value.gsub(/\n/," ") if value
336
+ end
337
+ @lens << len
338
+ a << f % [e, len, value]
339
+ }
340
+ @list = a # this keeps it compatible with textview operations.
341
+ return a
342
+ end
343
+
344
+ ## NOTE: earlier print_border was called only once in constructor, but when
345
+ ##+ a window is resized, and destroyed, then this was never called again, so the
346
+ ##+ border would not be seen in splitpane unless the width coincided exactly with
347
+ ##+ what is calculated in divider_location.
348
+ def paint #:nodoc:
349
+
350
+ $log.debug "XXX TEXTVIEW PAINT HAPPENING #{@current_index} "
351
+ #@left_margin ||= @row_selected_symbol.length
352
+ @left_margin = 0
353
+ @fieldbgcolor ||= get_color($datacolor,@bgcolor, 'cyan')
354
+ my_win = nil
355
+ if @form
356
+ my_win = @form.window
357
+ else
358
+ my_win = @target_window
359
+ end
360
+ @graphic = my_win unless @graphic
361
+ @win_left = my_win.left
362
+ @win_top = my_win.top
363
+
364
+ print_borders if (@suppress_borders == false && @repaint_all) # do this once only, unless everything changes
365
+ maxlen = @maxlen || @width-@internal_width
366
+ #$log.debug " #{@name} textview repaint width is #{@width}, height is #{@height} , maxlen #{maxlen}/ #{@maxlen}, #{@graphic.name} roff #{@row_offset} coff #{@col_offset}"
367
+ tm = get_content
368
+ return unless tm # no data
369
+ rc = tm.size # row_count
370
+ tr = @toprow
371
+ acolor = get_color $datacolor
372
+ h = scrollatrow()
373
+ r,c = rowcol
374
+ @longest_line = @width-@internal_width #maxlen
375
+ $log.debug "XXX: SELECTED ROW IS #{@selected_index} "
376
+ 0.upto(h) do |hh|
377
+ crow = tr+hh
378
+ if crow < rc
379
+ focussed = @current_index == crow # row focussed ?
380
+ selected = is_row_selected crow
381
+ content = tm[crow]
382
+ content = content.dup
383
+ sanitize content if @sanitization_required
384
+ truncate content
385
+
386
+ @graphic.printstring r+hh, c+@left_margin, "%-*s" % [@width-@internal_width,content], acolor, @attr
387
+
388
+ if selected
389
+ #print_selected_row r+hh, c+@left_margin, content, acolor
390
+ highlight_selected_row r+hh, c
391
+ #@graphic.printstring r+hh, c+@left_margin, "%-*s" % [@width-@internal_width,content], acolor, @focussed_attrib || 'reverse'
392
+ elsif focussed
393
+ # i am keeping this here just since sometimes repaint gets called
394
+ highlight_focussed_row :FOCUSSED, r+hh, c
395
+ end
396
+ #print_focussed_row :FOCUSSED, nil, nil, content, acolor
397
+ #@graphic.printstring r+hh, c+@left_margin, "%-*s" % [@width-@internal_width,content], acolor, @focussed_attrib || 'bold'
398
+
399
+ color_field crow
400
+ =begin
401
+ # paint field portion separately, take care of when panned
402
+ # hl only field length, not whole thing.
403
+ startpoint = [c+14+1-@pcol,c].max # don't let it go < 0
404
+ clen = @lens[crow]
405
+ # take into account when we've scrolled off right
406
+ clen -= @pcol-14-1 if 14+1-@pcol < 0
407
+ hlwidth = [clen,@width-@internal_width-14-1+@pcol, @width-@internal_width].min
408
+ hlwidth = 0 if hlwidth < 0
409
+
410
+ @graphic.mvchgat(y=r+hh, x=startpoint, hlwidth, Ncurses::A_NORMAL, @fieldbgcolor, nil)
411
+ #@graphic.mvchgat(y=r+hh, x=startpoint, hlwidth, Ncurses::A_BOLD, acolor, nil)
412
+ =end
413
+
414
+ # highlighting search results.
415
+ if @search_found_ix == tr+hh
416
+ if !@find_offset.nil?
417
+ # handle exceed bounds, and if scrolling
418
+ if @find_offset1 < maxlen+@pcol and @find_offset > @pcol
419
+ @graphic.mvchgat(y=r+hh, x=c+@find_offset-@pcol, @find_offset1-@find_offset, Ncurses::A_NORMAL, $reversecolor, nil)
420
+ end
421
+ end
422
+ end
423
+ else
424
+ # clear rows
425
+ @graphic.printstring r+hh, c, " " * (@width-@internal_width), acolor,@attr
426
+ end
427
+
428
+ end
429
+ @repaint_required = false
430
+ @repaint_footer_required = true
431
+ @repaint_all = false
432
+ # 2011-10-13
433
+ @widget_scrolled = false
434
+ @record_changed = false
435
+ @old_pcol = @pcol
436
+
437
+ end
438
+ def color_field index
439
+ return unless index
440
+ _r,c = rowcol
441
+ r = _convert_index_to_printable_row index
442
+ return unless r
443
+ # paint field portion separately, take care of when panned
444
+ # hl only field length, not whole thing.
445
+ startpoint = [c+14+1-@pcol,c].max # don't let it go < 0
446
+ clen = @lens[index]
447
+ # take into account when we've scrolled off right
448
+ clen -= @pcol-14-1 if 14+1-@pcol < 0
449
+ hlwidth = [clen,@width-@internal_width-14-1+@pcol, @width-@internal_width].min
450
+ hlwidth = 0 if hlwidth < 0
451
+
452
+ @graphic.mvchgat(y=r, x=startpoint, hlwidth, Ncurses::A_NORMAL, @fieldbgcolor, nil)
453
+ end
454
+ # the idea here is to be able to call externally or from loop
455
+ # However, for that content has to be truncated here, not in loop
456
+ def DELprint_focussed_row type, r=nil, c=nil, content=nil, acolor=nil
457
+ return unless @should_show_focus
458
+ case type
459
+ when :FOCUSSED
460
+ r = _convert_index_to_printable_row() unless r
461
+ attrib = @focussed_attrib || 'bold'
462
+ ix = @current_index
463
+
464
+ when :UNFOCUSSED
465
+ return if @oldrow.nil? || @oldrow == @current_index
466
+ r = _convert_index_to_printable_row(@oldrow) unless r
467
+ return unless r # row is not longer visible
468
+ ix = @oldrow
469
+ attrib = @attr
470
+ end
471
+ unless c
472
+ _r, c = rowcol
473
+ end
474
+ if content.nil?
475
+ content = @list[ix]
476
+ content = content.dup
477
+ sanitize content if @sanitization_required
478
+ truncate content
479
+ end
480
+ acolor ||= get_color $datacolor
481
+ #@graphic.printstring r+hh, c+@left_margin, "%-*s" % [@width-@internal_width,content], acolor, @focussed_attrib || 'bold'
482
+ @graphic.printstring r, c+@left_margin, "%-*s" % [@width-@internal_width, content], acolor, attrib
483
+ end
484
+
485
+ # this only highlights the selcted row, does not print data again
486
+ # so its safer and should be used instead of print_selected_row
487
+ def highlight_selected_row r=nil, c=nil, acolor=nil
488
+ return unless @selected_index # no selection
489
+ r = _convert_index_to_printable_row(@selected_index) unless r
490
+ return unless r # not on screen
491
+ unless c
492
+ _r, c = rowcol
493
+ end
494
+ acolor ||= get_color $datacolor
495
+ att = FFI::NCurses::A_REVERSE
496
+ att = get_attrib(@selected_attrib) if @selected_attrib
497
+ @graphic.mvchgat(y=r, x=c, @width-@internal_width, att , acolor , nil)
498
+ end
499
+ def highlight_focussed_row type, r=nil, c=nil, acolor=nil
500
+ return unless @should_show_focus
501
+ case type
502
+ when :FOCUSSED
503
+ r = _convert_index_to_printable_row() unless r
504
+ attrib = @focussed_attrib || 'bold'
505
+ ix = @current_index
506
+
507
+ when :UNFOCUSSED
508
+ return if @oldrow.nil? || @oldrow == @current_index
509
+ r = _convert_index_to_printable_row(@oldrow) unless r
510
+ return unless r # row is not longer visible
511
+ ix = @oldrow
512
+ attrib = @attr
513
+ end
514
+ unless c
515
+ _r, c = rowcol
516
+ end
517
+ acolor ||= get_color $datacolor
518
+ att = get_attrib(attrib) #if @focussed_attrib
519
+ @graphic.mvchgat(y=r, x=c, @width-@internal_width, att , acolor , nil)
520
+ #@graphic.printstring r, c+@left_margin, "%-*s" % [@width-@internal_width,content], acolor, @focussed_attrib || 'reverse'
521
+ end
522
+ def unhighlight_row index, r=nil, c=nil, acolor=nil
523
+ return unless index # no selection
524
+ r = _convert_index_to_printable_row(index) unless r
525
+ return unless r # not on screen
526
+ unless c
527
+ _r, c = rowcol
528
+ end
529
+ acolor ||= get_color $datacolor
530
+ att = FFI::NCurses::A_NORMAL
531
+ att = get_attrib(@normal_attrib) if @normal_attrib
532
+ @graphic.mvchgat(y=r, x=c, @width-@internal_width, att , acolor , nil)
533
+ end
534
+ def is_row_selected row
535
+ @selected_index == row
536
+ end
537
+ def on_enter_row arow
538
+ if @should_show_focus
539
+ highlight_focussed_row :FOCUSSED
540
+ unless @oldrow == @selected_index
541
+ highlight_focussed_row :UNFOCUSSED
542
+ color_field @oldrow
543
+ end
544
+ end
545
+ super
546
+ end
547
+ # no such method in superclass !!! XXX FIXME no such event too
548
+ def on_leave_row arow
549
+ #print_focussed_row :UNFOCUSSED
550
+ #print_normal_row
551
+ #super
552
+ end
553
+ # this is just a test of the simple "most" menu
554
+ # How can application add to this, or override
555
+ def disp_menu #:nodoc:
556
+ require 'rbcurse/extras/widgets/menutree'
557
+ # we need to put this into data-structure so that i can be manipulated by calling apps
558
+ # This should not be at the widget level, too many types of menus. It should be at the app
559
+ # level only if the user wants his app to use this kind of menu.
560
+
561
+ @menu = RubyCurses::MenuTree.new "Main", { s: :goto_start, r: :scroll_right, l: :scroll_left, m: :submenu }
562
+ @menu.submenu :m, "submenu", {s: :noignorecase, t: :goto_last_position, f: :next3 }
563
+ menu = PromptMenu.new self
564
+ menu.menu_tree @menu
565
+
566
+ =begin
567
+ menu = PromptMenu.new self
568
+ menu.add( menu.create_mitem( 's', "Goto start ", "Going to start", Proc.new { goto_start} ))
569
+ menu.add(menu.create_mitem( 'r', "scroll right", "I have scrolled ", :scroll_right ))
570
+ menu.add(menu.create_mitem( 'l', "scroll left", "I have scrolled ", :scroll_left ))
571
+ item = menu.create_mitem( 'm', "submenu", "submenu options" )
572
+ menu1 = PromptMenu.new( self, "Submenu Options")
573
+ menu1.add(menu1.create_mitem( 's', "CASE sensitive", "Ignoring Case in search" ))
574
+ menu1.add(menu1.create_mitem( 't', "goto last position", "moved to previous position", Proc.new { goto_last_position} ))
575
+ item.action = menu1
576
+ menu.add(item)
577
+ # how do i know what's available. the application or window should know where to place
578
+ #menu.display @form.window, 23, 1, $datacolor #, menu
579
+ =end
580
+ menu.display @form.window, $error_message_row, $error_message_col, $datacolor #, menu
581
+ end
582
+
583
+
584
+ end # class textview
585
+
586
+ end # modul