class Canis::TextPad
Attributes
rows is the actual number of rows the pad has which is slightly less than height (taking
into account borders. Same for cols and width.
dsl_accessor :list_footer # attempt at making a footer object
index of focussed row, starting 0, index into the data supplied
for external methods or classes to advance cursor
attr_accessor :curpos
the object that handles keys that are sent to this object by the form. This widget creates its own default handler if not overridden by user.
adding these only for debugging table, to see where cursor is.
adding these only for debugging table, to see where cursor is.
type of content in case parsing is required. Values :tmux, :ansi, :none trying to put content+type into document
attr_accessor :content_type
path of yaml file which contains conversion of style names to color, bgcolor and attrib
attr_accessor :stylesheet
rows is the actual number of rows the pad has which is slightly less than height (taking
into account borders. Same for cols and width.
a map of symbols and patterns, used currently in jumping to next or prev occurence of that
pattern. Default contains :word. Callers may add patterns, or modify existing ones and create key bindings for the same.
Public Class Methods
Canis::Widget::new
# File lib/canis/core/widgets/textpad.rb, line 81 def initialize form=nil, config={}, &block @editable = false @focusable = true @config = config @row = @col = 0 @prow = @pcol = 0 @startrow = 0 @startcol = 0 register_events( [:ENTER_ROW, :PRESS, :DIMENSION_CHANGED, :ROW_CHANGED]) @text_patterns = {} # FIXME we need to be compatible with vim's word @text_patterns[:word] = /[[:punct:][:space:]]\S/ # we need to be compatible with vim's WORD @text_patterns[:WORD] = /[[:space:]]\S/ super init_vars end
Public Instance Methods
—- Section search related end —– }}} —- dead unused {{{
some general methods for highlighting a row or changing attribute. However, these
will change the moment panning is done, or a repaint happens. If these should be maintained then they should be called from the repaint method
This was just indicative, and is not used anywhere
# File lib/canis/core/widgets/textpad.rb, line 1514 def DEADhighlight_row index = @current_index, cfg={} return unless index c = 0 # we are using pads so no col except for left_margin if present # in a pad we don't need to convert index to printable r = index defcolor = cfg[:defaultcolor] || $promptcolor acolor ||= get_color defcolor, cfg[:color], cfg[:bgcolor] att = FFI::NCurses::A_REVERSE att = get_attrib(cfg[:attrib]) if cfg[:attrib] #@graphic.mvchgat(y=r, x=c, @width-2, att , acolor , nil) FFI::NCurses.mvwchgat(@pad, y=r, x=c, @width-2, att, acolor, nil) end
old text {{{
# File lib/canis/core/widgets/textpad.rb, line 615 def ORIG_text(*val) if val.empty? return @list end lines = val[0] raise "Textpad: text() received nil" unless lines fmt = val.size == 2 ? val[1] : nil case fmt when Hash #raise "textpad.text expected content_type in Hash : #{fmt}" c = fmt[:content_type] t = fmt[:title] @title = t if t @content_type = c if c @stylesheet = fmt[:stylesheet] if fmt.key? :stylesheet $log.debug " TEXTPAD text() got #{@content_type} and #{@stylesheet} " fmt = c #raise "textpad.text expected content_type in Hash : #{fmt}" unless fmt when Symbol @content_type = fmt when NilClass else raise "textpad.text expected symbol or content_type in Hash, got #{fmt.class} " end ## some programs like testlistbox which uses multibuffers calls this with a config # in arg2 containing :content_type and :title # added so callers can have one interface and avoid an if condition #return formatted_text(lines, fmt) unless @content_type == :none # 2014-05-20 - 13:21 change and simplication of conversion process # We maintain original text in @list # but use another variable for native format (chunks). @parse_required = true @list = lines # 2014-06-15 - 13:39 i am commenting this off, since a user can change or set content_type # at any time, and also content handler. if @content_type # this will create a default content handler if not yet specified # which is wrong since user may specify it after setting text. he may not have # set the content type yet too. Too much dependent on order ! #preprocess_text #parse_formatted_text lines, :content_type => @content_type, :stylesheet => @stylesheet end return @list if lines.empty? #@list = lines #@native_text ||= @list @_populate_needed = true @repaint_all = true @repaint_required = true init_vars # i don't want the whole thing going into the event 2014-06-04 fire_property_change :text, "dummmy", "text has changed" self end
Find the next row that contains given string @return row and col offset of match, or nil FIXME: 2014-05-26 - 01:26 currently if there are two matches in a row skips the second
one, which is a pain if two matches only on same row. Ok, now it goes to second but won't come back to first, if only two matches, and both in one row.
@param String
to find
# File lib/canis/core/widgets/textpad.rb, line 1394 def ORIGnext_match str return unless str first = nil ## content can be string or Chunkline, so we had to write <tt>index</tt> for this. ## =~ does not give an error, but it does not work. @native_text.each_with_index do |line, ix| offset = 0 # next line just a hack and not correct if only one match in file FIXME offset = @curpos + 1 if ix == @current_index _col = line.index str, offset if _col first ||= [ ix, _col ] if ix > @current_index || ( ix == @current_index && _col > @curpos) return [ix, _col] end end end # if first is nil, then none found in current line also, so don't increment offset in current line # next time. FIXME TODO return first end
update the value at index with given value, returning self
# File lib/canis/core/widgets/textpad.rb, line 766 def []=(index, val) @list[index]=val fire_row_changed index self end
calculates the dimensions of the pad which will be used when the pad refreshes, taking into account whether borders are printed or not. This must be called whenever there is a change in height or width otherwise @rows will not be recalculated. Internal.
# File lib/canis/core/widgets/textpad.rb, line 122 def __calc_dimensions ## NOTE # --------------------------------------------------- # Since we are using pads, you need to get your height, width and rows correct # Make sure the height factors in the row, else nothing may show # --------------------------------------------------- raise " CALC inside #{@name} h or w is nil #{@height} , #{@width} " if @height.nil? or @width.nil? @rows = @height @cols = @width # NOTE XXX if cols is > COLS then padrefresh can fail @startrow = @row @startcol = @col unless @suppress_borders @row_offset = @col_offset = 1 @startrow += 1 @startcol += 1 @rows -=3 # 3 is since print_border_only reduces one from width, to check whether this is correct @cols -=3 @scrollatrows = @height - 3 else # no borders printed @rows -= 1 # 3 is since print_border_only reduces one from width, to check whether this is correct ## if next is 0 then padrefresh doesn't print, gives -1 sometimes. ## otoh, if we reduce 1, then there is a blank or white left at 128 since clear_pad clears 128 # but this only writes 127 2014-05-01 - 12:31 CLEAR_PAD #@cols -=0 @cols -=1 @scrollatrows = @height - 1 # check this out 0 or 1 @row_offset = @col_offset = 0 end @top = @row @left = @col @lastrow = @row + @row_offset @lastcol = @col + @col_offset $log.debug " CALC_DIMENSION r:#{@rows} , c:#{@cols}, h:#{@height} , w:#{@width} , top:#{@top} , left:#{@left} " end
# File lib/canis/core/widgets/textpad.rb, line 1283 def _do_borders unless @suppress_borders if @repaint_all ## XXX im not getting the background color. #@window.print_border_only @top, @left, @height-1, @width, $datacolor clr = get_color $datacolor, color(), bgcolor() #@window.print_border @top, @left, @height-1, @width, clr @window.print_border_only @top, @left, @height-1, @width, clr print_title # oldrow changed to oldindex 2014-04-13 - 16:55 @repaint_footer_required = true if @oldindex != @current_index print_foot if @print_footer && !@suppress_borders && @repaint_footer_required @window.wrefresh end end end
internal method to return the correct list. Rather than trying to synch list and native text for those who do not use the latter
let us just use the correct array
NOTE there are some cases where document can return a nil since native_text has not been
calculated yet. Happens in back button of help. Earlier preprocess was done from +text+ not it is only done from +repaint+
NOTE: native_text is currently Chunklines - chunks of text with information of color, whereas
list contains whatever the user set, which can include markup for coloring (ansi/tmux).
# File lib/canis/core/widgets/textpad.rb, line 693 def _getarray if @document.nil? return @list else return @document.native_text end end
this is a barebones handler to be used only if an overriding key handler wishes to fall back to default processing after it has handled some keys. The complete version is in Defaultkeyhandler. BUT the key will be executed again.
# File lib/canis/core/widgets/textpad.rb, line 1101 def _handle_key ch begin ret = process_key ch, self $multiplier = 0 bounds_check rescue => err $log.error " TEXTPAD ERROR _handle_key #{err} " $log.debug(err.backtrace.join("\n")) alert "#{err}" #textdialog ["Error in TextPad: #{err} ", *err.backtrace], :title => "Exception" ensure padrefresh Ncurses::Panel.update_panels end return 0 end
NOTE : 2014-04-09 - 14:05 i think this does not have line wise operations since we deal with formatting of data But what if data is not formatted. This imposes a severe limitation. listbox does have linewise operations, so lets try them append a row to the list
@deprecated pls use << or push as per Array semantics
# File lib/canis/core/widgets/textpad.rb, line 721 def append text raise "append: deprecated pls use << or push as per Array semantics" @list ||= [] @list.push text fire_dimension_changed :append self end
—- Section search related start —– {{{
Ask user for string to search for This uses the dialog, but what if user wants the old style. Isn't there a cleaner way to let user override style, or allow user to use own UI for getting pattern and then passing here. @param str default nil. If not passed, then user is prompted using get_string dialog
This allows caller to use own method to prompt for string such as 'get_line' or 'rbgetstr' / 'ask()'
# File lib/canis/core/widgets/textpad.rb, line 1357 def ask_search str=nil # the following is a change that enables callers to prompt for the string # using some other style, basically the classical style and send the string in str = get_string("Enter pattern: ", :title => "Find pattern") unless str return if str.nil? str = @last_regex if str == "" return if !str or str == "" str = Regexp.new str if str.is_a? String ix = next_match str return unless ix @last_regex = str #@oldindex = @current_index @current_index = ix[0] @curpos = ix[1] ensure_visible end
jump to previous occurence of given regexp within a line, and then to previous line
This is more line 'w' or 'b' in vim, not like '/'
@param [Regexp] pattern to go back to.
# File lib/canis/core/widgets/textpad.rb, line 977 def backward_regex regex if regex.is_a? Symbol regex = @text_patterns[regex] raise "Pattern specified #{regex} does not exist in text_patterns " unless regex end $multiplier = 1 if !$multiplier || $multiplier == 0 _arr = _getarray pos = @curpos || 0 # list does not have curpos line = @current_index #buff = _arr[line].to_s #return unless buff # if curpos is at zero , we should be checking previous line ! $multiplier.times { # if at start of line, go to previous line if pos == 0 if @current_index > 0 line -= 1 pos = _arr[line].to_s.size else # we are on first line of file at start break end end buff = _arr[line].to_s return unless buff pos2 = pos - 2 pos2 = 0 if pos2 < 0 found = buff.rindex(regex, pos2) $log.debug " backward: pos #{pos} , found #{found}, pos2 = #{pos2} " if !found || found == 0 # if not found, we've lost a counter if pos > 0 pos = 0 elsif line > 0 #line -= 1 #pos = _arr[line].to_s.size else break end else pos = found + 1 end $log.debug " backward_word: pos #{pos} line #{line} buff: #{buff}" } $multiplier = 0 @current_index = line @curpos = pos ensure_visible @repaint_required = true end
jump to previous word, like vim's “b”
# File lib/canis/core/widgets/textpad.rb, line 970 def backward_word #backward_regex(/[[:punct:][:space:]]\w/) backward_regex(:word) end
# File lib/canis/core/widgets/textpad.rb, line 818 def bottom_of_window @current_index = @prow + @scrollatrows $multiplier ||= 0 if $multiplier > 0 @current_index -= $multiplier $multiplier = 0 end end
# File lib/canis/core/widgets/textpad.rb, line 1166 def bounds_check raise "@list is empty in textpad. Bounds_check." unless @list r,c = rowcol @current_index = 0 if @current_index < 0 @current_index = @list.count()-1 if @current_index > @list.count()-1 ensure_visible check_prow @crow = @current_index + r - @prow @crow = r if @crow < r #$log.debug "XXX: PAD BOUNDS ci:#{@current_index} , old #{@oldrow},pr #{@prow}, crow #{@crow}, max #{@maxrow} pcol #{@pcol} maxcol #{@maxcol}" # 2 depends on whetehr suppress_borders if @suppress_borders @crow = @row + @height -1 if @crow >= r + @height -1 else @crow = @row + @height -2 if @crow >= r + @height -2 end #$log.debug " PAD BOUNDS CROW #{@crow} calling setrowcol" setrowcol @crow, @curpos+c lastcurpos @crow, @curpos+c if @oldindex != @current_index on_leave_row @oldindex if respond_to? :on_leave_row on_enter_row @current_index @oldindex = @current_index end if @oldrow != @prow || @oldcol != @pcol # only if scrolling has happened. @repaint_required = true end end
check that prow and pcol are within bounds
# File lib/canis/core/widgets/textpad.rb, line 1206 def check_prow @prow = 0 if @prow < 0 @pcol = 0 if @pcol < 0 cc = @list.count @rows = rows() #$log.debug " check_prow prow #{@prow} , list count #{cc}, rows #{@rows} " # 2014-05-28 - 22:41 changed < to <= otherwise prow became -1 when equal if cc <= @rows @prow = 0 else maxrow = cc - @rows - 1 if @prow > maxrow @prow = maxrow end end #$log.debug " check_prow after prow #{@prow} , list count #{cc} " # we still need to check the max that prow can go otherwise # the pad shows earlier stuff. # return end
clear all items in the object. NOTE: requires to be separate since init_vars
is called to reset index of focus etc. Also, listbox will extend this to clear selected_indices
# File lib/canis/core/widgets/textpad.rb, line 758 def clear return unless @list @list.clear @native_text.clear if @native_text # check this line, should it be removed 2014-08-27 - 20:54 fire_dimension_changed :clear init_vars end
clear the pad. There seem to be some cases when previous content of a pad remains in the last row or last col. So we clear. WARNING : pad can only clear the portion of the component placed on the window. As of 2014-05-01 - 16:07 this is no longer called since it messes with messagboxes. If you make this operational, pls test testmessageboxes.rb and look for black areas and see if the left-most column is missing.
# File lib/canis/core/widgets/textpad.rb, line 253 def clear_pad # this still doesn't work since somehow content_rows is less than height. # this is ineffectual if the rest of the code is functioning. # But REQUIRED for listbox which has its own clear_row needed in cases of white background. # as in testmessagebox.rb 5 # if called directly then cp does not reflect changes to bgcolor _color = color() _bgcolor = bgcolor() cp = get_color($datacolor, _color, _bgcolor) @cp = FFI::NCurses.COLOR_PAIR(cp) raise "pad not created" unless @pad $log.debug " clear pad content Rows is #{@content_rows} " raise "content_rows is nil" unless @content_rows (0..@content_rows).each do |n| clear_row @pad, n end # REST IS REQUIRED otherwise sometimes last line of window is not cleared # Happens in bline.rb. i think the above clears the new pad size in the window # which if it is smaller then does not clear complete window. ## TRYING OUT COMMENTING OFF THE REMAINDER 2014-05-31 - 14:35 # next part is messing up messageboxes which have a white background # so i use this copied from print_border # In messageboxes the border is more inside. but pad cannot clear the entire # window. The component may be just a part of the window. r,c = rowcol # width - 2 was okay if we started from @col + 1 as print_border does in window # but here we start from @col so reduce only 1 2014-06-20 - 01:13 TEST THIS XXX ww=width-2 ww=width-1 _col = @col + 0 startcol = 1 startcol = 0 if @suppress_borders # need to account for borders. in col+1 and ww ww=@width-0 if @suppress_borders color = @cp || $datacolor # check this out XXX @cp is already converted to COLOR_PAIR !! color = get_color($datacolor, _color, _bgcolor) att = attr() || NORMAL sp = " " * ww #if color == $datacolor $log.debug " clear_pad: colors #{@cp}, ( #{_bgcolor} #{_color} ) #{$datacolor} , attrib #{att} . r #{r} w #{ww}, h #{@height} top #{@window.top} " # 2014-05-15 - 11:01 seems we were clearing an extra row at bottom. # earlier it was r+1 but that was missing the first row, so now made it r+0 2014-06-20 - 01:15 XXX (r+0).upto(r+@height-startcol-1) do |rr| @window.printstring( rr, _col ,sp , color, att) end #end end
before updating a single row in a table we need to clear the row otherwise previous contents can show through
# File lib/canis/core/widgets/textpad.rb, line 455 def clear_row pad, lineno if @renderer and @renderer.respond_to? :clear_row @renderer.clear_row pad, lineno else # need pad width not window width, the other clearstring uses width of # widget to paint on window. @_clearstring ||= " " * @content_cols # what about bg color ??? XXX, left_margin and internal width #cp = get_color($datacolor, @color, @bgcolor) cp = @cp || FFI::NCurses.COLOR_PAIR($datacolor) att = attr() || NORMAL FFI::NCurses.wattron(pad,cp | att) FFI::NCurses.mvwaddstr(pad,lineno, 0, @_clearstring) FFI::NCurses.wattroff(pad,cp | att) end end
this is returning the original content Who is using this, should it return native ?
# File lib/canis/core/widgets/textpad.rb, line 679 def content raise "content is nil " unless @list return @list end
length of longest string in array This will give a 'wrong' max length if the array has ansi color escape sequences in it which inc the length but won't be printed. Such lines actually have less length when printed So in such cases, give more space to the pad.
# File lib/canis/core/widgets/textpad.rb, line 388 def content_cols longest = @list.max_by(&:length) ## 2013-03-06 - 20:41 crashes here for some reason when man gives error message no man entry return 0 unless longest longest.length end
# File lib/canis/core/widgets/textpad.rb, line 1082 def create_default_keyhandler @key_handler = DefaultKeyHandler.new self end
# File lib/canis/core/widgets/textpad.rb, line 1079 def create_default_renderer @renderer = DefaultRenderer.new self end
returns focussed value (what cursor is on) This may not be a string. A tree may return a node, a table an array or row
# File lib/canis/core/widgets/textpad.rb, line 709 def current_value # many descendants do not set native_text - note that list and tree and table use just @list. #@native_text[@current_index] _getarray[@current_index] end
move cursor backward by one char (currently will not pan)
# File lib/canis/core/widgets/textpad.rb, line 1042 def cursor_backward $multiplier = 1 if $multiplier == 0 if @curpos > 0 @curpos -= $multiplier @curpos = 0 if @curpos < 0 @repaint_required = true end $multiplier = 0 end
moves cursor to start of line, panning if required
# File lib/canis/core/widgets/textpad.rb, line 1064 def cursor_bol # copy of C-a - start of line @repaint_required = true if @pcol > 0 @pcol = 0 @curpos = 0 end
moves cursor to end of line also panning window if necessary NOTE: if one line on another page (not displayed) is way longer than any displayed line, then this will pan way ahead, so may not be very intelligent in such situations.
# File lib/canis/core/widgets/textpad.rb, line 1055 def cursor_eol # pcol is based on max length not current line's length @pcol = @content_cols - @cols - 1 _arr = _getarray @curpos = _arr[@current_index].size @repaint_required = true end
move cursor forward by one char (currently will not pan)
# File lib/canis/core/widgets/textpad.rb, line 1029 def cursor_forward $multiplier = 1 if $multiplier == 0 if @curpos < @cols @curpos += $multiplier if @curpos > @cols @curpos = @cols end @repaint_required = true end $multiplier = 0 end
Now since we use get_pad from window, upon the window being destroyed, it will call this. Else it will destroy pad
# File lib/canis/core/widgets/textpad.rb, line 308 def destroy FFI::NCurses.delwin(@pad) if @pad # when do i do this ? FIXME @pad = nil end
move down a line mimicking vim's j key @param [int] multiplier entered prior to invoking key
# File lib/canis/core/widgets/textpad.rb, line 834 def down num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier) #@oldindex = @current_index if num > 10 @current_index += num # no , i don't like this here. it scrolls up too much making prow = current_index unless is_visible? @current_index #alert "#{@current_index} not visible prow #{@prow} #{@scrollatrows} " @prow += num end #ensure_visible $multiplier = 0 end
Ensure current row is visible, if not make it first row NOTE - need to check if its at end and then reduce scroll at rows, check_prow
does that
@param current_index
(default if not given)
# File lib/canis/core/widgets/textpad.rb, line 1489 def ensure_visible row = @current_index unless is_visible? row @prow = row end end
supply a filename as source for textpad Reads up file into @list One can optionally send in a method which takes a filename and returns an array of data This is required if you are processing files which are binary such as zip/archives and wish to print the contents. (e.g. cygnus gem sends in :get_file_contents).
filename("a.c", method(:get_file_contents))
TODO: i think this should be sent to text
in case content_type has been set. FIXME
# File lib/canis/core/widgets/textpad.rb, line 507 def filename(filename, reader=nil) @file = filename unless File.exists? filename alert "#{filename} does not exist" return end @filetype = File.extname filename if reader @list = reader.call(filename) else @list = File.open(filename,"r").read.split("\n") end if @filetype == "" if @list.first.index("ruby") @filetype = ".rb" end end init_vars @repaint_all = true @_populate_needed = true end
Find next matching row for string accepted in ask_search
# File lib/canis/core/widgets/textpad.rb, line 1377 def find_more return unless @last_regex ix = next_match @last_regex return unless ix #@oldindex = @current_index @current_index = ix[0] @curpos = ix[1] ensure_visible end
event when user hits ENTER on a row, user would bind :PRESS callers may use +text()+ to get the value of the row, source
to get parent object.
obj.bind :PRESS { |eve| eve.text }
# File lib/canis/core/widgets/textpad.rb, line 1125 def fire_action_event return if @list.nil? || @list.size == 0 require 'canis/core/include/ractionevent' aev = text_action_event fire_handler :PRESS, aev end
to be called with program / user has added a row or changed column widths so that the pad needs to be recreated. However, cursor positioning is maintained since this is considered to be a minor change. We do not call `init_vars` since user is continuing to do some work on a row/col. NOTE : if height and width are changed then only render_all
is required
not a reparse since content has not changed.
# File lib/canis/core/widgets/textpad.rb, line 403 def fire_dimension_changed _method=nil # recreate pad since width or ht has changed (row count or col width changed) @_populate_needed = true @repaint_required = true @repaint_all = true fire_handler :DIMENSION_CHANGED, _method @__first_time = nil end
repaint only one row since content of that row has changed. No recreate of pad is done.
# File lib/canis/core/widgets/textpad.rb, line 413 def fire_row_changed ix return if ix >= @list.length clear_row @pad, ix # allow documents to reparse that line fire_handler :ROW_CHANGED, ix _arr = _getarray render @pad, ix, _arr[ix] end
jump to the next occurence of given regex in the current line. It only jumps to next line after exhausting current. @param [Regexp] passed to String.index
# File lib/canis/core/widgets/textpad.rb, line 937 def forward_regex regex if regex.is_a? Symbol regex = @text_patterns[regex] raise "Pattern specified #{regex} does not exist in text_patterns " unless regex end $multiplier = 1 if !$multiplier || $multiplier == 0 line = @current_index _arr = _getarray buff = _arr[line].to_s return unless buff pos = @curpos || 0 # list does not have curpos $multiplier.times { found = buff.index(regex, pos) if !found # if not found, we've lost a counter if line+1 < _arr.length line += 1 else return end pos = 0 else pos = found + 1 end $log.debug " forward_word: pos #{pos} line #{line} buff: #{buff}" } $multiplier = 0 @current_index = line @curpos = pos ensure_visible @repaint_required = true end
jumps cursor to next word, like vim's w key
# File lib/canis/core/widgets/textpad.rb, line 930 def forward_word #forward_regex(/[[:punct:][:space:]]\w/) forward_regex(:word) end
goto last line of file
# File lib/canis/core/widgets/textpad.rb, line 792 def goto_end $multiplier ||= 0 if $multiplier > 0 goto_line $multiplier - 1 return end @current_index = @list.count() - 1 @prow = @current_index - @scrollatrows $multiplier = 0 end
# File lib/canis/core/widgets/textpad.rb, line 897 def goto_last_position return unless @oldindex tmp = @current_index @current_index = @oldindex @oldindex = tmp bounds_check end
# File lib/canis/core/widgets/textpad.rb, line 802 def goto_line line ## we may need to calculate page, zfm style and place at right position for ensure visible #line -= 1 @current_index = line ensure_visible line bounds_check $multiplier = 0 end
# File lib/canis/core/widgets/textpad.rb, line 779 def goto_start $multiplier ||= 0 if $multiplier > 0 goto_line $multiplier - 1 return end @current_index = 0 @curpos = @pcol = @prow = 0 @prow = 0 $multiplier = 0 end
# File lib/canis/core/widgets/textpad.rb, line 1086 def handle_key ch return :UNHANDLED unless @list unless @key_handler create_default_keyhandler end @oldrow = @prow @oldcol = @pcol $log.debug "XXX: TEXTPAD got #{ch} prow = #{@prow}" ret = @key_handler.handle_key(ch) end
update the height This also calls fire_dimension_changed
so that the dimensions can be recalculated
# File lib/canis/core/widgets/textpad.rb, line 175 def height=(val) super fire_dimension_changed :height end
# File lib/canis/core/widgets/textpad.rb, line 100 def init_vars $multiplier = 0 @oldindex = @current_index = 0 # column cursor @prow = @pcol = @curpos = 0 if @row && @col @lastrow = @row + @row_offset @lastcol = @col + @col_offset end # next 4 are required because in some cases a descendant may not use methods such as list or text # to populate. +tree+ has an option of using +root()+. @repaint_required = true @repaint_all = true @_populate_needed = true map_keys unless @mapped_keys end
return true if the given row is visible
# File lib/canis/core/widgets/textpad.rb, line 1072 def is_visible? index j = index - @prow #@toprow j >= 0 && j <= scrollatrows() end
save last cursor position so when reentering, cursor can be repositioned
# File lib/canis/core/widgets/textpad.rb, line 1198 def lastcurpos r,c @lastrow = r @lastcol = c end
key mappings
TODO take from listbindings so that emacs and vim can be selected. also user can change in one place.
# File lib/canis/core/widgets/textpad.rb, line 1306 def map_keys @mapped_keys = true require 'canis/core/include/listbindings' bindings =begin bind_key([?g,?g], 'goto_start'){ goto_start } # mapping double keys like vim bind_key(279, 'goto_start'){ goto_start } bind_keys([?G,277], 'goto end'){ goto_end } bind_keys([?k,KEY_UP], "Up"){ up } bind_keys([?j,KEY_DOWN], "Down"){ down } bind_key(?\C-e, "Scroll Window Down"){ scroll_window_down } bind_key(?\C-y, "Scroll Window Up"){ scroll_window_up } bind_keys([32,338, ?\C-d], "Scroll Forward"){ scroll_forward } # adding CTRL_SPACE as back scroll 2014-04-14 bind_keys([0,?\C-b,339], "Scroll Backward"){ scroll_backward } # the next one invalidates the single-quote binding for bookmarks #bind_key([?',?']){ goto_last_position } # vim , goto last row position (not column) bind_key(?/, :ask_search) bind_key(?n, :find_more) bind_key([?\C-x, ?>], :scroll_right) bind_key([?\C-x, ?<], :scroll_left) bind_key(?\M-l, :scroll_right) bind_key(?\M-h, :scroll_left) bind_key(?L, :bottom_of_window) bind_key(?M, :middle_of_window) bind_key(?H, :top_of_window) bind_key(?w, :forward_word) bind_key(?b, :backward_word) bind_key(?l, :cursor_forward) bind_key(?h, :cursor_backward) bind_key(?$, :cursor_eol) bind_key(KEY_ENTER, :fire_action_event) =end end
# File lib/canis/core/widgets/textpad.rb, line 827 def middle_of_window @current_index = @prow + (@scrollatrows/2) $multiplier = 0 end
since 2014-05-26 - 12:13 new logic to take into account multiple matches in one line
First time, starts searching current line from cursor position onwards to end of file
If no match, then checks from start of file to current line.
@param [String, Regex] pattern to search (uses :index
) @param [Integer] line number to start with. Optional. nil
means start search from current position. @param [Integer] line number to end with. Optional. nil
means end search at end of array @return [Array<Integer, Integer>] index of line where pattern found, index of offset in line.
+nil+ returned if no match.
NOTE:
startline and endline are more for internal purposes, externally we would call this only with the pattern.
@example
pos = next_match /abc/ # pos is nil or [line, col]
2014-05-28 - Added to_s before index() so that other descendants can use, such as treemodel or tablemodel
# File lib/canis/core/widgets/textpad.rb, line 1432 def next_match str, startline=nil, endline=nil # 1. look in current row after the curpos # 2. if no match look in rest of array # 3. if no match and you started from current_index, then search # from start of file to current_index. call _next_match with 0. _arr = _getarray if !startline startline = @current_index pos = @curpos + 1 # FIXME you could be at end of line #_line = _arr[startline] _line = to_searchable(startline) _col = _line.index(str, pos) if _line if _col return [startline, _col] end startline += 1 # FIXME check this end of file end # FIXME iterate only through the ones we need, not all _arr.each_with_index do |line, ix| next if ix < startline break if endline && ix > endline # next line just a hack and not correct if only one match in file FIXME line = to_searchable(ix) _col = line.index str if _col return [ix, _col] end end if startline > 0 return next_match str, 0, @current_index end return nil end
search for the next occurence of given regexp. Returns line and col if found, else nil. @param [String, Regexp] pattern to search for (uses :index) @return [nil] return value of no consequence
# File lib/canis/core/widgets/textpad.rb, line 1470 def next_regex regex if regex.is_a? Symbol regex = @text_patterns[regex] raise "Pattern specified #{regex} does not exist in text_patterns " unless regex end @last_regex = regex find_more end
called when this widget is entered, by form
Canis::Widget#on_enter
# File lib/canis/core/widgets/textpad.rb, line 1152 def on_enter super set_form_row end
execute binding when a row is entered, used more in lists to display some text in a header or footer as one traverses
# File lib/canis/core/widgets/textpad.rb, line 1139 def on_enter_row arow return nil if @list.nil? || @list.size == 0 @repaint_footer_required = true ## can this be done once and stored, and one instance used since a lot of traversal will be done require 'canis/core/include/ractionevent' aev = TextActionEvent.new self, :ENTER_ROW, current_value().to_s, @current_index, @curpos fire_handler :ENTER_ROW, aev end
content_rows can be more than size of pad, but never less. Same for cols. width or cols of pad, earlier called @content_cols
# File lib/canis/core/widgets/textpad.rb, line 217 def pad_cols return @content_cols if @content_cols _content_cols = content_cols() _content_cols = @cols if _content_cols < @cols return _content_cols end
content_rows can be more than size of pad, but never less. Same for cols. height of pad, or number of row, earlier called @content_rows
# File lib/canis/core/widgets/textpad.rb, line 207 def pad_rows # content_rows can be more than size of pad, but never less. Same for cols. return @content_rows if @content_rows content_rows = @list.count content_rows = @rows if content_rows < @rows return content_rows end
write pad onto window
private
padrefresh can fail if width is greater than NCurses.COLS padrefresh can fail if height (@rows + sr) is greater than NCurses.LINES or tput lines try reducing height when creating textpad.
# File lib/canis/core/widgets/textpad.rb, line 318 def padrefresh # sometimes padref is called directly from somewhere but dimensions have changed. # 2014-05-27 - 11:42 unless @__first_time __calc_dimensions @__first_time = true end # startrow is the row of TP plus 1 for border top = @window.top left = @window.left sr = @startrow + top sc = @startcol + left ser = @rows + sr sec = @cols + sc if @fixed_bounds #retval = FFI::NCurses.prefresh(@pad,@prow,@pcol, sr , sc , ser , sec ); $log.debug "PAD going into fixed_bounds with #{@fixed_bounds}" sr = @fixed_bounds[0] if @fixed_bounds[0] sc = @fixed_bounds[1] if @fixed_bounds[1] ser = @fixed_bounds[2] if @fixed_bounds[2] sec = @fixed_bounds[3] if @fixed_bounds[3] end # this is a fix, but the entire popup is not moved up. title and borders are still # drawn in wrong positions, and left there after popup is off. maxr = FFI::NCurses.LINES - 1 maxc = FFI::NCurses.COLS if ser > maxr $log.warn "XXX PADRE ser > max. sr= #{sr} and ser #{ser}. sr:#{@startrow}+ #{top} , sc:#{@startcol}+ #{left}, rows:#{@rows}+ #{sr} cols:#{@cols}+ #{sc} top #{top} left #{left} " #_t = ser - maxr #ser = maxr #sr -= _t #$log.warn "XXX PADRE after correcting ser #{sr} and #{ser} " end # there are some freak cases where prow or pcol comes as -1, but prefresh does not return a -1. However, this # could affect some other calculation somewhere. retval = FFI::NCurses.prefresh(@pad,@prow,@pcol, sr , sc , ser , sec ); $log.warn "XXXPADREFRESH #{retval} #{self.class}:#{self.name}, #{@prow}, #{@pcol}, #{sr}, #{sc}, #{ser}, #{sec}." if retval == -1 # remove next debug statement after some testing DELETE $log.debug "0PADREFRESH #{retval} #{self.class}, #{@prow}, #{@pcol}, #{sr}, #{sc}, #{ser}, #{sec}." if retval == 0 if retval < 0 Ncurses.beep if sr > maxr $log.warn "XXXPADREF #{sr} should be <= #{maxr} " end if sc < 0 || sc >= maxc $log.warn "XXXPADREF #{sc} should be less than #{maxc} " end if ser > maxr || ser < sr $log.warn "XXXPADREF #{ser} should be less than #{maxr} and gt #{sr} " end if sec > maxc || sec < sc $log.warn "XXXPADREF #{sec} should be less than #{maxc} and gt #{sc} " end $log.warn "XXXPADRE sr= #{sr} and ser #{ser}. sr:#{@startrow}+ #{top} , sc:#{@startcol}+ #{left}, rows:#{@rows}+ #{sr} cols:#{@cols}+ #{sc} top #{top} left #{left} " end #$log.debug "XXX: PADREFRESH #{retval} #{self.class}, #{@prow}, #{@pcol}, #{sr}, #{sc}, #{ser}, #{sec}." if retval == 0 # padrefresh can fail if width is greater than NCurses.COLS # or if height exceeds tput lines. As long as content is less, it will work # the moment content_rows exceeds then this issue happens. # @rows + sr < tput lines #FFI::NCurses.prefresh(@pad,@prow,@pcol, @startrow + top, @startcol + left, @rows + @startrow + top, @cols+@startcol + left); end
textpad's preprocess
# File lib/canis/core/widgets/textpad.rb, line 701 def preprocess_text if @document @document.preprocess_text @list end end
This is to render a row, for those overriding classes who have overridden
render_all, but not +render+. e.g. +Table+. THis is also required for row modifications.
# File lib/canis/core/widgets/textpad.rb, line 444 def render pad, lineno, text @renderer.render pad, lineno, text end
—- end pad related —– }}} —- Section render related —– {{{
iterate through content rendering each row 2013-03-27 - 01:51 separated so that widgets with headers such as tables can override this for better control
# File lib/canis/core/widgets/textpad.rb, line 428 def render_all _arr = _getarray raise "textpad:render_all: array is nil " unless _arr @renderer.source ||= self @renderer.render_all @pad, _arr end
supply a custom renderer that implements +render()+ @see render
# File lib/canis/core/widgets/textpad.rb, line 438 def renderer r @renderer = r end
# File lib/canis/core/widgets/textpad.rb, line 1230 def repaint unless @__first_time __calc_dimensions @__first_time = true end return unless @list # trying out since it goes into padrefresh even when no data 2014-04-10 - 00:32 @graphic = @form.window unless @graphic @window ||= @graphic raise "Window not set in textpad" unless @window unless @renderer create_default_renderer end ## 2013-03-08 - 21:01 This is the fix to the issue of form callign an event like ? or F1 # which throws up a messagebox which leaves a black rect. We have no place to put a refresh # However, form does call repaint for all objects, so we can do a padref here. Otherwise, # it would get rejected. UNfortunately this may happen more often we want, but we never know # when something pops up on the screen. #$log.debug " repaint textpad RR #{@repaint_required} #{@window.top} " unless @repaint_required print_foot if @repaint_footer_required # set in on_enter_row # trying out removing this, since too many refreshes 2014-05-01 - 12:45 #padrefresh return end # if repaint is required, print_foot not called. unless repaint_all is set, and that # is rarely set. preprocess_text # in textdialog, @window was nil going into create_pad 2014-04-15 - 01:28 # creates pad and calls render_all populate_pad if !@pad or @_populate_needed if @repaint_all clear_pad Ncurses::Panel.update_panels render_all end raise "PAD IS NIL -- populate_pad was not called ??" unless @pad _do_borders print_foot if @repaint_footer_required # if still not done padrefresh # in some cases next line prevents overlapped window from refreshing again, leaving black rows. # removing it causes problems in other cases. (tasks.rb, confirm window. dbdemo, F2 closing) Ncurses::Panel.update_panels @repaint_required = false @repaint_all = false end
@deprecated : row_count
used just for compat, use length or size
# File lib/canis/core/widgets/textpad.rb, line 729 def row_count ; @list.length ; end
scrolls lines backward a window full at a time, on pressing pageup C-u may not work since it is trapped by form earlier. Need to fix
# File lib/canis/core/widgets/textpad.rb, line 892 def scroll_backward #@oldindex = @current_index @current_index -= @scrollatrows @prow = @current_index - @scrollatrows end
scrolls lines a window full at a time, on pressing SPACE or C-d or pagedown
# File lib/canis/core/widgets/textpad.rb, line 884 def scroll_forward #@oldindex = @current_index @current_index += @scrollatrows @prow = @current_index - @scrollatrows end
# File lib/canis/core/widgets/textpad.rb, line 924 def scroll_left @pcol -= 1 end
# File lib/canis/core/widgets/textpad.rb, line 904 def scroll_right # I don't think it will ever be less since we've increased it to cols if @content_cols <= @cols maxpcol = 0 @pcol = 0 else maxpcol = @content_cols - @cols - 1 @pcol += 1 @pcol = maxpcol if @pcol > maxpcol end # to prevent right from retaining earlier painted values # padreader does not do a clear, yet works fine. # OK it has an update_panel after padrefresh, that clears it seems. #this clears entire window not just the pad #FFI::NCurses.wclear(@window.get_window) # so border and title is repainted after window clearing # # Next line was causing all sorts of problems when scrolling with ansi formatted text #@repaint_all = true end
scrolls window down mimicking vim C-e @param [int] multiplier entered prior to invoking key
# File lib/canis/core/widgets/textpad.rb, line 863 def scroll_window_down num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier) @prow += num if @prow > @current_index @current_index += 1 end #check_prow $multiplier = 0 end
scrolls window up mimicking vim C-y @param [int] multiplier entered prior to invoking key
# File lib/canis/core/widgets/textpad.rb, line 874 def scroll_window_up num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier) @prow -= num unless is_visible? @current_index # one more check may be needed here TODO @current_index -= num end $multiplier = 0 end
# File lib/canis/core/widgets/textpad.rb, line 160 def scrollatrows unless @suppress_borders @height - 3 else @height - 1 end end
called by form
# File lib/canis/core/widgets/textpad.rb, line 1161 def set_form_col end
called by form
# File lib/canis/core/widgets/textpad.rb, line 1157 def set_form_row setrowcol @lastrow, @lastcol end
display text given in an array format. This is the principal way of giving content to a textpad, other than filename(). @param Array of lines @param format (optional) can be :tmux :ansi or :none If a format other than :none is given, then formatted_text is called.
# File lib/canis/core/widgets/textpad.rb, line 539 def text(*val) if val.empty? return @list end $log.debug " TEXTPAD inside text() with #{val.class} " case val when Array # either its an array of strings # or its an array, and val[0] is an array of strings, and val[1] is a hash / symbol TODO case val[0] when String # This is the usual simple case of an array of strings @list = val $log.debug " creating TEXTDOC 0 with String" when TextDocument # this is repeating it seems FIXME $log.debug " creating TEXTDOC 04 with TextDocu #{val[0].content_type} " @document = val[0] @document.source ||= self @document.parse_required # added 2014-09-03 - 17:54 @list = @document.text when Array # This is the complex case which i would like to phase out. # Earlier this was what was used where the second arg was an optional hash @list = val[0] if val[1].is_a? Symbol content_type = val[1] hsh = { :text => @list, :content_type => content_type } $log.debug " creating TEXTDOC 1 with #{content_type} " @document = TextDocument.new hsh @document.source = self elsif val[1].is_a? Hash # this is hack for those cases where ct is there, but the caller may not # pass it in config if val[1].key? :content_type and val[1][:content_type].nil? ; else # content type comes nil from viewer/help which sets it later using block yield content_type = val[1][:content_type] stylesheet = val[1][:stylesheet] @title = val[1][:title] if val[1].key? :title $log.debug " creating TEXTDOC 2 with ct=#{content_type}, #{val[1]} " @document = TextDocument.new val[1] @document.text = @list @document.source = self end else #raise "val_1 Unable to do anything with #{val[1].class} " $log.debug " val_1 Unable to do anything with #{val[1].class} in textpad text()" end else raise "val_0 Unable to do anything with #{val[0].class} " end when Hash $log.debug " creating TEXTDOC 3 with #{val[:content_type]} " @document = TextDocument.new val @document.source ||= self @list = @document.text when TextDocument $log.debug " creating TEXTDOC 4 with TextDocu #{val.content_type} " @document = val @document.source ||= self @document.parse_required # added 2014-09-03 - 17:54 @list = @document.text end @_populate_needed = true @repaint_all = true @repaint_required = true init_vars # i don't want the whole thing going into the event 2014-06-04 fire_property_change :text, "dummmy", "text has changed" self end
creates and returns a textactionevent object with current_value
, line number and cursor position
# File lib/canis/core/widgets/textpad.rb, line 1132 def text_action_event aev = TextActionEvent.new self, :PRESS, current_value().to_s, @current_index, @curpos end
convert the row to something we can run index
on. it should be exactly as the display will be, so find offsets are correct. Required for descendants such as Table
.
# File lib/canis/core/widgets/textpad.rb, line 1480 def to_searchable index _getarray[index].to_s end
# File lib/canis/core/widgets/textpad.rb, line 810 def top_of_window @current_index = @prow $multiplier ||= 0 if $multiplier > 0 @current_index += $multiplier $multiplier = 0 end end
move up a line mimicking vim's k key @param [int] multiplier entered prior to invoking key
# File lib/canis/core/widgets/textpad.rb, line 848 def up num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier) #@oldindex = @current_index if num > 10 @current_index -= num #unless is_visible? @current_index #if @prow > @current_index ##$status_message.value = "1 #{@prow} > #{@current_index} " #@prow -= 1 #else #end #end $multiplier = 0 end
returns the row offset of the focussed row, based on what is visible this takes into account scrolling, and is useful if some caller needs to know where the current index is actually being displayed (example if it wishes to display a popup at that row) An argument is not being taken since the index should be visible.
# File lib/canis/core/widgets/textpad.rb, line 1500 def visual_index row = @current_index row - @prow end
set the width This also calls fire_dimension_changed
so that the dimensions can be recalculated
# File lib/canis/core/widgets/textpad.rb, line 181 def width=(val) super fire_dimension_changed :width end
Private Instance Methods
creates the pad
# File lib/canis/core/widgets/textpad.rb, line 190 def create_pad destroy if @pad #$log.debug "XXXCP: create_pad #{@content_rows} #{@content_cols} , w:#{@width} c #{@cols} , r: #{@rows}" @content_rows = @content_cols = nil @content_rows = pad_rows() @content_cols = pad_cols() $log.debug "XXXCP: create_pad :#{@content_rows} , #{@content_cols} . w:#{@width} c #{@cols} , r: #{@rows}" raise "create_pad content_rows is nil " unless @content_rows raise "create_pad content_cols is nil " unless @content_cols @pad = @window.get_pad(@content_rows, @content_cols ) end
convenience method to return byte – is it used ???
# File lib/canis/core/widgets/textpad.rb, line 1342 def key x x.getbyte(0) end
creates pad and sets repaint_all so populate happens FIXME - earlier used to create and populate, but now i have decoupled populate
since that was forcing create to happen.
# File lib/canis/core/widgets/textpad.rb, line 228 def populate_pad @_populate_needed = false create_pad # clearstring is the string required to clear the pad to background color @clearstring = nil $log.debug " populate pad color = #{@color} , bg = #{@bgcolor} " #cp = get_color($datacolor, color(), bgcolor()) # commenting off next line meant that textdialog had a black background 2014-05-01 - 23:37 #@cp = FFI::NCurses.COLOR_PAIR(cp) # we seem to be clearing always since a pad is often reused. so making the variable whenever pad created. @repaint_all = true end