rbcurse-extras 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/README.md +75 -0
  2. data/VERSION +1 -0
  3. data/examples/data/list.txt +300 -0
  4. data/examples/data/lotr.txt +12 -0
  5. data/examples/data/table.txt +36 -0
  6. data/examples/data/tasks.txt +27 -0
  7. data/examples/data/unix1.txt +21 -0
  8. data/examples/inc/qdfilechooser.rb +70 -0
  9. data/examples/inc/rfe_renderer.rb +121 -0
  10. data/examples/newtabbedwindow.rb +100 -0
  11. data/examples/rfe.rb +1236 -0
  12. data/examples/test2.rb +670 -0
  13. data/examples/testeditlist.rb +78 -0
  14. data/examples/testtable.rb +270 -0
  15. data/examples/testvimsplit.rb +141 -0
  16. data/lib/rbcurse/extras/include/celleditor.rb +112 -0
  17. data/lib/rbcurse/extras/include/checkboxcellrenderer.rb +57 -0
  18. data/lib/rbcurse/extras/include/comboboxcellrenderer.rb +30 -0
  19. data/lib/rbcurse/extras/include/defaultlistselectionmodel.rb +79 -0
  20. data/lib/rbcurse/extras/include/listkeys.rb +37 -0
  21. data/lib/rbcurse/extras/include/listselectable.rb +144 -0
  22. data/lib/rbcurse/extras/include/tableextended.rb +40 -0
  23. data/lib/rbcurse/extras/widgets/horizlist.rb +203 -0
  24. data/lib/rbcurse/extras/widgets/menutree.rb +63 -0
  25. data/lib/rbcurse/extras/widgets/multilinelabel.rb +142 -0
  26. data/lib/rbcurse/extras/widgets/rcomboedit.rb +256 -0
  27. data/lib/rbcurse/extras/widgets/rlink.rb.moved +27 -0
  28. data/lib/rbcurse/extras/widgets/rlistbox.rb +1247 -0
  29. data/lib/rbcurse/extras/widgets/rmenulink.rb.moved +21 -0
  30. data/lib/rbcurse/extras/widgets/rmulticontainer.rb +304 -0
  31. data/lib/rbcurse/extras/widgets/rmultisplit.rb +722 -0
  32. data/lib/rbcurse/extras/widgets/rmultitextview.rb +306 -0
  33. data/lib/rbcurse/extras/widgets/rpopupmenu.rb +755 -0
  34. data/lib/rbcurse/extras/widgets/rtable.rb +1758 -0
  35. data/lib/rbcurse/extras/widgets/rvimsplit.rb +800 -0
  36. data/lib/rbcurse/extras/widgets/table/tablecellrenderer.rb +86 -0
  37. data/lib/rbcurse/extras/widgets/table/tabledatecellrenderer.rb +98 -0
  38. metadata +94 -0
@@ -0,0 +1,21 @@
1
+ Eric S. Raymond, in his book The Art of Unix Programming,[2] summarizes the Unix philosophy as the widely-used KISS Principle of "Keep it Simple, Stupid."[3] He also provides a series of design rules:
2
+
3
+ * Rule of Modularity: Write simple parts connected by clean interfaces.
4
+ * Rule of Clarity: Clarity is better than cleverness.
5
+ * Rule of Composition: Design programs to be connected to other programs.
6
+ * Rule of Separation: Separate policy from mechanism; separate interfaces from engines.
7
+ * Rule of Simplicity: Design for simplicity; add complexity only where you must.
8
+ * Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do.
9
+ * Rule of Transparency: Design for visibility to make inspection and debugging easier.
10
+ * Rule of Robustness: Robustness is the child of transparency and simplicity.
11
+ * Rule of Representation: Fold knowledge into data so program logic can be stupid and robust.[4]
12
+ * Rule of Least Surprise: In interface design, always do the least surprising thing.
13
+ * Rule of Silence: When a program has nothing surprising to say, it should say nothing.
14
+ * Rule of Repair: When you must fail, fail noisily and as soon as possible.
15
+ * Rule of Economy: Programmer time is expensive; conserve it in preference to machine time.
16
+ * Rule of Generation: Avoid hand-hacking; write programs to write programs when you can.
17
+ * Rule of Optimization: Prototype before polishing. Get it working before you optimize it.
18
+ * Rule of Diversity: Distrust all claims for "one true way".
19
+ * Rule of Extensibility: Design for the future, because it will be here sooner than you think.
20
+
21
+
@@ -0,0 +1,70 @@
1
+ # NOTE this no longer works after the changes in Messagebox,
2
+ # needs to be redone.
3
+ # this is a test program, tests out messageboxes. type F1 to exit
4
+ # a quick dirty file chooser in 2 lines of code.
5
+ require 'logger'
6
+ require 'rbcurse/core/system/ncurses'
7
+ require 'rbcurse/core/system/window'
8
+ require 'rbcurse/core/widgets/rwidget'
9
+
10
+ ##
11
+ # a quick dirty file chooser - only temporary till we make something better.
12
+ class QDFileChooser
13
+ attr_accessor :show_folders # bool
14
+ attr_accessor :traverse_folders # bool
15
+ attr_accessor :default_pattern # e.g. "*.*"
16
+ attr_accessor :dialog_title # File Chooser
17
+ def initialize
18
+
19
+ end
20
+ def show_open_dialog
21
+ @form = RubyCurses::Form.new nil
22
+ label = RubyCurses::Label.new @form, {'text' => 'File', 'row'=>3, 'col'=>4, 'color'=>'black', 'bgcolor'=>'white', 'mnemonic'=>'F'}
23
+ field = RubyCurses::Field.new @form do
24
+ name "file"
25
+ row 3
26
+ col 10
27
+ display_length 40
28
+ set_label label
29
+ end
30
+ default_pattern ||= "*.*"
31
+ flist = Dir.glob(default_pattern)
32
+ @listb = RubyCurses::Listbox.new @form do
33
+ name "mylist"
34
+ row 5
35
+ col 10
36
+ width 40
37
+ height 10
38
+ list flist
39
+ title "File list"
40
+ title_attrib 'bold'
41
+ end
42
+ #@listb.list.bind(:ENTER_ROW) { field.set_buffer @listb.selected_item }
43
+ listb = @listb
44
+ field.bind(:CHANGE) do |f|
45
+ flist = Dir.glob(f.getvalue+"*")
46
+ l = listb.list
47
+ l.remove_all
48
+ l.insert 0, *flist
49
+ end
50
+ atitle = @dialog_title || "Quick Dirty(TM) File Chooser"
51
+ @mb = RubyCurses::MessageBox.new @form do
52
+ title atitle
53
+ type :override
54
+ height 20
55
+ width 60
56
+ top 5
57
+ left 20
58
+ default_button 0
59
+ button_type :ok_cancel
60
+ end
61
+ #$log.debug "MBOX :selected #{@listb.selected_item}, #{@listb[@listb.getvalue[0]]} "
62
+ return @mb.selected_index == 0 ? :OK : :CANCEL
63
+ end
64
+ def get_selected_file
65
+ #return @mb.selected_index == 0 ? @listb.selected_item : nil
66
+ #return @mb.selected_index == 0 ? @listb[@listb.getvalue[0]] : nil
67
+ # return either the selected_value or if user pressed okay, then focussed item
68
+ return @mb.selected_index == 0 ? @listb.selected_value || @listb.selected_item : nil
69
+ end
70
+ end
@@ -0,0 +1,121 @@
1
+ module RubyCurses
2
+
3
+ ##
4
+ # This is a basic list cell renderer that will render the to_s value of anything.
5
+ # Using alignment one can use for numbers too.
6
+ # However, for booleans it will print true and false. If editing, you may want checkboxes
7
+ class RfeRenderer < ListCellRenderer
8
+ def initialize text="", config={}, &block
9
+ super
10
+ @orig_bgcolor = @bgcolor
11
+ @orig_color = @color
12
+ @orig_attr = @attr
13
+ # we need to refer to file types, executable, dir, link, otherwise we'll go crazy
14
+ @color_hash = {
15
+ '.rb' => [get_color($datacolor, 'red', 'black'), 0],
16
+ '.txt' => [get_color($datacolor, 'white', 'black'), 0],
17
+ '.gemspec' => [get_color($datacolor, 'yellow', 'black'), 0],
18
+ '.gem' => [get_color($datacolor, 'yellow', 'black'), 0],
19
+ '.c' => [get_color($datacolor, 'green', 'black'), 0],
20
+ '.py' => [get_color($datacolor, 'green', 'black'), 0],
21
+ '.tgz' => [get_color($datacolor, 'red', 'black'), get_attrib('bold')],
22
+ '.gz' => [get_color($datacolor, 'red', 'black'), 0],
23
+ '.zip' => [get_color($datacolor, 'red', 'black'), 0],
24
+ '.jar' => [get_color($datacolor, 'red', 'black'), 0],
25
+ '.html' => [get_color($datacolor, 'green', 'black'), get_attrib('reverse')],
26
+ "" => [get_color($datacolor, 'yellow', 'black'), get_attrib('bold')],
27
+ '.jpg' => [get_color($datacolor, 'magenta', 'black'), 0],
28
+ '.png' => [get_color($datacolor, 'magenta', 'black'), 0],
29
+ '.sh' => [get_color($datacolor, 'red', 'black'), 0],
30
+ '.mp3' => [get_color($datacolor, 'cyan', 'blue'), 0],
31
+ '.bak' => [get_color($datacolor, 'magenta', 'black'), 0],
32
+ '.tmp' => [get_color($datacolor, 'black', 'blue'), 0],
33
+ '.pl' => [get_color($datacolor, 'green', 'black'), 0],
34
+ '.java' => [get_color($datacolor, 'cyan', 'blue'), 0],
35
+ '.class' => [get_color($datacolor, 'magenta', 'black'), 0],
36
+ '.pyc' => [get_color($datacolor, 'magenta', 'black'), 0],
37
+ '.o' => [get_color($datacolor, 'magenta', 'black'), 0],
38
+ '.a' => [get_color($datacolor, 'magenta', 'black'), 0],
39
+ '.lib' => [get_color($datacolor, 'magenta', 'black'), 0]
40
+ }
41
+ end
42
+
43
+ # override parent method to set color_pair and attr for different kind of files
44
+ def select_colors focussed, selected
45
+ ext = File.extname(@path)
46
+ c = @color_hash[ext]
47
+ c = [$datacolor, FFI::NCurses::A_NORMAL] unless c
48
+ if File.directory? @path
49
+ c = [get_color($datacolor, 'blue', 'black'), FFI::NCurses::A_BOLD]
50
+ end
51
+ @color_pair, @attr = *c
52
+ if selected
53
+ #@attr = FFI::NCurses::A_UNDERLINE | FFI::NCurses::A_BOLD # UL not avaible on screen
54
+ @attr = FFI::NCurses::A_REVERSE | FFI::NCurses::A_BOLD
55
+ @color_pair = $datacolor
56
+ end
57
+ if focussed
58
+ @attr = FFI::NCurses::A_REVERSE # | FFI::NCurses::A_BOLD
59
+ end
60
+ end
61
+ #
62
+ def repaint graphic, r=@row,c=@col, row_index=-1, value=@text, focussed=false, selected=false
63
+
64
+ @bgcolor = @orig_bgcolor
65
+ @color = @orig_color
66
+ @row_attr = @orig_attr
67
+ # XXX ouch, when we delete from list, must delete from here too.
68
+ value = @parent.entries[row_index]
69
+ if value[0,1]=="/"
70
+ path = value.dup
71
+ else
72
+ path = @parent.cur_dir()+"/"+value
73
+ end
74
+ @path = path # i need it in select_color
75
+ begin
76
+ stat = File.stat(path)
77
+ if File.directory? path
78
+ @row_attr = Ncurses::A_BOLD
79
+ #@color = 'yellow'
80
+ end
81
+ value = format_string(value, path, stat)
82
+ super
83
+
84
+ rescue => err
85
+ $log.debug " rfe_renderer: #{err}"
86
+ end
87
+
88
+ end
89
+ GIGA_SIZE = 1073741824.0
90
+ MEGA_SIZE = 1048576.0
91
+ KILO_SIZE = 1024.0
92
+
93
+ # Return the file size with a readable style.
94
+ def readable_file_size(size, precision)
95
+ case
96
+ #when size == 1 then "1 B"
97
+ when size < KILO_SIZE then "%d B" % size
98
+ when size < MEGA_SIZE then "%.#{precision}f K" % (size / KILO_SIZE)
99
+ when size < GIGA_SIZE then "%.#{precision}f M" % (size / MEGA_SIZE)
100
+ else "%.#{precision}f G" % (size / GIGA_SIZE)
101
+ end
102
+ end
103
+ def date_format t
104
+ t.strftime "%Y/%m/%d"
105
+ end
106
+ def format_string fn, path,stat
107
+ max_len = 30
108
+ f = fn.dup
109
+ if File.directory? path
110
+ #"%-*s\t(dir)" % [max_len,f]
111
+ #f = "/"+f # disallows search on keypress
112
+ f = "/"+f
113
+ end
114
+ if f.size > max_len
115
+ f = f[0..max_len-1]
116
+ end
117
+ "%-*s %10s %s" % [max_len,f, readable_file_size(stat.size,1), date_format(stat.mtime)]
118
+ end
119
+ # ADD HERE
120
+ end
121
+ end
@@ -0,0 +1,100 @@
1
+ # this is a test program, tests out tabbed panes. type F1 to exit
2
+ #
3
+ require 'logger'
4
+ require 'rbcurse'
5
+ require 'rbcurse/core/widgets/rtabbedpane'
6
+ require 'rbcurse/core/widgets/rcontainer'
7
+ require 'rbcurse/core/widgets/rcombo'
8
+ require 'rbcurse/core/widgets/rtabbedwindow'
9
+
10
+ include RubyCurses
11
+ class SetupTabbedPane
12
+ def run
13
+ $config_hash ||= Variable.new Hash.new
14
+ #configvar.update_command(){ |v| $config_hash[v.source()] = v.value }
15
+
16
+ r = Container.new nil, :suppress_borders => true
17
+ l1 = Label.new nil, :name => "profile", :attr => 'bold', :text => "Profile"
18
+ f1 = Field.new nil, :name => "name", :maxlen => 20, :display_length => 20, :bgcolor => :white,
19
+ :color => :black, :text => "abc", :label => ' Name: '
20
+ f2 = Field.new nil, :name => "email", :display_length => 20, :bgcolor => :white,
21
+ :color => :blue, :text => "me@google.com", :label => 'Email: '
22
+ f3 = RadioButton.new nil, :variable => $config_hash, :text => "red", :value => "RED", :color => :red
23
+ f4 = RadioButton.new nil, :variable => $config_hash, :text => "blue", :value => "BLUE", :color => :blue
24
+ f5 = RadioButton.new nil, :variable => $config_hash, :text => "green", :value => "GREEN", :color => :green
25
+ r.add(l1,f1)
26
+ r.add(f2)
27
+ r.add(f3,f4,f5)
28
+
29
+ tp = TabbedWindow.new :row => 3, :col => 7, :width => 60, :height => 20 do
30
+ title "User Setup"
31
+ button_type :ok_apply_cancel
32
+ tab "&Profile" do
33
+ item Field.new nil, :row => 2, :col => 2, :text => "enter your name", :label => ' Name: '
34
+ item Field.new nil, :row => 3, :col => 2, :text => "enter your email", :label => 'Email: '
35
+ end
36
+ tab "&Settings" do
37
+ item Label.new nil, :text => "Text", :row => 1, :col => 2, :attr => 'bold'
38
+ item CheckBox.new nil, :row => 2, :col => 2, :text => "Antialias text"
39
+ item CheckBox.new nil, :row => 3, :col => 2, :text => "Use bold fonts"
40
+ item CheckBox.new nil, :row => 4, :col => 2, :text => "Allow blinking text"
41
+ item CheckBox.new nil, :row => 5, :col => 2, :text => "Display ANSI Colors"
42
+ item Label.new nil, :text => "Cursor", :row => 7, :col => 2, :attr => 'bold'
43
+ $config_hash.set_value Variable.new, :cursor
44
+ item RadioButton.new nil, :row => 8, :col => 2, :text => "Block", :value => "block", :variable => $config_hash[:cursor]
45
+ item RadioButton.new nil, :row => 9, :col => 2, :text => "Blink", :value => "blink", :variable => $config_hash[:cursor]
46
+ item RadioButton.new nil, :row => 10, :col => 2, :text => "Underline", :value => "underline", :variable => $config_hash[:cursor]
47
+ end
48
+ tab "&Term" do
49
+
50
+ item Label.new nil, :text => "Arrow Key in Combos", :row => 2, :col => 2, :attr => 'bold'
51
+ x = Variable.new
52
+ $config_hash.set_value x, :term
53
+ item RadioButton.new nil, :row => 3, :col => 2, :text => "ignore", :value => "ignore", :variable => $config_hash[:term]
54
+ item RadioButton.new nil, :row => 4, :col => 2, :text => "popup", :value => "popup", :variable => $config_hash[:term]
55
+ item RadioButton.new nil, :row => 5, :col => 2, :text => "next", :value => "next", :variable => $config_hash[:term]
56
+ cb = ComboBox.new nil, :row => 7, :col => 2, :display_length => 15,
57
+ :list => %w[xterm xterm-color xterm-256color screen vt100 vt102],
58
+ :label => "Declare terminal as: "
59
+ #radio.update_command() {|rb| ENV['TERM']=rb.value }
60
+ item cb
61
+ x.update_command do |rb|
62
+ cb.arrow_key_policy=rb.value.to_sym
63
+ end
64
+
65
+ end
66
+ tab "Conta&iner" do
67
+ item r
68
+ end
69
+ # tell tabbedpane what to do if a button is pressed (ok/apply/cancel)
70
+ command do |eve|
71
+ alert "user pressed button index:#{eve.event} , Name: #{eve.action_command}, Tab: #{eve.source.current_tab} "
72
+ case eve.event
73
+ when 0,2 # ok cancel
74
+ throw :close, eve.event
75
+ when 1 # apply
76
+ end
77
+ end
78
+ end
79
+ tp.run
80
+ end
81
+
82
+ end
83
+ if $0 == __FILE__
84
+ # Initialize curses
85
+ begin
86
+ # XXX update with new color and kb
87
+ VER::start_ncurses # this is initializing colors via ColorMap.setup
88
+ $log = Logger.new((File.join(ENV["LOGDIR"] || "./" ,"rbc13.log")))
89
+ $log.level = Logger::DEBUG
90
+ tp = SetupTabbedPane.new()
91
+ buttonindex = tp.run
92
+ rescue => ex
93
+ ensure
94
+ VER::stop_ncurses
95
+ p ex if ex
96
+ p(ex.backtrace.join("\n")) if ex
97
+ $log.debug( ex) if ex
98
+ $log.debug(ex.backtrace.join("\n")) if ex
99
+ end
100
+ end
@@ -0,0 +1,1236 @@
1
+ #####################################################
2
+ # This is a sample program demonstrating a 2 pane file explorer using
3
+ # rbcurse's widgets.
4
+ # I have used a listbox here, perhaps a Table would be more configurable
5
+ # than a listbox.
6
+ #
7
+ # FIXME many popups use the old Messagebox which will crash. i've updated
8
+ # grep not the others.
9
+ # Copyright rkumar 2009, 2010 under Ruby License.
10
+ #
11
+ ####################################################
12
+ require 'rbcurse'
13
+ require 'rbcurse/core/widgets/rcombo'
14
+ require 'rbcurse/extras/widgets/rlistbox'
15
+ require './inc/rfe_renderer'
16
+ require 'rbcurse/core/widgets/keylabelprinter'
17
+ require 'rbcurse/core/widgets/applicationheader'
18
+ require 'rbcurse/core/include/action'
19
+ require 'fileutils'
20
+ require 'yaml' ## added for 1.9
21
+ #$LOAD_PATH << "/Users/rahul/work/projects/rbcurse/"
22
+
23
+ # TODO
24
+ # operations on selected files: move, delete, zip, copy
25
+ # - delete should move to Trash if exists - DONE
26
+ # global - don't ask confirm
27
+ # Select based on pattern.
28
+ # This class represents the finder pane. There are 2
29
+ # on this sample app
30
+ # NOTE: rfe_renderer uses entries so you may need to sync it with list_data_model
31
+ # actually, renderer should refer back to list data model!
32
+ class FileExplorer
33
+ include FileUtils
34
+ attr_reader :wdir
35
+ attr_reader :list # listbox
36
+ attr_reader :dir
37
+ attr_reader :prev_dirs
38
+ attr_reader :other_list # the opposite list
39
+ attr_reader :entries # avoid, can be outdated
40
+ attr_accessor :filter_pattern
41
+
42
+ def initialize form, rfe, row, col, height, width
43
+ @form = form
44
+ @rfe = rfe
45
+ @row, @col, @ht, @wid = row, col, height, width
46
+ @dir = Dir.new(Dir.getwd)
47
+ @wdir = @dir.path
48
+ @filter_pattern = '*'
49
+ @prev_dirs=[]
50
+ @inside_block = false
51
+ $entry_count = 0 # just for show... may not be accurate after deletes etc
52
+
53
+ end
54
+ def title str
55
+ @list.title = str
56
+ end
57
+ def selected_color
58
+ @list.selected_color
59
+ end
60
+ def selected_bgcolor
61
+ @list.selected_bgcolor
62
+ end
63
+
64
+ # changes to given dir
65
+ # ensure that path is provided since other list
66
+ # may have cd'd elsewhere
67
+ def change_dir adir
68
+ list = @list
69
+ begin
70
+ #dir = File.expand_path dir
71
+ #cd "#{@dir.path}/#{adir}"
72
+ cd adir
73
+ list.title = pwd()
74
+ @dir = Dir.new(Dir.getwd)
75
+ @wdir = @dir.path
76
+ @prev_dirs << @wdir
77
+ rescan
78
+ rescue => err
79
+ @rfe.status_row.text = err.to_s
80
+ end
81
+ end
82
+ def goto_previous_dir
83
+ #alert "Previous dirs contain #{@prev_dirs} "
84
+ d = @prev_dirs.pop
85
+ if !d.nil? && d == @wdir
86
+ d = @prev_dirs.pop
87
+ end
88
+ d = @wdir unless d # for those cases where we are showing some result and user doesn't know how t get back
89
+ change_dir d unless d.nil?
90
+ end
91
+ def filter list
92
+ list.delete_if { |f|
93
+ !File.directory? @wdir +"/"+ f and !File.fnmatch?(@filter_pattern, f)
94
+ }
95
+ #$log.debug " FILTER CALLED AFTER #{list.size}, #{list.entries}"
96
+ end
97
+ def rescan
98
+ flist = @dir.entries
99
+ $entry_count = flist.size # just for show... may not be accurate after deletes etc
100
+ flist.shift
101
+ #populate @entries
102
+ populate flist
103
+ end
104
+ def populate flist
105
+ #fl << format_string("..", nil)
106
+ #fl = []
107
+ #flist.each {|f| ff = "#{@wdir}/#{f}"; stat = File.stat(ff)
108
+ # fl << format_string(f, stat)
109
+ #}
110
+ filter(flist) if @filter_pattern != '*'
111
+ @entries = flist
112
+ list.list_data_model.remove_all
113
+ #list.list_data_model.insert 0, *fl
114
+ list.list_data_model.insert 0, *flist
115
+ list.set_form_row
116
+ #$entry_count = list.size # just for show... may not be accurate after deletes etc
117
+ end
118
+ def sort key, reverse=false
119
+ # remove parent before sorting, keep at top
120
+ first = @entries.delete_at(0) if @entries[0]==".."
121
+ key ||= @sort_key
122
+ cdir=cur_dir()+"/"
123
+ case key
124
+ when :size
125
+ @entries.sort! {|x,y| xs = File.stat(cdir+x); ys = File.stat(cdir+y);
126
+ if reverse
127
+ xs.size <=> ys.size
128
+ else
129
+ ys.size <=> xs.size
130
+ end
131
+ }
132
+ when :mtime
133
+ @entries.sort! {|x,y| xs = File.stat(cdir+x); ys = File.stat(cdir+y);
134
+ if reverse
135
+ xs.mtime <=> ys.mtime
136
+ else
137
+ ys.mtime <=> xs.mtime
138
+ end
139
+ }
140
+ when :atime
141
+ @entries.sort! {|x,y| xs = File.stat(cdir+x); ys = File.stat(cdir+y);
142
+ if reverse
143
+ xs.atime <=> ys.atime
144
+ else
145
+ ys.atime <=> xs.atime
146
+ end
147
+ }
148
+ when :name
149
+ @entries.sort! {|x,y| x <=> y
150
+ if reverse
151
+ x <=> y
152
+ else
153
+ y <=> x
154
+ end
155
+ }
156
+ when :ext
157
+ @entries.sort! {|x,y|
158
+ if reverse
159
+ File.extname(cdir+x) <=> File.extname(cdir+y)
160
+ else
161
+ File.extname(cdir+y) <=> File.extname(cdir+x)
162
+ end
163
+ }
164
+ end
165
+ @sort_key = key
166
+ @entries.insert 0, first unless first.nil? # keep parent on top
167
+ populate @entries
168
+ end
169
+ GIGA_SIZE = 1073741824.0
170
+ MEGA_SIZE = 1048576.0
171
+ KILO_SIZE = 1024.0
172
+
173
+ # Return the file size with a readable style.
174
+ def readable_file_size(size, precision)
175
+ case
176
+ #when size == 1 : "1 B"
177
+ when size < KILO_SIZE then "%d B" % size
178
+ when size < MEGA_SIZE then "%.#{precision}f K" % (size / KILO_SIZE)
179
+ when size < GIGA_SIZE then "%.#{precision}f M" % (size / MEGA_SIZE)
180
+ else "%.#{precision}f G" % (size / GIGA_SIZE)
181
+ end
182
+ end
183
+ def date_format t
184
+ t.strftime "%Y/%m/%d"
185
+ end
186
+ def oldformat_string fn, stat
187
+ max_len = 30
188
+ f = fn.dup
189
+ if File.directory? f
190
+ #"%-*s\t(dir)" % [max_len,f]
191
+ #f = "/"+f # disallows search on keypress
192
+ f = f + "/ "
193
+ end
194
+ if f.size > max_len
195
+ f = f[0..max_len-1]
196
+ end
197
+ "%-*s\t%10s\t%s" % [max_len,f, readable_file_size(stat.size,1), date_format(stat.mtime)]
198
+ end
199
+ def cur_dir
200
+ @dir.path
201
+ end
202
+ alias :current_dir :cur_dir
203
+ def draw_screen dir=nil
204
+ # I get an error here if dir in yaml file is non-existent, like deleted or copied from another machine
205
+ # 2010-08-22 19:25
206
+ if dir
207
+ if File.exists?(dir) && File.directory?(dir)
208
+ cd dir unless dir.nil?
209
+ else
210
+ dir = nil
211
+ end
212
+ end
213
+
214
+ wdir = FileUtils.pwd
215
+ @prev_dirs << wdir
216
+ @dir = Dir.new(Dir.getwd)
217
+ @wdir = @dir.path
218
+ r = @row
219
+ c = @col
220
+ #cola = 1
221
+ #colb = Ncurses.COLS/2
222
+ ht = @ht
223
+ wid = @wid
224
+ #fl = Dir.glob(default_pattern)
225
+ #flist << format_string("..", nil)
226
+ fl = @dir.entries
227
+ fl.shift
228
+ filter(fl)
229
+ #flist = []
230
+ #fl.each {|f| stat = File.stat(f)
231
+ # flist << format_string(f, stat)
232
+ #}
233
+ @entries = fl
234
+ $entry_count = @entries.size
235
+ title = pwd()
236
+ @wdir = title
237
+ rfe = self
238
+
239
+ lista = Listbox.new @form do
240
+ name "lista"
241
+ row r
242
+ col c
243
+ width wid
244
+ height ht
245
+ #list flist
246
+ list fl
247
+ title wdir
248
+ #title_attrib 'reverse'
249
+ cell_renderer RfeRenderer.new "", {"color"=>@color, "bgcolor"=>@bgcolor, "parent" => rfe, "display_length"=> wid-2}
250
+ KEY_ROW_SELECTOR 0 # C-space since space used for preview
251
+ end
252
+ @list = lista
253
+ lista.bind(:ENTER) {|l| @rfe.current_list(self); l.title_attrib 'reverse'; }
254
+ lista.bind(:LEAVE) {|l| l.title_attrib 'normal'; }
255
+
256
+
257
+ #row_cmd = lambda {|list| file = list.list_data_model[list.current_index].split(/\t/)[0].strip; @rfe.status_row.text = File.stat("#{cur_dir()}/#{file}").inspect }
258
+ row_cmd = lambda {|lb, list| file = list.entries[lb.current_index]; @rfe.status_row.text = file; # File.stat("#{cur_dir()}/#{file}").inspect
259
+ }
260
+ lista.bind(:ENTER_ROW, self) {|lb,list| row_cmd.call(lb,list) }
261
+
262
+ end
263
+ def list_data
264
+ @list.list_data_model
265
+ end
266
+ def current_index
267
+ @list.current_index
268
+ end
269
+ def filename ix=current_index()
270
+ #@entries[@list.current_index]
271
+ list_data()[ix]
272
+ end
273
+ def filepath ix=current_index()
274
+ f = filename(ix)
275
+ return unless f
276
+ if f[0,1]=='/'
277
+ f
278
+ else
279
+ cur_dir() + "/" + f
280
+ end
281
+ end
282
+ # delete the item at position i
283
+ def delete_at i=@list.current_index
284
+ ret = @list.list_data_model.delete_at i
285
+ ret = @entries.delete_at i
286
+ end
287
+ def delete obj
288
+ ret = @list.list_data_model.delete obj
289
+ ret = @entries.delete_at obj
290
+ end
291
+ def insert_at obj, ix = @list.current_index
292
+ @list.list_data_model.insert ix, f
293
+ @entries.insert ix, obj
294
+ end
295
+ def remove_selected_rows
296
+ rows = @list.selected_rows
297
+ rows=rows.sort! {|x,y| y <=> x }
298
+ rows.each do |i|
299
+ ret = @list.list_data_model.delete_at i
300
+ ret = @entries.delete_at i
301
+ end
302
+ end
303
+
304
+ # ADD
305
+ end
306
+ class RFe
307
+ attr_reader :status_row
308
+ def initialize
309
+ @window = VER::Window.root_window
310
+ @form = Form.new @window
311
+ # made in invisible since i am using chunks to print. however, we do set this variable
312
+ status_row = RubyCurses::Label.new @form, {'text' => "", :row => Ncurses.LINES-4, :col => 0, :display_length=>Ncurses.COLS-2, :visible => false}
313
+ @status_row = status_row
314
+ colb = Ncurses.COLS/2
315
+ ht = Ncurses.LINES - 4
316
+ wid = Ncurses.COLS/2 - 0
317
+ @trash_path = File.expand_path("~/.Trash")
318
+ @trash_exists = File.directory? @trash_path
319
+ $log.debug " trash_path #{@trash_path}, #{@trash_exists}"
320
+ @lista = FileExplorer.new @form, self, row=1, col=1, ht, wid
321
+ @listb = FileExplorer.new @form, self, row=1, col=colb, ht, wid
322
+
323
+ init_vars
324
+ end
325
+ def init_vars
326
+ @bookmarks=[]
327
+ @config_name = File.expand_path("~/.rfe.yml")
328
+ if File.exist? @config_name
329
+ @config = YAML::load( File.open(@config_name));
330
+ if !@config.nil?
331
+ @bookmarks = @config["bookmarks"]||[]
332
+ @last_dirs = @config["last_dirs"]
333
+ end
334
+ end
335
+ @config ||={}
336
+ @stopping = false
337
+ end
338
+ def save_config
339
+ @config["last_dirs"]=[@lista.current_dir(),@listb.current_dir()]
340
+ File.open(@config_name, "w") { | f | YAML.dump( @config, f )}
341
+ end
342
+ def move
343
+ fp = @current_list.filepath #.gsub(' ',"\ ")
344
+ fn = @current_list.filename
345
+ $log.debug " FP #{fp}"
346
+ other_list = [@lista, @listb].index(@current_list)==0 ? @listb : @lista
347
+ other_dir = other_list.cur_dir
348
+ $log.debug " OL #{other_list.cur_dir}"
349
+ if @current_list.list.selected_row_count == 0
350
+ str= "#{fn}"
351
+ else
352
+ str= "#{@current_list.list.selected_row_count} files "
353
+ end
354
+
355
+ mb = RubyCurses::MessageBox.new :width => 80 do
356
+ title "Move"
357
+ message "Move #{str} to"
358
+ add Field.new :default => other_dir, :bgcolor => :cyan, :name => "name"
359
+ button_type :ok_cancel
360
+ default_button 0
361
+ end
362
+ index = mb.run
363
+ fld = mb.widget("name")
364
+
365
+ #mb = RubyCurses::MessageBox.new do
366
+ #title "Move"
367
+ #message "Move #{str} to"
368
+ #type :input
369
+ #width 80
370
+ #default_value other_dir
371
+ #button_type :ok_cancel
372
+ #default_button 0
373
+ #end
374
+ #confirm "selected :#{mb.input_value}, #{mb.selected_index}"
375
+ if index == 0
376
+ begin
377
+ if @current_list.list.selected_row_count == 0
378
+ FileUtils.move(fp, fld.text)
379
+ #ret = @current_list.list.list().delete_at @current_list.list.current_index # ???
380
+ # @current_list.entries.delete_at @current_list.current_index
381
+ @current_list.delete_at
382
+ else
383
+ each_selected_row do |f|
384
+ FileUtils.move(f, fld.text)
385
+ end
386
+ @current_list.remove_selected_rows
387
+ @current_list.list.clear_selection
388
+ end
389
+ rescue => err
390
+ textdialog err
391
+ $log.debug "XXX: Exception in rfe, move #{err.to_s} "
392
+ end
393
+ other_list.rescan
394
+ end
395
+ end
396
+ def each_selected_row #title, message, default_value
397
+ rows = @current_list.list.selected_rows
398
+ rows = rows.dup
399
+ rows.each do |i|
400
+ fp = @current_list.filepath i
401
+ #$log.debug " moving #{i}: #{fp}"
402
+ #FileUtils.move(fp, mb.input_value)
403
+ yield fp
404
+ end
405
+ end
406
+ def copy
407
+ fp = @current_list.filepath #.gsub(' ',"\ ")
408
+ fn = @current_list.filename
409
+ $log.debug " FP #{fp}"
410
+ other_list = [@lista, @listb].index(@current_list)==0 ? @listb : @lista
411
+ other_dir = other_list.cur_dir
412
+ $log.debug " OL #{other_list.cur_dir}"
413
+ if @current_list.list.selected_row_count == 0
414
+ str= "#{fn}"
415
+ else
416
+ str= "#{@current_list.list.selected_row_count} files "
417
+ end
418
+ mb = RubyCurses::MessageBox.new do
419
+ title "Copy"
420
+ message "Copy #{str} to"
421
+ type :input
422
+ width 80
423
+ default_value other_dir
424
+ button_type :ok_cancel
425
+ default_button 0
426
+ end
427
+ if mb.selected_index == 0
428
+ if @current_list.list.selected_row_count == 0
429
+ FileUtils.copy(fp, mb.input_value)
430
+ else
431
+ each_selected_row do |f|
432
+ FileUtils.copy(f, mb.input_value)
433
+ end
434
+ @current_list.list.clear_selection
435
+ end
436
+ other_list.rescan
437
+ end
438
+ end
439
+ def delete
440
+ fp = @current_list.filepath #.gsub(' ',"\ ")
441
+ fn = @current_list.filename
442
+ if @current_list.list.selected_row_count == 0
443
+ str= "#{fn}"
444
+ else
445
+ str= "#{@current_list.list.selected_row_count} files "
446
+ end
447
+ if confirm("delete #{str}")
448
+ if @current_list.list.selected_row_count == 0
449
+ if @trash_exists
450
+ FileUtils.mv fp, @trash_path
451
+ else
452
+ FileUtils.rm fp
453
+ end
454
+ ret=@current_list.delete_at
455
+ else
456
+ each_selected_row do |f|
457
+ if @trash_exists
458
+ FileUtils.mv f, @trash_path
459
+ else
460
+ FileUtils.rm f
461
+ end
462
+ end
463
+ @current_list.remove_selected_rows
464
+ @current_list.list.clear_selection
465
+ end
466
+ end
467
+ end
468
+ def copy1
469
+ fp = @current_list.filepath
470
+ fn = @current_list.filename
471
+ $log.debug " FP #{fp}"
472
+ other_list = [@lista, @listb].index(@current_list)==0 ? @listb : @lista
473
+ other_dir = other_list.cur_dir
474
+ $log.debug " OL #{other_list.cur_dir}"
475
+ str= "copy #{fn} to #{other_list.cur_dir}"
476
+ $log.debug " copy #{fp}"
477
+ #confirm "#{str}"
478
+ mb = RubyCurses::MessageBox.new do
479
+ title "Copy"
480
+ message "Copy #{fn} to"
481
+ type :input
482
+ width 60
483
+ default_value other_dir
484
+ button_type :ok_cancel
485
+ default_button 0
486
+ end
487
+ #confirm "selected :#{mb.input_value}, #{mb.selected_index}"
488
+ if mb.selected_index == 0
489
+ # need to redraw directories
490
+ FileUtils.copy(fp, mb.input_value)
491
+ other_list.rescan
492
+ end
493
+ end
494
+ ## TODO : make this separate and callable with its own keylabels
495
+ # Now you can just use viewer() instead of having to write this. See Utils in rwidget.rb
496
+ def view content=nil # can throw IO errors
497
+ require 'rbcurse/core/widgets/rtextview'
498
+ wt = 0
499
+ wl = 0
500
+ wh = Ncurses.LINES-wt
501
+ ww = Ncurses.COLS-wl
502
+ if content.nil?
503
+ begin
504
+ fp = @current_list.filepath
505
+ content = get_contents(fp)
506
+ rescue => err
507
+ $log.debug err.to_s
508
+ $log.debug(err.backtrace.join("\n"))
509
+ alert err.to_s
510
+ return
511
+ end
512
+ else
513
+ fp=""
514
+ end
515
+ @layout = { :height => wh, :width => ww, :top => wt, :left => wl }
516
+ @v_window = VER::Window.new(@layout)
517
+ @v_form = RubyCurses::Form.new @v_window
518
+ @textview = TextView.new @v_form do
519
+ name "myView"
520
+ row 0
521
+ col 0
522
+ width ww
523
+ height wh-0
524
+ title fp
525
+ title_attrib 'bold'
526
+ print_footer true
527
+ footer_attrib 'bold'
528
+ end
529
+ #content = File.open(fp,"r").readlines
530
+ begin
531
+ @textview.set_content content #, :WRAP_WORD
532
+ @v_form.repaint
533
+ @v_window.wrefresh
534
+ Ncurses::Panel.update_panels
535
+ while((ch = @v_window.getchar()) != ?\C-q.getbyte(0) )
536
+ break if [ FFI::NCurses::KEY_F3 , KEY_F10 , ?q.getbyte(0) , 27, 2727].include? ch
537
+ @v_form.handle_key ch
538
+ @v_form.repaint
539
+ ##@v_window.wrefresh
540
+ end
541
+ rescue => err
542
+ alert err.to_s
543
+ ensure
544
+ @v_window.destroy if !@v_window.nil?
545
+ end
546
+ end
547
+ def get_contents fp
548
+ return nil unless File.readable? fp
549
+ return Dir.new(fp).entries if File.directory? fp
550
+ case File.extname(fp)
551
+ when '.tgz','.gz'
552
+ cmd = "tar -ztvf #{fp}"
553
+ content = %x[#{cmd}]
554
+ when '.zip'
555
+ cmd = "unzip -l #{fp}"
556
+ content = %x[#{cmd}]
557
+ else
558
+ content = File.open(fp,"r").readlines
559
+ end
560
+ end
561
+ def opt_file c
562
+ fp = @current_list.filepath
563
+ fn = @current_list.filename
564
+ other_list = [@lista, @listb].index(@current_list)==0 ? @listb : @lista
565
+ case c
566
+ when 'c'
567
+ copy
568
+ when 'm'
569
+ move
570
+ when 'd'
571
+ delete
572
+ when 'u'
573
+ str= "move #{fn} to #{other_list.cur_dir}"
574
+ if confirm("#{str}")
575
+ $log.debug " MOVE #{str}"
576
+ end
577
+ when 'v'
578
+ str= "view #{fp}"
579
+ #if confirm("#{str}")==:YES
580
+ $log.debug " VIEW #{fp}"
581
+ view
582
+ #end
583
+ when 'r'
584
+ str= "ruby #{fn}"
585
+ if confirm("#{str}")
586
+ $log.debug " #{str} "
587
+ end
588
+ when 'e'
589
+ str= "edit #{fp}"
590
+ #if confirm("#{str}")==:YES
591
+ edit fp
592
+ when 'x'
593
+ str= "exec #{fp}"
594
+ exec_popup fp
595
+ when 'p'
596
+ page fp
597
+ when 'q'
598
+ qlmanage fp
599
+ end
600
+ end
601
+ def opt_dir c
602
+ fp = @current_list.filepath
603
+ fn = @current_list.filename
604
+ case c
605
+ when 'O'
606
+ # str= "copy #{fn} to #{other_list.cur_dir}"
607
+ if File.directory? @current_list.filepath
608
+ @current_list.change_dir fp
609
+ end
610
+ when 'o'
611
+ if File.directory? @current_list.filepath
612
+ @other_list.change_dir fp
613
+ end
614
+ @open_in_other = true # ???basically keep opening in other
615
+ when 'd'
616
+ str= "delete #{fn} "
617
+ if confirm("#{str}")
618
+ $log.debug " delete #{fp}"
619
+ FileUtils.rm fp
620
+ ret=@current_list.list.list_data_model.delete_at @current_list.list.current_index # ???
621
+ $log.debug " DEL RET #{ret},#{@current_list.list.current_index}"
622
+ end
623
+ when 'u'
624
+ str= "move #{fn} to #{other_list.cur_dir}"
625
+ if confirm("#{str}")
626
+ $log.debug " MOVE #{str}"
627
+ end
628
+ when 'b'
629
+ dd = @current_list.wdir
630
+ @bookmarks << dd unless @bookmarks.include? dd
631
+ when 'u'
632
+ dd = @current_list.wdir
633
+ @bookmarks.delete dd
634
+ when 'l'
635
+ @current_list.populate @bookmarks
636
+ when 's'
637
+ @config["bookmarks"] = @bookmarks
638
+ save_config
639
+ when 'e'
640
+ str= "edit #{fp}"
641
+ #if confirm("#{str}")==:YES
642
+ edit fp
643
+ when 'm'
644
+ f = get_string("Enter a directory to create", 20 )
645
+ if f != ""
646
+ FileUtils.mkdir f
647
+ @current_list.list.list_data_model.insert @current_list.list.current_index, f # ???
648
+ @current_list.entries.insert @current_list.list.current_index, f # ???
649
+ end
650
+
651
+ when 'x'
652
+ str= "exec #{fp}"
653
+ exec_popup fp
654
+ end
655
+ end
656
+ def exec_popup fp
657
+ last_exec_def1 = @last_exec_def1 || ""
658
+ last_exec_def2 = @last_exec_def2 || false
659
+
660
+ sel, inp, hash = get_string_with_options("Enter a command to execute on #{fp}", 30, last_exec_def1, {"checkboxes" => ["view result"], "checkbox_defaults"=>[last_exec_def2]})
661
+ if sel == 0
662
+ @last_exec_def1 = inp
663
+ @last_exec_def2 = hash["view result"]
664
+ cmd = "#{inp} #{fp}"
665
+ filestr = %x[ #{cmd} 2>/dev/null ]
666
+ if hash["view result"]==true
667
+ view filestr
668
+ end
669
+ end
670
+ end
671
+ def edit fp=@current_list.filepath
672
+ $log.debug " edit #{fp}"
673
+ editor = ENV['EDITOR'] || 'vi'
674
+ vimp = %x[which #{editor}].chomp
675
+ shell_out "#{vimp} #{fp}"
676
+ end
677
+
678
+ # TODO we need to move these to some common file so differnt programs and demos
679
+ # can use them on pressing space or enter.
680
+ def page fp=@current_list.filepath
681
+ ft=%x[file #{fp}]
682
+ if ft.index("text")
683
+ pager = ENV['PAGER'] || 'less'
684
+ vimp = %x[which #{pager}].chomp
685
+ shell_out "#{vimp} #{fp}"
686
+ elsif ft.index(/zip/i)
687
+ shell_out "tar tvf #{fp} | less"
688
+ elsif ft.index(/directory/i)
689
+ shell_out "ls -lh #{fp} | less"
690
+ else
691
+ alert "#{fp} is not text, not paging "
692
+ #use_on_file "als", fp # only zip or archive
693
+ end
694
+ end
695
+ def qlmanage fp=@current_list.filepath
696
+ begin
697
+ a=%x[which qlmanage]
698
+ if a != ""
699
+ cmd="qlmanage -p #{@current_list.filepath} 2>/dev/null"
700
+ %x[#{cmd}]
701
+ else
702
+ alert "qlmanage not present on your system. Trying pager"
703
+ page fp
704
+ end
705
+ rescue Interrupt
706
+ rescue => err
707
+ alert err.to_s
708
+ end
709
+ end
710
+ # also if calling program gives output but does not stop
711
+ # or take control, then nothing will show up
712
+ def use_on_file prog, args, fp=@current_list.filepath
713
+ vimp = %x[which #{prog}].chomp
714
+ if vimp != ""
715
+ alert "using #{prog} "
716
+ shell_out "#{vimp} #{args } #{fp}"
717
+ else
718
+ alert "could not locate #{prog} "
719
+ end
720
+ end
721
+ def stopping?
722
+ @stopping
723
+ end
724
+ def draw_screens
725
+ lasta = lastb = nil
726
+ if !@config["last_dirs"].nil?
727
+ lasta = @config["last_dirs"][0]
728
+ lastb = @config["last_dirs"][1]
729
+ end
730
+ @lista.draw_screen lasta
731
+ @listb.draw_screen lastb
732
+
733
+ # bind dd to delete file
734
+ # actually this should be in listbox, and we should listen for row delete and then call opt_file
735
+ @form.bind_key([?d,?d], 'delete'){
736
+ opt_file 'd'
737
+ }
738
+ # list traps 'q' so this won't work
739
+ @form.bind_key([?q,?q], 'quit'){
740
+ @stopping = true
741
+ }
742
+ # this won't work since the listbox will consume the d first
743
+ @form.bind_key(?@, 'home'){
744
+ @current_list.change_dir File.expand_path("~/")
745
+ }
746
+ @form.bind_key(?^, 'previous dir'){
747
+ @current_list.change_dir @current_list.prev_dirs[0] unless @current_list.prev_dirs.empty?
748
+ }
749
+ @form.bind_key(?\C-f, 'file menu'){
750
+ @klp.mode :file
751
+ @klp.repaint
752
+ while((ch = @window.getchar()) != ?\C-c.getbyte(0) )
753
+ if ch < 33 || ch > 126
754
+ Ncurses.beep
755
+ elsif "cmdsuvrexpq".index(ch.chr) == nil
756
+ Ncurses.beep
757
+ else
758
+ opt_file ch.chr
759
+ break
760
+ end
761
+ end
762
+ @klp.mode :normal
763
+ }
764
+ # C-d has been bound to scroll-down
765
+ @form.bind_key(?\C-w, 'dir menu'){
766
+ @klp.mode :dir
767
+ @klp.repaint
768
+ keys = @klp.get_current_keys
769
+ while((ch = @window.getchar()) != ?\C-c.getbyte(0) )
770
+ if ch < 33 || ch > 126
771
+ Ncurses.beep
772
+ elsif !keys.include?(ch.chr)
773
+ Ncurses.beep
774
+ else
775
+ opt_dir ch.chr
776
+ break
777
+ end
778
+ end
779
+ @klp.mode :normal
780
+ }
781
+ # backspace KEY_BS
782
+ @form.bind_key(127, 'previous dir'){
783
+ @current_list.goto_previous_dir
784
+ }
785
+ # form gives this to list which consumes it for selection.
786
+ # need to bind to list's LIST_SELECTION_EVENT
787
+ @form.bind_key(32, 'pager'){
788
+ fp = @current_list.filepath
789
+ page fp
790
+ }
791
+ @form.bind_key(FFI::NCurses::KEY_F3, 'view'){
792
+ begin
793
+ view()
794
+ rescue => err
795
+ alert err.to_s
796
+ end
797
+ }
798
+ @form.bind_key(FFI::NCurses::KEY_F1, 'help'){
799
+ #Io.view(["this is some help text","hello there"])
800
+ color0 = get_color($promptcolor, 'black','green')
801
+ color1 = get_color($reversecolor, 'green','black')
802
+ color2 = get_color($reversecolor, 'magenta','black')
803
+ arr = []
804
+ #arr << " FILE BROWSER HELP "
805
+ arr << [color0," FILE BROWSER HELP ", FFI::NCurses::A_NORMAL]
806
+ arr << " "
807
+ arr << [[color1," <tab> ",nil],[color2, "- switch between windows", nil]]
808
+ arr << [[color1," <enter> ",nil],[color2, "- open dir", nil]]
809
+ arr << [[color1," F3 ",nil],[color2, "- view file/dir/zip contents (toggle) ", nil]]
810
+ arr << [[color1," F4 ",nil],[color2, "- edit file content", nil]]
811
+ arr << [[color1," <char> ", nil],[color2, "first file starting with <char>",nil]]
812
+ #arr << " VIM MODE "
813
+ arr << [color0," VIM MODE ", FFI::NCurses::A_NORMAL]
814
+ arr << [[color1," M",nil],[color2, "-v - Vim like bindings for navigation (toggle)", nil]]
815
+ arr << " jk gg G up down motion, C-n C-p, Alt-0, Alt-9 "
816
+ arr << [[color1," p or Space ",nil],[color2, "- use pager on current file", nil]]
817
+ arr << [[color1," q ",nil],[color2, "- use qlmanage on file ",nil],[$promptcolor, " (OSX)", nil]]
818
+ arr << [[color1," f<char> ",nil],[color2, "- first file starting with <char>", nil]]
819
+
820
+ arr << " "
821
+ arr << " < > ^ V - move help window "
822
+ arr << " "
823
+ # next line does not work due to chunks
824
+ w = arr.max_by(&:length).length
825
+ #arr = ["this is some help text","hello there"]
826
+
827
+ require 'rbcurse/core/util/viewer'
828
+ RubyCurses::Viewer.view(arr, :layout => [10,10, 4+arr.size, w+2],:close_key => KEY_RETURN, :title => "<Enter> to close", :print_footer => false) do |t|
829
+ # you may configure textview further here.
830
+ #t.suppress_borders true
831
+ #t.color = :black
832
+ #t.bgcolor = :white
833
+ # or
834
+ #t.attr = :reverse
835
+ t.attr = :normal
836
+ #t.bind_key(KEY_F1){ alert "closing up!"; throw :close }
837
+ # just for kicks move window around
838
+ t.bind_key('<'){ f = t.form.window; c = f.left - 1; f.hide; f.mvwin(f.top, c); f.show;
839
+ f.reset_layout([f.height, f.width, f.top, c]);
840
+ }
841
+ t.bind_key('>'){ f = t.form.window; c = f.left + 1; f.hide; f.mvwin(f.top, c);
842
+ f.reset_layout([f.height, f.width, f.top, c]); f.show;
843
+ }
844
+ t.bind_key('^'){ f = t.form.window; c = f.top - 1 ; f.hide; f.mvwin(c, f.left);
845
+ f.reset_layout([f.height, f.width, c, f.left]) ; f.show;
846
+ }
847
+ t.bind_key('V'){ f = t.form.window; c = f.top + 1 ; f.hide; f.mvwin(c, f.left);
848
+ f.reset_layout([f.height, f.width, c, f.left]); f.show;
849
+ }
850
+ end
851
+ }
852
+ @form.bind_key(FFI::NCurses::KEY_F4, 'edit'){
853
+ edit()
854
+ }
855
+ @form.bind_key(FFI::NCurses::KEY_F6, 'sort'){
856
+ selected_index, sort_key, reverse, case_sensitive = sort_popup
857
+ if selected_index == 0
858
+ @current_list.sort(sort_key, reverse)
859
+ end
860
+ }
861
+ @form.bind_key(FFI::NCurses::KEY_F5, 'filter'){
862
+ filter()
863
+ }
864
+ @form.bind_key(FFI::NCurses::KEY_F7, 'grep'){
865
+ grep_popup()
866
+ }
867
+ @form.bind_key(FFI::NCurses::KEY_F8, 'system'){
868
+ system_popup()
869
+ }
870
+ # will work if you are in vim mode
871
+ @form.bind_key(?p, 'pager'){
872
+ page
873
+ }
874
+ @form.bind_key(?z, 'qlmanage OSX'){
875
+ qlmanage
876
+ }
877
+ # will no longer come here. list event has to be used
878
+ #@form.bind_key(?\C-m){ # listbox has eaten it up
879
+ @lista.list.bind(:PRESS){
880
+ dir = @current_list.filepath
881
+ if File.directory? @current_list.filepath
882
+ @current_list.change_dir dir
883
+ end
884
+ }
885
+ @listb.list.bind(:PRESS){
886
+ dir = @current_list.filepath
887
+ if File.directory? @current_list.filepath
888
+ @current_list.change_dir dir
889
+ end
890
+ }
891
+ gw = get_color($reversecolor, 'green', 'black')
892
+ bw = get_color($datacolor, 'blue', 'black')
893
+ @klp = RubyCurses::KeyLabelPrinter.new @form, get_key_labels
894
+ @klp.footer_color_pair = bw
895
+ @klp.footer_mnemonic_color_pair = gw
896
+ @klp.set_key_labels get_key_labels(:file), :file
897
+ @klp.set_key_labels get_key_labels(:view), :view
898
+ @klp.set_key_labels get_key_labels(:dir), :dir
899
+ @form.repaint
900
+ @window.wrefresh
901
+ Ncurses::Panel.update_panels
902
+ begin
903
+ chunks=["File","Key"]
904
+ color0 = get_color($promptcolor, 'white','blue')
905
+ color1 = get_color($datacolor, 'black','green')
906
+
907
+ ## qq stops program, but only if M-v (vim mode)
908
+ while(!stopping? && (ch = @window.getchar()) != ?\C-q.getbyte(0) )
909
+ break if ch == KEY_F10
910
+ s = keycode_tos ch
911
+ #status_row.text = "| Pressed #{ch} , #{s}"
912
+ @form.handle_key(ch)
913
+
914
+ # the following is just a test of show_colored_chunks
915
+ # Please use status_line introduced in 1.4.x
916
+ #count = @current_list.list.size
917
+ count1 = @lista.list.size
918
+ count2 = @listb.list.size
919
+ x, y = FFI::NCurses::getyx @window.get_window
920
+ chunks[0]=[color0, "%-30s" % status_row.text, FFI::NCurses::A_BOLD]
921
+ chunks[1]=[color1, "%-18s" % " | #{ch}, #{s} |", FFI::NCurses::A_NORMAL]
922
+ chunks[2]=[color0, " Total: #{count1}, #{count2}"]
923
+ @window.wmove(Ncurses.LINES-3,0)
924
+ @window.color_set(color0, nil)
925
+ @window.print_empty_line
926
+ @window.wmove(Ncurses.LINES-3,0)
927
+ @window.show_colored_chunks chunks
928
+ @window.wmove(x,y)
929
+
930
+ #@form.repaint
931
+ @window.wrefresh
932
+ end
933
+ ensure
934
+ @window.destroy if !@window.nil?
935
+ save_config
936
+ end
937
+
938
+ end
939
+ # TODO
940
+ #
941
+ #
942
+ #
943
+
944
+ # current_list
945
+ ##
946
+ # getter and setter for current_list
947
+ def current_list(*val)
948
+ if val.empty?
949
+ @current_list
950
+ else
951
+ @current_list = val[0]
952
+ @other_list = [@lista, @listb].index(@current_list)==0 ? @listb : @lista
953
+ end
954
+ end
955
+ def get_key_labels categ=nil
956
+ if categ.nil?
957
+ key_labels = [
958
+ ['C-q', 'Exit'], ['C-v', 'View'],
959
+ ['C-f', 'File'], ['C-w', 'Dir'],
960
+ ['C-Sp','Select'], ['Spc', "Preview"],
961
+ ['F3', 'View'], ['F4', 'Edit'],
962
+ ['F5', 'Filter'], ['F6', 'Sort'],
963
+ ['F7', 'Grep'], ['F8', 'System'],
964
+ ['M-0', 'Top'], ['M-9', 'End'],
965
+ ['C-p', 'PgUp'], ['C-n', 'PgDn'],
966
+ ['M-v', 'Vim Mode'], ['BSpc', 'PrevDir']
967
+ ]
968
+ elsif categ == :file
969
+ key_labels = [
970
+ ['c', 'Copy'], ['m', 'Move'],
971
+ ['d', 'Delete'], ['v', 'View'],
972
+ ['s', 'Select'], ['u', 'Unselect'],
973
+ ['p', 'Page'], ['x', 'Exec Cmd'],
974
+ ['r', 'ruby'], ['e', "Edit"],
975
+ ['C-c', 'Cancel']
976
+ ]
977
+ elsif categ == :view
978
+ key_labels = [
979
+ ['c', 'Date'], ['m', 'Size'],
980
+ ['d', 'Delete'], ['v', 'View'],
981
+ ['C-c', 'Cancel']
982
+ ]
983
+ elsif categ == :dir
984
+ key_labels = [
985
+ ['o', 'open'], ['O', 'Open in right'],
986
+ ['d', 'Delete'], ['R', 'Del Recurse'],
987
+ ['t', 'tree'], ['p', 'Previous'],
988
+ ['b', 'Bookmark'], ['u', 'Unbookmark'],
989
+ ['l', 'List'], ['s', 'Save'],
990
+ ['m', 'mkdir'], nil,
991
+ ['C-c', 'Cancel']
992
+ ]
993
+ end
994
+ return key_labels
995
+ end
996
+ def get_key_labels_table
997
+ key_labels = [
998
+ ['M-n','NewRow'], ['M-d','DelRow'],
999
+ ['C-x','Select'], nil,
1000
+ ['M-0', 'Top'], ['M-9', 'End'],
1001
+ ['C-p', 'PgUp'], ['C-n', 'PgDn'],
1002
+ ['M-Tab','Nxt Fld'], ['Tab','Nxt Col'],
1003
+ ['+','Widen'], ['-','Narrow']
1004
+ ]
1005
+ return key_labels
1006
+ end
1007
+ def sort_popup
1008
+ #mform = RubyCurses::Form.new nil
1009
+ mform = nil
1010
+ field_list = []
1011
+ r = 4
1012
+ $radio = RubyCurses::Variable.new
1013
+ rtextvalue = [:name, :ext, :size, :mtime, :atime]
1014
+ ["Name", "Extension", "Size", "Modify Time", "Access Time" ].each_with_index do |rtext,ix|
1015
+ field = RubyCurses::RadioButton.new mform do
1016
+ variable $radio
1017
+ text rtext
1018
+ value rtextvalue[ix]
1019
+ color 'black'
1020
+ bgcolor 'white'
1021
+ #row r
1022
+ #col 5
1023
+ end
1024
+ field_list << field
1025
+ r += 1
1026
+ end
1027
+ r = 4
1028
+ ["Reverse", "case sensitive"].each do |cbtext|
1029
+ field = RubyCurses::CheckBox.new mform do
1030
+ text cbtext
1031
+ name cbtext
1032
+ color 'black'
1033
+ bgcolor 'white'
1034
+ #row r
1035
+ #col 30
1036
+ end
1037
+ field_list << field
1038
+ r += 1
1039
+ end
1040
+ mb = RubyCurses::MessageBox.new :width => 50 do
1041
+ title "Sort Options"
1042
+ button_type :ok_cancel
1043
+ default_button 0
1044
+ end
1045
+ field_list.each { |e| mb.add(e) }
1046
+ index = mb.run
1047
+ if index == 0
1048
+ $log.debug " SORT POPUP #{$radio.value}"
1049
+ #$log.debug " SORT POPUP #{mb.inspect}"
1050
+ $log.debug " SORT POPUP #{mb.widget("Reverse").value}"
1051
+ $log.debug " SORT POPUP #{mb.widget("case sensitive").value}"
1052
+ end
1053
+ return index, $radio.value, mb.widget("Reverse").value, mb.widget("case sensitive").value
1054
+ end
1055
+ def filter
1056
+ f = get_string("Enter a filter pattern", :maxlen => 20, :default => "*")
1057
+ f = "*" if f.nil? or f == ""
1058
+ @current_list.filter_pattern = f
1059
+ @current_list.rescan
1060
+ end
1061
+
1062
+ # ask user for pattern to grep through files
1063
+ # and display file names in same list
1064
+ # Please see testmessagebox.rb for cleaner ways of creating messageboxes.
1065
+
1066
+ def grep_popup
1067
+ last_regex = @last_regex || ""
1068
+ last_pattern = @last_pattern || "*"
1069
+ r = 4
1070
+ mform = nil
1071
+ field = RubyCurses::Field.new mform do
1072
+ name "regex"
1073
+ #row r
1074
+ #col 30
1075
+ set_buffer last_regex
1076
+ label 'Regex'
1077
+ #set_label Label.new @form, {'text' => 'Regex', 'col'=>5, 'mnemonic'=> 'R'}
1078
+ end
1079
+ r += 1
1080
+ field1 = RubyCurses::Field.new mform do
1081
+ name "filepattern"
1082
+ #row r
1083
+ #col 30
1084
+ set_buffer last_pattern
1085
+ label 'File Pattern'
1086
+ #set_label Label.new @form, {'text' => 'File Pattern','col'=>5, :color=>'black',:bgcolor=>'white','mnemonic'=> 'F'}
1087
+ end
1088
+ r += 1
1089
+ fields = []
1090
+ ["Recurse", "case insensitive"].each do |cbtext|
1091
+ fields.push(RubyCurses::CheckBox.new(mform) do
1092
+ text cbtext
1093
+ name cbtext
1094
+ #color 'black'
1095
+ #bgcolor 'white'
1096
+ #row r
1097
+ #col 5
1098
+ end
1099
+ )
1100
+ r += 1
1101
+ end
1102
+ mb = RubyCurses::MessageBox.new :width => 50 do
1103
+ title "Grep Options"
1104
+ button_type :ok_cancel
1105
+ default_button 0
1106
+ item field
1107
+ item field1
1108
+ item fields.first
1109
+ item fields.last
1110
+ end
1111
+ selected_index = mb.run
1112
+ if selected_index == 0
1113
+ return if mb.form.by_name["regex"].getvalue()==""
1114
+ @last_regex = mb.form.by_name["regex"].getvalue
1115
+ inp = mb.form.by_name["regex"].getvalue
1116
+ fp = mb.form.by_name["filepattern"].getvalue
1117
+ @last_pattern = fp
1118
+ flags=""
1119
+ #flags << " -i " if mb.form.by_name["case insensitive"].value==true
1120
+ #flags << " -R " if mb.form.by_name["Recurse"].value==true
1121
+ flags << " -i " if fields[0].value==true
1122
+ flags << " -R " if fields[1].value==true
1123
+ # sometimes grep pukes "No such file or directory if you specify some
1124
+ # filespec that is not present. I don't want it to come in filestr
1125
+ # so i am not piping stderr to stdout
1126
+ cmd = "cd #{@current_list.cur_dir()};grep -l #{flags} #{inp} #{fp}"
1127
+ filestr = %x[ #{cmd} ]
1128
+ files = nil
1129
+ files = filestr.split(/\n/) unless filestr.nil?
1130
+ #view filestr
1131
+ if !files || files.empty?
1132
+ alert "Sorry! No files for regex #{inp}"
1133
+ return
1134
+ end
1135
+ @current_list.title "grep #{inp}"
1136
+ @current_list.populate files
1137
+ # Use backspace to go back
1138
+ end
1139
+ return selected_index, mb.form.by_name["regex"].getvalue, mb.form.by_name["filepattern"].getvalue, fields[1].value, fields[0].value
1140
+ end
1141
+
1142
+ def system_popup
1143
+ deflt = @last_system || ""
1144
+ options=["run in shell","view output","file explorer"]
1145
+ #inp = get_string("Enter a system command", 30, deflt)
1146
+
1147
+ mb = MessageBox.new :title => "System Command" , :width => 80 do
1148
+ add Field.new :label => 'Enter command', :name => "cmd", :display_length => 50, :bgcolor => :cyan,
1149
+ :text => deflt
1150
+ $radio = RubyCurses::Variable.new
1151
+ add RadioButton.new :text => "run in shell", :color => :blue, :variable => $radio
1152
+ add RadioButton.new :text => "view output", :color => :red, :variable => $radio
1153
+ add RadioButton.new :text => "file explorer", :color => :red, :variable => $radio
1154
+ button_type :ok_cancel
1155
+ end
1156
+ sel = mb.run
1157
+ inp = mb.widget("cmd").text
1158
+ $log.debug "XXX: value of radio is #{$radio.value}, inp : #{inp} "
1159
+ #sel, inp, hash = get_string_with_options("Enter a system command", 40, deflt, {"radiobuttons" => options, "radio_default"=>@last_system_radio || options[0]})
1160
+ if sel == 0
1161
+ if !inp.nil?
1162
+ @last_system = inp
1163
+ @last_system_radio = $radio.value
1164
+ case $radio.value
1165
+ when options[0]
1166
+ shell_out inp
1167
+ when options[1]
1168
+ filestr = %x[ #{inp} ]
1169
+ view filestr
1170
+ when options[2]
1171
+ filestr = %x[ #{inp} ]
1172
+ files = nil
1173
+ files = filestr.split(/\n/) unless filestr.nil?
1174
+ @current_list.title inp
1175
+ @current_list.populate files
1176
+ $log.debug " SYSTEM got #{files.size}, #{files.inspect}"
1177
+ else
1178
+ shell_out inp
1179
+ end
1180
+ end
1181
+ end
1182
+ end
1183
+ # This will notwork, dunno if its being called
1184
+ def popup
1185
+ deflt = @last_regexp || ""
1186
+ #sel, inp, hash = get_string_with_options("Enter a filter pattern", 20, "*", {"checkboxes" => ["case sensitive","reverse"], "checkbox_defaults"=>[true, false]})
1187
+ sel, inp, hash = get_string_with_options("Enter a grep pattern", 20, deflt, {"checkboxes" => ["case insensitive","not-including"]})
1188
+ if sel == 0
1189
+ @last_regexp = inp
1190
+ flags=""
1191
+ flags << " -i " if hash["case insensitive"]==true
1192
+ flags << " -v " if hash["not-including"]==true
1193
+ cmd = "grep -l #{flags} #{inp} *"
1194
+ filestr = %x[ #{cmd} ]
1195
+ files = nil
1196
+ files = filestr.split(/\n/) unless filestr.nil?
1197
+ view filestr
1198
+ end
1199
+ $log.debug " POPUP: #{sel}: #{inp}, #{hash['case sensitive']}, #{hash['reverse']}"
1200
+ end
1201
+ # This won't work if you do something like 'ls -l', you will need to do 'ls -l | less'
1202
+ def shell_out command
1203
+ @window.hide
1204
+ Ncurses.endwin
1205
+ ret = system command
1206
+ Ncurses.refresh
1207
+ #Ncurses.curs_set 0 # why ?
1208
+ @window.show
1209
+ ret
1210
+ end
1211
+ end
1212
+ if $0 == __FILE__
1213
+ include RubyCurses
1214
+ include RubyCurses::Utils
1215
+
1216
+ begin
1217
+ # Initialize curses
1218
+ VER::start_ncurses # this is initializing colors via ColorMap.setup
1219
+ #$log = Logger.new("rbc13.log")
1220
+ $log = Logger.new(ENV['LOGDIR'] || "" + "rbc13.log")
1221
+
1222
+ $log.level = Logger::DEBUG
1223
+
1224
+ catch(:close) do
1225
+ t = RFe.new
1226
+ t.draw_screens
1227
+ end
1228
+ rescue => ex
1229
+ ensure
1230
+ VER::stop_ncurses
1231
+ p ex if ex
1232
+ p(ex.backtrace.join("\n")) if ex
1233
+ $log.debug( ex) if ex
1234
+ $log.debug(ex.backtrace.join("\n")) if ex
1235
+ end
1236
+ end