rbcurse-experimental 0.0.0

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