canis 0.0.4

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