canis 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +45 -0
  3. data/CHANGES +52 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +24 -0
  7. data/Rakefile +2 -0
  8. data/canis.gemspec +25 -0
  9. data/examples/alpmenu.rb +46 -0
  10. data/examples/app.sample +19 -0
  11. data/examples/appemail.rb +191 -0
  12. data/examples/atree.rb +105 -0
  13. data/examples/bline.rb +181 -0
  14. data/examples/common/devel.rb +319 -0
  15. data/examples/common/file.rb +93 -0
  16. data/examples/data/README.markdown +9 -0
  17. data/examples/data/brew.txt +38 -0
  18. data/examples/data/color.2 +37 -0
  19. data/examples/data/gemlist.txt +59 -0
  20. data/examples/data/lotr.txt +12 -0
  21. data/examples/data/ports.txt +136 -0
  22. data/examples/data/table.txt +37 -0
  23. data/examples/data/tasks.csv +88 -0
  24. data/examples/data/tasks.txt +27 -0
  25. data/examples/data/todo.txt +16 -0
  26. data/examples/data/todocsv.csv +28 -0
  27. data/examples/data/unix1.txt +21 -0
  28. data/examples/data/unix2.txt +11 -0
  29. data/examples/dbdemo.rb +506 -0
  30. data/examples/dirtree.rb +177 -0
  31. data/examples/newtabbedwindow.rb +100 -0
  32. data/examples/newtesttabp.rb +92 -0
  33. data/examples/tabular.rb +212 -0
  34. data/examples/tasks.rb +179 -0
  35. data/examples/term2.rb +88 -0
  36. data/examples/testbuttons.rb +307 -0
  37. data/examples/testcombo.rb +102 -0
  38. data/examples/testdb.rb +182 -0
  39. data/examples/testfields.rb +208 -0
  40. data/examples/testflowlayout.rb +43 -0
  41. data/examples/testkeypress.rb +98 -0
  42. data/examples/testlistbox.rb +187 -0
  43. data/examples/testlistbox1.rb +199 -0
  44. data/examples/testmessagebox.rb +144 -0
  45. data/examples/testprogress.rb +116 -0
  46. data/examples/testree.rb +107 -0
  47. data/examples/testsplitlayout.rb +53 -0
  48. data/examples/testsplitlayout1.rb +49 -0
  49. data/examples/teststacklayout.rb +48 -0
  50. data/examples/testwsshortcuts.rb +68 -0
  51. data/examples/testwsshortcuts2.rb +129 -0
  52. data/lib/canis.rb +16 -0
  53. data/lib/canis/core/docs/index.txt +104 -0
  54. data/lib/canis/core/docs/list.txt +16 -0
  55. data/lib/canis/core/docs/style_help.yml +34 -0
  56. data/lib/canis/core/docs/tabbedpane.txt +15 -0
  57. data/lib/canis/core/docs/table.txt +31 -0
  58. data/lib/canis/core/docs/textpad.txt +48 -0
  59. data/lib/canis/core/docs/tree.txt +23 -0
  60. data/lib/canis/core/include/.DS_Store +0 -0
  61. data/lib/canis/core/include/action.rb +83 -0
  62. data/lib/canis/core/include/actionmanager.rb +49 -0
  63. data/lib/canis/core/include/appmethods.rb +179 -0
  64. data/lib/canis/core/include/bordertitle.rb +49 -0
  65. data/lib/canis/core/include/canisparser.rb +100 -0
  66. data/lib/canis/core/include/colorparser.rb +437 -0
  67. data/lib/canis/core/include/defaultfilerenderer.rb +64 -0
  68. data/lib/canis/core/include/io.rb +320 -0
  69. data/lib/canis/core/include/layouts/SplitLayout.rb +161 -0
  70. data/lib/canis/core/include/layouts/abstractlayout.rb +213 -0
  71. data/lib/canis/core/include/layouts/flowlayout.rb +104 -0
  72. data/lib/canis/core/include/layouts/stacklayout.rb +109 -0
  73. data/lib/canis/core/include/listbindings.rb +89 -0
  74. data/lib/canis/core/include/listeditable.rb +319 -0
  75. data/lib/canis/core/include/listoperations.rb +61 -0
  76. data/lib/canis/core/include/listselectionmodel.rb +388 -0
  77. data/lib/canis/core/include/multibuffer.rb +173 -0
  78. data/lib/canis/core/include/ractionevent.rb +73 -0
  79. data/lib/canis/core/include/rchangeevent.rb +27 -0
  80. data/lib/canis/core/include/rhistory.rb +95 -0
  81. data/lib/canis/core/include/rinputdataevent.rb +47 -0
  82. data/lib/canis/core/include/textdocument.rb +111 -0
  83. data/lib/canis/core/include/vieditable.rb +175 -0
  84. data/lib/canis/core/include/widgetmenu.rb +66 -0
  85. data/lib/canis/core/system/colormap.rb +165 -0
  86. data/lib/canis/core/system/keydefs.rb +32 -0
  87. data/lib/canis/core/system/ncurses.rb +237 -0
  88. data/lib/canis/core/system/panel.rb +129 -0
  89. data/lib/canis/core/system/window.rb +1081 -0
  90. data/lib/canis/core/util/ansiparser.rb +119 -0
  91. data/lib/canis/core/util/app.rb +696 -0
  92. data/lib/canis/core/util/basestack.rb +412 -0
  93. data/lib/canis/core/util/defaultcolorparser.rb +84 -0
  94. data/lib/canis/core/util/extras/README +5 -0
  95. data/lib/canis/core/util/extras/bottomline.rb +1815 -0
  96. data/lib/canis/core/util/extras/padreader.rb +192 -0
  97. data/lib/canis/core/util/focusmanager.rb +31 -0
  98. data/lib/canis/core/util/helpmanager.rb +160 -0
  99. data/lib/canis/core/util/oldwidgetshortcuts.rb +304 -0
  100. data/lib/canis/core/util/promptmenu.rb +235 -0
  101. data/lib/canis/core/util/rcommandwindow.rb +933 -0
  102. data/lib/canis/core/util/rdialogs.rb +520 -0
  103. data/lib/canis/core/util/textutils.rb +74 -0
  104. data/lib/canis/core/util/viewer.rb +238 -0
  105. data/lib/canis/core/util/widgetshortcuts.rb +508 -0
  106. data/lib/canis/core/widgets/applicationheader.rb +103 -0
  107. data/lib/canis/core/widgets/box.rb +58 -0
  108. data/lib/canis/core/widgets/divider.rb +310 -0
  109. data/lib/canis/core/widgets/extras/README.md +12 -0
  110. data/lib/canis/core/widgets/extras/rtextarea.rb +960 -0
  111. data/lib/canis/core/widgets/extras/stackflow.rb +474 -0
  112. data/lib/canis/core/widgets/keylabelprinter.rb +194 -0
  113. data/lib/canis/core/widgets/listbox.rb +326 -0
  114. data/lib/canis/core/widgets/listfooter.rb +86 -0
  115. data/lib/canis/core/widgets/rcombo.rb +210 -0
  116. data/lib/canis/core/widgets/rcontainer.rb +415 -0
  117. data/lib/canis/core/widgets/rlink.rb +30 -0
  118. data/lib/canis/core/widgets/rmenu.rb +970 -0
  119. data/lib/canis/core/widgets/rmenulink.rb +30 -0
  120. data/lib/canis/core/widgets/rmessagebox.rb +400 -0
  121. data/lib/canis/core/widgets/rprogress.rb +118 -0
  122. data/lib/canis/core/widgets/rtabbedpane.rb +631 -0
  123. data/lib/canis/core/widgets/rtabbedwindow.rb +70 -0
  124. data/lib/canis/core/widgets/rwidget.rb +3634 -0
  125. data/lib/canis/core/widgets/scrollbar.rb +147 -0
  126. data/lib/canis/core/widgets/statusline.rb +113 -0
  127. data/lib/canis/core/widgets/table.rb +1072 -0
  128. data/lib/canis/core/widgets/tabular.rb +264 -0
  129. data/lib/canis/core/widgets/textpad.rb +1674 -0
  130. data/lib/canis/core/widgets/tree.rb +690 -0
  131. data/lib/canis/core/widgets/tree/treecellrenderer.rb +150 -0
  132. data/lib/canis/core/widgets/tree/treemodel.rb +432 -0
  133. data/lib/canis/version.rb +3 -0
  134. metadata +229 -0
@@ -0,0 +1,192 @@
1
+ =begin
2
+ * Name: PadReader.rb
3
+ * Description : This is an independent file viewer that uses a Pad and traps keys
4
+ * Author: jkepler (http://github.com/mare-imbrium/canis/)
5
+ * Date: 22.10.11 - 20:35
6
+ * License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
7
+ * Last update: 2013-04-01 13:43
8
+
9
+ == CHANGES
10
+ == TODO
11
+ make the window configurable so we can move to a textview that is pad based, later even list ?
12
+ Note that cursor does not move, in real life applicatino cursor must move to bottom row
13
+ and only then scrolling should start.
14
+ NOTE: I have continued this in textpad which is a widget that uses pads to scroll.
15
+ This is very rough, i may work on this more later.
16
+ =end
17
+ require 'canis'
18
+
19
+ include Canis
20
+ module Canis
21
+ class PadReader
22
+
23
+ # You may pass height, width, row and col for creating a window otherwise a fullscreen window
24
+ # will be created. If you pass a window from caller then that window will be used.
25
+ # Some keys are trapped, jkhl space, pgup, pgdown, end, home, t b
26
+ # This is currently very minimal and was created to get me started to integrating
27
+ # pads into other classes such as textview.
28
+ def initialize config={}, &block
29
+
30
+ @config = config
31
+ @rows = FFI::NCurses.LINES-1
32
+ @cols = FFI::NCurses.COLS-1
33
+ @prow = @pcol = 0
34
+ @startrow = 0
35
+ @startcol = 0
36
+
37
+ h = config.fetch(:height, 0)
38
+ w = config.fetch(:width, 0)
39
+ t = config.fetch(:row, 0)
40
+ l = config.fetch(:col, 0)
41
+ @rows = h unless h == 0
42
+ @cols = w unless w == 0
43
+ @startrow = t unless t == 0
44
+ @startcol = l unless l == 0
45
+ @suppress_border = config[:suppress_border]
46
+ unless @suppress_border
47
+ @startrow += 1
48
+ @startcol += 1
49
+ @rows -=3 # 3 is since print_border_only reduces one from width, to check whether this is correct
50
+ @cols -=3
51
+ end
52
+ @top = t
53
+ @left = l
54
+ view_file config[:filename]
55
+ @window = config[:window] || Canis::Window.new(:height => h, :width => w, :top => t, :left => l)
56
+ # print border reduces on from width for some historical reason
57
+ @window.print_border_only @top, @left, h-1, w, $datacolor
58
+ @ph = @content_rows
59
+ @pw = @content_cols # get max col
60
+ @pad = FFI::NCurses.newpad(@ph, @pw)
61
+
62
+ Ncurses::Panel.update_panels
63
+ @content.each_index { |ix|
64
+
65
+ FFI::NCurses.mvwaddstr(@pad,ix, 0, @content[ix])
66
+ }
67
+ @window.wrefresh
68
+ padrefresh
69
+ #FFI::NCurses.prefresh(@pad, 0,0, @startrow ,@startcol, @rows,@cols);
70
+
71
+ @window.bkgd(Ncurses.COLOR_PAIR(5));
72
+ FFI::NCurses.keypad(@pad, true);
73
+ #@form = Form.new @window
74
+ config[:row] = config[:col] = 0 # ??? XXX
75
+ end
76
+
77
+ private
78
+ def view_file(filename)
79
+ @file = filename
80
+ @content = File.open(filename,"r").readlines
81
+ @content_rows = @content.count
82
+ @content_cols = content_cols()
83
+ #run()
84
+ end
85
+ # write pad onto window
86
+ private
87
+ def padrefresh
88
+ FFI::NCurses.prefresh(@pad,@prow,@pcol, @startrow,@startcol, @rows + @startrow,@cols+@startcol);
89
+ end
90
+ # returns button index
91
+ # Call this after instantiating the window
92
+ public
93
+ def run
94
+ #@form.repaint
95
+ #@window.wrefresh
96
+ return handle_keys
97
+ end
98
+
99
+ # convenience method
100
+ private
101
+ def key x
102
+ x.getbyte(0)
103
+ end
104
+ def content_cols
105
+ longest = @content.max_by(&:length)
106
+ longest.length
107
+ end
108
+
109
+ # returns button index
110
+ private
111
+ def handle_keys
112
+ ht = @window.height.ifzero FFI::NCurses.LINES-1
113
+ buttonindex = catch(:close) do
114
+ @maxrow = @content_rows - @rows
115
+ @maxcol = @content_cols - @cols
116
+ while((ch = @window.getchar()) != FFI::NCurses::KEY_F10 )
117
+ #while((ch = FFI::NCurses.wgetch(@pad)) != FFI::NCurses::KEY_F10 )
118
+ break if ch == ?\C-q.getbyte(0)
119
+ begin
120
+ case ch
121
+ when key(?g), 279 # home as per iterm2
122
+ @prow = 0
123
+ when key(?b), key(?G), 277 # end as per iterm2
124
+ @prow = @maxrow-1
125
+ when key(?j)
126
+ @prow += 1
127
+ when key(?k)
128
+ @prow -= 1
129
+ when 32, 338 # Page Down abd Page Up as per iTerm2
130
+ @prow += 10
131
+ when key(?\C-d)
132
+ @prow += ht
133
+ when key(?\C-b)
134
+ @prow -= ht
135
+ when 339
136
+ @prow -= 10
137
+ when key(?l)
138
+ @pcol += 1
139
+ when key(?$)
140
+ @pcol = @maxcol - 1
141
+ when key(?h)
142
+ @pcol -= 1
143
+ when key(?0)
144
+ @pcol = 0
145
+ when key(?q)
146
+ throw :close
147
+ else
148
+ alert " #{ch} not mapped "
149
+ end
150
+ @prow = 0 if @prow < 0
151
+ @pcol = 0 if @pcol < 0
152
+ if @prow > @maxrow-1
153
+ @prow = @maxrow-1
154
+ end
155
+ if @pcol > @maxcol-1
156
+ @pcol = @maxcol-1
157
+ end
158
+ #@window.wclear
159
+ #FFI::NCurses.prefresh(@pad,@prow,@pcol, @startrow,0, @rows,@cols);
160
+ padrefresh
161
+ Ncurses::Panel.update_panels
162
+ #@form.handle_key(ch)
163
+ #@window.wrefresh
164
+ rescue => err
165
+ $log.debug( err) if err
166
+ $log.debug(err.backtrace.join("\n")) if err
167
+
168
+ textdialog ["Error in padreader: #{err} ", *err.backtrace], :title => "Exception"
169
+ $error_message.value = ""
170
+ ensure
171
+ end
172
+
173
+ end # while loop
174
+ end # close
175
+ $log.debug "XXX: CALLER GOT #{buttonindex} "
176
+ @window.destroy unless @config[:window]
177
+ FFI::NCurses.delwin(@pad)
178
+ return buttonindex
179
+ end
180
+ end
181
+ end
182
+ if __FILE__ == $PROGRAM_NAME
183
+ require 'canis/core/util/app'
184
+ App.new do
185
+ #status_line :row => FFI::NCurses.LINES-1
186
+ @form.repaint
187
+ #p = PadReader.new :filename => "padreader.rb", :height => 20, :width => 60, :row => 4, :col => 4, :window => @window, :suppress_border => true
188
+ p = PadReader.new :filename => "padreader.rb", :height => FFI::NCurses.LINES-1, :width => 0, :row => 0, :col => 0, :window => @window, :suppress_border => true
189
+ p.run
190
+ throw :close
191
+ end
192
+ end
@@ -0,0 +1,31 @@
1
+ # Allow some objects to take focus when a certain key is pressed.
2
+ # This is for objects like scrollbars and grabbars. We don't want these always
3
+ # getting focus, only sometimes when we want to resize panes.
4
+ # This will not only be included by Form but by containers such as Vimsplit
5
+ # or MasterDetail.
6
+ # Usage: the idea is that when you create grabbars, you would add them to the FocusManager
7
+ # Thus they would remain non-focusable on creation. When hte user presses (say F3) then
8
+ # make_focusable is called, or toggle_focusable. Now user can press TAB and access
9
+ # these bars. When he is done he can toggle again.
10
+ # TODO: we might add a Circular class here so user can traverse only these objects
11
+ module Canis
12
+ module FocusManager
13
+ extend self
14
+ attr_reader :focusables
15
+ # add a component to this list so it can be made focusable later
16
+ def add component
17
+ @focusables ||= []
18
+ @focusables << component
19
+ self
20
+ end
21
+ def make_focusable bool=true
22
+ @focusing = bool
23
+ @focusables.each { |e| e.focusable(bool) }
24
+ end
25
+ def toggle_focusable
26
+ return unless @focusables
27
+ alert "FocusManager Making #{@focusables.length} objects #{!@focusing} "
28
+ make_focusable !@focusing
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,160 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: helpmanager.rb
3
+ # Description: manages display of help text and hyperlinking with other files in doc dir.
4
+ # Author: j kepler http://github.com/mare-imbrium/canis/
5
+ # Date: 2014-07-08 - 20:59
6
+ # License: MIT
7
+ # Last update: 2014-07-08 21:07
8
+ # ----------------------------------------------------------------------------- #
9
+ # helpmanager.rb Copyright (C) 2012-2014 j kepler
10
+ # TODO
11
+ # - the method display_help is huge and a mess. That part needs to be a class.
12
+ module Canis
13
+ CANIS_DOCPATH = File.dirname(File.dirname(__FILE__)) + "/docs/"
14
+
15
+ # manages the help file of an application and the inbuilt help the application provides
16
+ # for the widgets.
17
+ class HelpManager # {{{
18
+ def initialize form, config={}, &block
19
+ @form = form
20
+ #super
21
+ #instance_eval &block if block_given?
22
+ end
23
+ def help_text text=nil
24
+ if text
25
+ @help_text = text
26
+ end
27
+ return @help_text
28
+ end
29
+ # Assign help text to variable
30
+ # @param [String] help text is a string with newlines, or an Array. Will be split if String.
31
+ # May use markup for help files which is a very limited subset of markdown.
32
+ def help_text=(text); help_text(text); end
33
+
34
+ # Displays help provided by program. If no program is specified, then default help
35
+ # is displayed. If help was provided, then default help is also displayed on next page
36
+ # after program's help
37
+ def display_help
38
+ require 'canis/core/util/textutils'
39
+ filename = CANIS_DOCPATH + "index.txt"
40
+ stylesheet = CANIS_DOCPATH + "style_help.yml"
41
+ # defarr contains default help
42
+ if File.exists?(filename)
43
+ defarr = File.open(filename,'r').read.split("\n")
44
+ # convert help file into styles for use by tmux
45
+ # quick dirty converter for the moment
46
+ defarr = Canis::TextUtils::help2tmux defarr
47
+ else
48
+ arr = []
49
+ arr << " Could not find help file for application "
50
+ arr << " "
51
+ arr << "Most applications provide the following keys, unless overriden:"
52
+ arr << " "
53
+ arr << " F10 - exit application "
54
+ arr << " C-q - exit application "
55
+ arr << " ? (or M-?) - current widget key bindings "
56
+ arr << " "
57
+ arr << " Alt-x - select commands "
58
+ arr << " : (or M-:) - select commands "
59
+ arr << " "
60
+ defarr = arr
61
+ end
62
+ defhelp = true
63
+ if @help_text
64
+ defhelp = false
65
+ arr = @help_text
66
+ arr = arr.split("\n") if arr.is_a? String
67
+ arr = Canis::TextUtils::help2tmux arr # FIXME can this happen automatically if it is help format
68
+ else
69
+ arr = defarr
70
+ end
71
+ #w = arr.max_by(&:length).length
72
+ h = FFI::NCurses.LINES - 4
73
+ w = FFI::NCurses.COLS - 10
74
+ wbkgd = get_color($reversecolor, :black, :cyan)
75
+
76
+ require 'canis/core/util/viewer'
77
+ # this was the old layout that centered with a border, but was a slight bit confusing since the bg was the same
78
+ # as the lower window.
79
+ _layout = [h, w, 2, 4]
80
+ sh = Ncurses.LINES-1
81
+ sc = Ncurses.COLS-0
82
+ # this is the new layout that is much like bline's command list. no border, a thick app header on top
83
+ # and no side margin
84
+ # Suppressing border means that title will not be updated on app_header, we have to do so FIXME
85
+ _layout = [ h, sc, sh - h, 0]
86
+ doc = TextDocument.new :text => arr, :content_type => :tmux, :stylesheet => stylesheet
87
+ Canis::Viewer.view(doc, :layout => _layout, :close_key => KEY_F10, :title => "[ Help ]", :print_footer => true,
88
+ :app_header => true ) do |t, items|
89
+ # would have liked it to be 'md' or :help
90
+ #t.content_type = :tmux
91
+ #t.stylesheet = stylesheet
92
+ t.suppress_borders = true
93
+ t.print_footer = false
94
+ t.bgcolor = :black
95
+ t.bgcolor = 16
96
+ t.color = :white
97
+ ah = items[:header]
98
+ t.bind(:PROPERTY_CHANGE) { |eve|
99
+ # title is not a property, so we check if text has changed and then look for title.
100
+ if eve.property_name == :text
101
+ #$log.debug " PROP NAME IS #{eve.property_name} , title is #{t.title} "
102
+ ah.text_center = t.title
103
+ end
104
+ }
105
+ #t.text_patterns[:link] = Regexp.new(/\[[^\]]\]/)
106
+ t.text_patterns[:link] = Regexp.new(/\[\w+\]/)
107
+ t.bind_key(KEY_TAB, "goto link") { t.next_regex(:link) }
108
+ # FIXME bgcolor add only works if numberm not symbol
109
+ t.bind_key(?a, "increment bgcolor") { t.bgcolor += 1 ; t.bgcolor = 1 if t.bgcolor > 256;
110
+ $log.debug " HELP BGCOLOR is #{t.bgcolor} ";
111
+ t.clear_pad; t.render_all }
112
+ t.bind(:PRESS){|eve|
113
+ link = nil
114
+ s = eve.word_under_cursor
115
+ if is_link?(t, s)
116
+ link = get_link(t, s)
117
+ end
118
+ #alert "word under cursor is #{eve.word_under_cursor}, link is #{link}"
119
+ if link
120
+ arr = read_help_file link
121
+ if arr
122
+ doc = TextDocument.new :text => arr, :content_type => :tmux, :stylesheet => stylesheet, :title => link
123
+ #t.add_content arr, :title => link
124
+ t.add_content doc
125
+ #items[:header].text_center = "[#{link}]"
126
+ t.buffer_last
127
+ else
128
+ alert "No help file for #{link}"
129
+ end
130
+ else
131
+ end
132
+ }
133
+
134
+ # help was provided, so default help is provided in second buffer
135
+ unless defhelp
136
+ doc = TextDocument.new :text => defarr, :content_type => :tmux, :stylesheet => stylesheet, :title => " General Help "
137
+ #t.add_content defarr, :title => ' General Help ', :stylesheet => stylesheet, :content_type => :tmux
138
+ t.add_content doc
139
+ end
140
+ end
141
+ end
142
+ def is_link? t, s
143
+ s.index(t.text_patterns[:link]) >= 0
144
+ end
145
+ def get_link t, s
146
+ s.match(t.text_patterns[:link])[0].gsub!(/[\[\]]/,"")
147
+ end
148
+ def read_help_file link
149
+ filename = CANIS_DOCPATH + "#{link}.txt"
150
+ defarr = nil
151
+ # defarr contains default help
152
+ if File.exists?(filename)
153
+ defarr = File.open(filename,'r').read.split("\n")
154
+ # convert help file into styles for use by tmux
155
+ # quick dirty converter for the moment
156
+ defarr = Canis::TextUtils::help2tmux defarr
157
+ end
158
+ end
159
+ end # class }}}
160
+ end
@@ -0,0 +1,304 @@
1
+ # ------------------------------------------------------------ #
2
+ # File: widgetshortcuts.rb
3
+ # Description: A common module for shortcuts to create widgets
4
+ # Also, stacks and flows objects
5
+ # Author: jkepler http://github.com/mare-imbrium/canis/
6
+ # Date: 05.11.11 - 15:13
7
+ # Last update: 06.11.11 - 10:57
8
+ # == TODO
9
+ # add multirow comps like textview and textarea, list
10
+ # add blocks that make sense like in app
11
+ # - what if user does not want form attached - app uses useform ot
12
+ # to check for this, if current_object don't add form
13
+ #
14
+ # - usage of _position inside means these shortcuts cannot be reused
15
+ # with other positioning systems, we'll be cut-pasting forever
16
+ #
17
+ # == CHANGES
18
+ # ------------------------------------------------------------ #
19
+ #
20
+
21
+ # what is the real purpose of the shortcuts, is it to avoid putting nil
22
+ # for form there if not required.
23
+ # Or is it positioning, such as in a stack. or just a method ?
24
+ require 'canis/core/widgets/rlist'
25
+ require 'canis/core/widgets/rtextview'
26
+ module Canis
27
+ module WidgetShortcuts
28
+ class Ws
29
+ attr_reader :config
30
+ def initialize config={}
31
+ @config = config
32
+ end
33
+ def [](sym)
34
+ @config[sym]
35
+ end
36
+ def []=(sym, val)
37
+ @config[sym] = val
38
+ end
39
+ end
40
+ class WsStack < Ws; end
41
+ class WsFlow < Ws; end
42
+ def widget_shortcuts_init
43
+ @_ws_app_row = @_ws_app_col = 0
44
+ @_ws_active = []
45
+ @_ws_components = []
46
+ @variables = {}
47
+ end
48
+ def field config={}, &block
49
+ w = Field.new nil, config #, &block
50
+ _position w
51
+ if block
52
+ w.bind(:CHANGED, &block)
53
+ end
54
+ return w
55
+ end
56
+ def label config={}, &block
57
+ w = Label.new nil, config, &block
58
+ _position w
59
+ return w
60
+ end
61
+ def blank
62
+ label :text => ""
63
+ end
64
+ def line config={}
65
+ #horizontal line TODO
66
+ #row = config[:row] || @app_row
67
+ #width = config[:width] || 20
68
+ #_position config
69
+ #col = config[:col] || 1
70
+ #@color_pair = config[:color_pair] || $datacolor
71
+ #@attrib = config[:attrib] || Ncurses::A_NORMAL
72
+ #@window.attron(Ncurses.COLOR_PAIR(@color_pair) | @attrib)
73
+ #@window.mvwhline( row, col, FFI::NCurses::ACS_HLINE, width)
74
+ #@window.attron(Ncurses.COLOR_PAIR(@color_pair) | @attrib)
75
+ end
76
+ def check config={}, &block
77
+ w = CheckBox.new nil, config #, &block
78
+ _position w
79
+ if block
80
+ w.bind(:PRESS, &block)
81
+ end
82
+ return w
83
+ end
84
+ def button config={}, &block
85
+ w = Button.new nil, config #, &block
86
+ _position w
87
+ if block
88
+ w.bind(:PRESS, &block)
89
+ end
90
+ return w
91
+ end
92
+ def radio config={}, &block
93
+ a = config[:group]
94
+ # should we not check for a nil
95
+ if @variables.has_key? a
96
+ v = @variables[a]
97
+ else
98
+ v = Variable.new
99
+ @variables[a] = v
100
+ end
101
+ config[:variable] = v
102
+ config.delete(:group)
103
+ w = RadioButton.new nil, config #, &block
104
+ _position w
105
+ if block
106
+ w.bind(:PRESS, &block)
107
+ end
108
+ return w
109
+ end
110
+ # editable text area
111
+ def textarea config={}, &block
112
+ require 'canis/rtextarea'
113
+ # TODO confirm events many more
114
+ events = [ :CHANGE, :LEAVE, :ENTER ]
115
+ block_event = events[0]
116
+ #_process_args args, config, block_event, events
117
+ #config[:width] = config[:display_length] unless config.has_key? :width
118
+ # if no width given, expand to flows width
119
+ #config[:width] ||= @stack.last.width if @stack.last
120
+ useform = nil
121
+ #useform = @form if @current_object.empty?
122
+ w = TextArea.new useform, config
123
+ w.width = :expand unless w.width
124
+ w.height ||= 8 # TODO
125
+ _position(w)
126
+ # need to expand to stack's width or flows itemwidth if given
127
+ if block
128
+ w.bind(block_event, &block)
129
+ end
130
+ return w
131
+ end
132
+ def textview config={}, &block
133
+ events = [ :LEAVE, :ENTER ]
134
+ block_event = events[0]
135
+ #_process_args args, config, block_event, events
136
+ #config[:width] = config[:display_length] unless config.has_key? :width
137
+ # if no width given, expand to flows width
138
+ #config[:width] ||= @stack.last.width if @stack.last
139
+ useform = nil
140
+ #useform = @form if @current_object.empty?
141
+ w = TextView.new useform, config
142
+ w.width = :expand unless w.width
143
+ w.height ||= 8 # TODO
144
+ _position(w)
145
+ # need to expand to stack's width or flows itemwidth if given
146
+ if block
147
+ w.bind(block_event, &block)
148
+ end
149
+ return w
150
+ end
151
+ def _position w
152
+ cur = @_ws_active.last
153
+ # this is outside any stack or flow, so we do the minimal
154
+ # user should specify row and col
155
+ unless cur
156
+ w.row ||= 0
157
+ w.col ||= 0
158
+ $log.debug "XXX: LABEL #{w.row} , #{w.col} "
159
+ w.set_form @form if @form # temporary,, only set if not inside an object FIXME
160
+ if w.width == :expand
161
+ w.width = FFI::NCurses.COLS-0 # or take windows width since this could be in a message box
162
+ end
163
+ if w.height == :expand
164
+ # take from current row, and not zero FIXME
165
+ w.height = FFI::NCurses.LINES-0 # or take windows width since this could be in a message box
166
+ end
167
+ return
168
+ end
169
+ r = cur[:row] || 0
170
+ c = cur[:col] || 0
171
+ w.row = r
172
+ w.col = c
173
+ if cur.is_a? WsStack
174
+ r += w.height || 1
175
+ cur[:row] = r
176
+ else
177
+ wid = cur[:item_width] || w.width || 10
178
+ c += wid + 1
179
+ cur[:col] = c
180
+ end
181
+ if w.width == :expand
182
+ w.width = cur[:width] or raise "Width not known for stack"
183
+ end
184
+ if w.height == :expand
185
+ w.height = cur[:height] or raise "height not known for flow"
186
+ end
187
+ w.color ||= cur[:color]
188
+ w.bgcolor ||= cur[:bgcolor]
189
+ w.set_form @form if @form # temporary
190
+ @_ws_components << w
191
+ cur[:components] << w
192
+ end
193
+ # make it as simple as possible, don't try to be intelligent or
194
+ # clever, put as much on the user
195
+ def stack config={}, &block
196
+ s = WsStack.new config
197
+ _configure s
198
+ @_ws_active << s
199
+ yield_or_eval &block if block_given?
200
+ @_ws_active.pop
201
+
202
+ # ---- stack is finished now
203
+ last = @_ws_active.last
204
+ if last
205
+ case last
206
+ when WsStack
207
+ when WsFlow
208
+ last[:col] += last[:item_width] || 0
209
+ # this tries to set height of outer flow based on highest row
210
+ # printed, however that does not account for height of object,
211
+ # so user should give a height to the flow.
212
+ last[:height] = s[:row] if s[:row] > (last[:height]||0)
213
+ $log.debug "XXX: STACK setting col to #{s[:col]} "
214
+ end
215
+ end
216
+
217
+ end
218
+ #
219
+ # item_width - width to use per item
220
+ # but the item width may apply to stacks inside not to items
221
+ def flow config={}, &block
222
+ s = WsFlow.new config
223
+ _configure s
224
+ @_ws_active << s
225
+ yield_or_eval &block if block_given?
226
+ @_ws_active.pop
227
+ last = @_ws_active.last
228
+ if last
229
+ case last
230
+ when WsStack
231
+ if s[:height]
232
+ last[:row] += s[:height]
233
+ else
234
+ #last[:row] += last[:highest_row]
235
+ last[:row] += 1
236
+ end
237
+ when WsFlow
238
+ last[:col] += last[:item_width] || 0
239
+ end
240
+ end
241
+ end
242
+ # flow and stack could have a border option
243
+ def box config={}, &block
244
+ require 'canis/core/widgets/box'
245
+ # take current stacks row and col
246
+ # advance row by one and col by one
247
+ # at end note row and advance by one
248
+ # draw a box around using these coordinates. width should be
249
+ # provided unless we have item width or something.
250
+ last = @_ws_active.last
251
+ if last
252
+ r = last[:row]
253
+ c = last[:col]
254
+ config[:row] = r
255
+ config[:col] = c
256
+ last[:row] += config[:margin_top] || 1
257
+ last[:col] += config[:margin_left] || 1
258
+ _box = Box.new @form, config # needs to be created first or will overwrite area after others painted
259
+ yield_or_eval &block if block_given?
260
+ h = config[:height] || last[:height] || (last[:row] - r)
261
+ h = 2 if h < 2
262
+ w = config[:width] || last[:width] || 15 # tmp
263
+ case last
264
+ when WsFlow
265
+ w = last[:col]
266
+ when WsStack
267
+ #h += 1
268
+ end
269
+ config[:row] = r
270
+ config[:col] = c
271
+ config[:height] = h
272
+ config[:width] = w
273
+ _box.row r
274
+ _box.col c
275
+ _box.height h
276
+ _box.width w
277
+ last[:row] += 1
278
+ last[:col] += 1 # ??? XXX if flow we need to increment properly or not ?
279
+ end
280
+ end
281
+ def _configure s
282
+ s[:row] ||= 0
283
+ s[:col] ||= 0
284
+ s[:row] += (s[:margin_top] || 0)
285
+ s[:col] += (s[:margin_left] || 0)
286
+ s[:width] = FFI::NCurses.COLS if s[:width] == :expand
287
+ last = @_ws_active.last
288
+ if last
289
+ if last.is_a? WsStack
290
+ s[:row] += (last[:row] || 0)
291
+ s[:col] += (last[:col] || 0)
292
+ else
293
+ s[:row] += (last[:row] || 0)
294
+ s[:col] += (last[:col] || 0) # we are updating with item_width as each st finishes
295
+ s[:width] ||= last[:item_width] #
296
+ end
297
+ end
298
+ s[:components] = []
299
+ end
300
+
301
+
302
+
303
+ end
304
+ end