canis 0.0.5 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d5a775dee6fbf1b0f9bc371c1e11ec5822241396
4
- data.tar.gz: ac9799e0d5419870a54b30f388b0e56984def45e
3
+ metadata.gz: f94ffa2e37387dc50fb96d5f40cbd6b97cd60143
4
+ data.tar.gz: 2323ea61ad5c4403628a6932665aed0a5ab7901d
5
5
  SHA512:
6
- metadata.gz: f261dc96bb4b87476193966e16cbf8c417de1cd49f4b4e99a173837045ea527dc6e503916c06a868c2e9598fa981c30eaf7f749b58c7d730e5c4e2c06fb1aa3a
7
- data.tar.gz: 992fe5d6546ece188d6b5599a03d94b6f3b35e86fb40e878dcda350107a389f51edd31929a522546a3d3ac6fd26ea263d194f766eb64fe14aba61be2f96f956b
6
+ metadata.gz: 981c442662ac2c991dde00187fa1dc2210c5ce3a901a3d0d249599c08b56effc13929c1ae14b373c91f828a28b50c5b8d4664c9e338b3a710bc3b8342fe1f1be
7
+ data.tar.gz: 943dab012e46633bfe16fc35bab8dfad406abd700dae2bd4d97a44a22bff4ecfcd61e959cc99d552a1795a1c4e266300f412de3a177721b14bbe9693a1a451bf
data/CHANGES CHANGED
@@ -55,3 +55,13 @@ Built 0.0.2 but i did not release it.
55
55
  - for 0.0.5
56
56
  - fixed a bug in handling of multiple key assignments due to which extra keys pushed onto stack
57
57
  - App keyblock yields string of key without changing or converting to symbol
58
+
59
+ 2014-09-01
60
+ - for 0.0.6
61
+ - Statusline location defaults to -1 from bottom, earlier -3
62
+ - Statusline attrib was A_REVERSE earlier, now defaults to A_NORMAL.
63
+ - bugs and omissions in parse_format, and in DefaultColorParser fixed.
64
+
65
+ 2014-09-02
66
+ - for 0.0.7
67
+ rdialog.rb introduced a syntax error while adding names to windows, had to yank 0.0.6
data/README.md CHANGED
@@ -1,9 +1,12 @@
1
1
  # Canis
2
2
 
3
- ncurses library for ruby with essential components/controls only
3
+ wrapper over ruby ffi-ncurses library with essential components/controls only.
4
4
 
5
- Things are looking better now. Textpad seems to be more or less stable as far
6
- as changes are concerned
5
+ Canis has taken over the codebase of rbcurse. Canis is _not_ backward compatible with rbcurse.
6
+ Canis tries to simplify and refactor rbcurse. Canis also tries to standardize without causing too much change.
7
+
8
+ Applications that worked with rbcurse can be moved over to canis with some minor rework. An example is the `ri`
9
+ document reader, `rigel` which is based on the code of `ribhu` (which was rbcurse based).
7
10
 
8
11
  ## Installation
9
12
 
@@ -11,12 +14,104 @@ as changes are concerned
11
14
 
12
15
  ## Usage
13
16
 
17
+ Until, we have better documentation, the best way to understand "canis" is to run the examples in the examples
18
+ folder. This gives a good idea of what the library does. Now you can open the source and get a feel of the
19
+ structure of the code and what to study. One can also select an example that is close to the application one has in mind
20
+ and use the source as a starting point.
21
+
22
+ That said, all applications will use a `Window`, typically a `root_window` which covers the entire screen.
23
+ A Window contains a `Form`, which manages all the widgets or controls inside it. It manages traversal, as well
24
+ as calling events on them and setting their state.
25
+ So you may be interested in reading up on the controls you need such as a `Listbox` or a `Textpad` (a multiline readonly
26
+ textarea). Pay attention to the events that they handle such as row_selection, or entering and leaving a row, etc.
27
+
28
+ Of interest is also the `App` class which wraps some basic boilerplate code such as starting and shutting ncurses, setting
29
+ colors, and creating a root_window and its form.
30
+
31
+ Each widget has a large number of properties which can be changed at any time. Unfortunately these may not show up
32
+ in the generated documentation since they are not created using `attr_accessor`. It is necessary for a widget to be repainted whenever
33
+ a property is changed, thus `dsl_property` has been used in place of `attr_accessor`.
34
+
35
+ Canis (from rbcurse) provides all the small utilities you need for user interfaces such as dialogs to get a string from the user, confirmation dialogs, dialogs to display running text or exceptions, alerts, statuslines (like vim), an application header (like alpine). dialogs to select one or more rows from a list, menus etc. It borrows features from other text applications such as vim or emacs such as multiple key mappings (e.g., 'gg'). All multiline widgets have vim keybindings for traversal including numeric prefixes.
36
+
37
+ There are routines for accessing the OS, such as shelling out to the shell, or running a shell command and seeing the results in a Viewer, or editing a file externally in your EDITOR. These can be seen in the examples.
38
+
39
+ The key F1, provides access to a help system which explains the keys related to all the widgets. An application can and should add its help to this.
40
+
41
+
14
42
  TODO: Write usage instructions here
15
43
 
16
- Textpad is more or less frozen. I have also simplified Field a bit.
44
+ Commonly used widgets and features:
45
+
46
+ - Textpad - to display non-editable multiline text. Text can be colored.
47
+ - Listbox - identical to the textpad except that it allows single or multiple selection, and has
48
+ some extra events such as entering and leaving or row, and selection.
49
+ - Field - user entry of a single line of data.
50
+ - Label - readonly text
51
+ - Button - action oriented widget
52
+
53
+ Some optional application related widgets and features:
54
+
55
+ - Application Header : the first row of an application, containing application name, or program module name. Usually
56
+ some of this text changes as a user navigates, such as line number in a list.
57
+ - Statusline : similar to vim's statusline with various bits of information, or status, and time.
58
+ - Dock : Identical to Alpine's key-action labels at the bottom of the screen informing the user of some actions
59
+ that may be taken in the current context.
60
+
61
+ Lesser used widgets and features:
62
+
63
+ - Menubar: similar to the menubar on all applications with menu's and menuitems that trigger actions.
64
+ - Tree : heirarchical data structure
65
+ - Table : tabular data structure
66
+ - Other buttons: Checkbox, Radiobutton, Togglebutton
67
+ - TabbedPane - useful for configuration screens
68
+ - Variable : based on TK's tkVariable, once used a lot internally in each widget, now used only in radiobuttons.
69
+ - Progress Bar - display progress of a process. See also progress_dialog.
70
+ - Textarea : editable multiline widget.
71
+
72
+ Some Issues with rbcurse:
73
+
74
+ Widgets required explicit coordinates. To that effect the App class allowed for `Stack` and `Flow` (idea borrowed from
75
+ the **Shoes** project. This works well, but all stack and flow information is lost when controls are placed meaning that a
76
+ change in the window size (if user resizes) does not (and cannot) resize the application.
77
+
78
+ Canis has recently introduced Layout objects which have the ability to re-locate and resize objects when screen size
79
+ is changed, although these layout objects are quite simple compared to the earlier stack and flow. The earlier stack
80
+ and flow allowed any number of recursive layers.
81
+
82
+ Currently there are three layout objects:
83
+ - `StackLayout` : does only stacking of objects (vertical placement)
84
+ - `FlowLayout` : does only horizontal placement of obects
85
+ - `SplitLayout` : allows for multiple stacks and flows by means of splitting a split either horizontally or vertically
86
+ and either placing an object in it, or splitting it further.
87
+ These are based on an `AbstractLayout` which can be used to derive further layouts.
88
+
89
+ It is my intention to move usage over to these layouts since they are simpler, and allow for resizing (and to abandon
90
+ stacks and flows at some stage, unless people find them easier to use).
91
+
92
+ Issues canis would like to address:
93
+
94
+ - further simplifying of canis, but giving the user/programmer the ability of adding complexity at his level.
95
+ I would like to do this before reaching 1.0.
96
+
97
+ - Keymapping. Currently, takes codes as integers, but i would have liked moving to strings as in vim.
98
+ Currently we have to map `?\C-a.getbytes(0)` or `[?g, ?g]`, whereas a string would allow us to map `"<C-x>s"`
99
+ or `"<F1>"` or `"gg"`. The issue is that there is too much rework within the library since each widget uses integer mappings.
100
+ Mapping and matching multiple keys would be a lot easier if stored internally as a string, currently multiple
101
+ mappings require a hash or tree at each level.
102
+
103
+ For a tutorial of rbcurse, see:
104
+ https://github.com/rkumar/rbcurse-tutorial
105
+ This tutorial needs to be updated for canis. Although, canis has diverged/forked from rbcurse, but the basic principles are still the same.
106
+
107
+ There is some on-line documentation of classes at:
108
+ http://rubydoc.info/gems/canis/0.0.5/frames
17
109
 
18
110
  ## Contributing
19
111
 
112
+ 0. Please give suggestions on how to improve the documentation.
113
+ 0.1. Please give suggestion on how to improve canis.
114
+
20
115
  1. Fork it ( https://github.com/[my-github-username]/canis/fork )
21
116
  2. Create your feature branch (`git checkout -b my-new-feature`)
22
117
  3. Commit your changes (`git commit -am 'Add some feature'`)
@@ -4,7 +4,7 @@
4
4
  # Author: j kepler http://github.com/mare-imbrium/canis/
5
5
  # Date: 2014-06-02 - 20:26
6
6
  # License: MIT
7
- # Last update: 2014-07-10 18:33
7
+ # Last update: 2014-08-30 17:50
8
8
  # ----------------------------------------------------------------------------- #
9
9
  # devel.rb Copyright (C) 2012-2014 j kepler
10
10
  require 'canis/core/include/appmethods'
@@ -78,6 +78,89 @@ module Canis
78
78
  code_browse str
79
79
  end
80
80
  end
81
+ # for the current field, display the instance variables and their values
82
+ # as well as the public methods.
83
+ # (We can do this in a tree format too)
84
+ def view_properties field=@form.get_current_field
85
+ alert "Nil field" unless field
86
+ return unless field
87
+ text = ["Instance Variables"]
88
+ text << "------------------"
89
+ #iv = field.instance_variables.map do |v| v.to_s; end
90
+ field.instance_variables.each do |v|
91
+ val = field.instance_variable_get(v)
92
+ klass = val.class
93
+ if val.is_a? Array
94
+ val = val.size
95
+ elsif val.is_a? Hash
96
+ val = val.keys
97
+ end
98
+ case val
99
+ when String, Fixnum, Integer, TrueClass, FalseClass, NilClass, Array, Hash, Symbol
100
+ ;
101
+ else
102
+ val = "Not shown"
103
+ end
104
+ text << "%20s %10s %s" % [v, klass, val]
105
+ end
106
+ text << " "
107
+ text << "Public Methods"
108
+ text << "--------------"
109
+ pm = field.public_methods(false).map do |v| v.to_s; end
110
+ text += pm
111
+ text << " "
112
+ text << "Inherited Methods"
113
+ text << "-----------------"
114
+ pm = field.public_methods(true) - field.public_methods(false)
115
+ pm = pm.map do |v| v.to_s; end
116
+ text += pm
117
+
118
+ #$log.debug " view_properties #{s.size} , #{s} "
119
+ textdialog text, :title => "Properties"
120
+ end
121
+
122
+ # place instance_vars of current or given object into a hash
123
+ # and view in a treedialog.
124
+ def view_properties_as_tree field=@form.get_current_field
125
+ alert "Nil field" unless field
126
+ return unless field
127
+ text = []
128
+ tree = {}
129
+ #iv = field.instance_variables.map do |v| v.to_s; end
130
+ field.instance_variables.each do |v|
131
+ val = field.instance_variable_get(v)
132
+ klass = val.class
133
+ if val.is_a? Array
134
+ #tree[v.to_s] = val
135
+ text << { v.to_s => val }
136
+ val = val.size
137
+ elsif val.is_a? Hash
138
+ #tree[v.to_s] = val
139
+ text << { v.to_s => val }
140
+ if val.size <= 5
141
+ val = val.keys
142
+ else
143
+ val = val.keys.size.to_s + " [" + val.keys.first(5).join(", ") + " ...]"
144
+ end
145
+ end
146
+ case val
147
+ when String, Fixnum, Integer, TrueClass, FalseClass, NilClass, Array, Hash, Symbol
148
+ ;
149
+ else
150
+ val = "Not shown"
151
+ end
152
+ text << "%-20s %10s %s" % [v, klass, val]
153
+ end
154
+ tree["Instance Variables"] = text
155
+ pm = field.public_methods(false).map do |v| v.to_s; end
156
+ tree["Public Methods"] = pm
157
+ pm = field.public_methods(true) - field.public_methods(false)
158
+ pm = pm.map do |v| v.to_s; end
159
+ tree["Inherited Methods"] = pm
160
+
161
+ #$log.debug " view_properties #{s.size} , #{s} "
162
+ treedialog tree, :title => "Properties"
163
+ end
81
164
  def ruby_renderer
82
165
  require 'canis/core/include/defaultfilerenderer'
83
166
  dr = DefaultFileRenderer.new
@@ -1,8 +1,228 @@
1
1
  require 'canis/core/util/app'
2
2
  require 'sqlite3'
3
- #require 'canis/experimental/resultsettextview.rb'
4
- #require 'canis/experimental/widgets/undomanager'
5
3
 
4
+ def menu_bar hash, config={}, &block
5
+ if hash.is_a? Hash
6
+ list = hash.keys
7
+ else
8
+ list = hash
9
+ end
10
+ raise ArgumentError, "Nil list received by popuplist" unless list
11
+
12
+ max_visible_items = config[:max_visible_items]
13
+ # FIXME have to ensure that row and col don't exceed FFI::NCurses.LINES and cols that is the window
14
+ # should not FINISH outside or padrefresh will fail.
15
+ row = config[:row] || 5
16
+ col = config[:col] || 5
17
+ relative_to = config[:relative_to]
18
+ if relative_to
19
+ layout = relative_to.form.window.layout
20
+ row += layout[:top]
21
+ col += layout[:left]
22
+ end
23
+ config.delete :relative_to
24
+ extra = 2 # trying to space the popup slightly, too narrow
25
+ width = config[:width] || longest_in_list(list)+4 # borders take 2
26
+ if config[:title]
27
+ width = config[:title].size + 4 if width < config[:title].size + 4
28
+ end
29
+ height = config[:height]
30
+ height ||= [max_visible_items || 10+2, list.length+2].min
31
+ #layout(1+height, width+4, row, col)
32
+ layout = { :height => 0+height, :width => 0+width, :top => row, :left => col }
33
+ window = Canis::Window.new(layout)
34
+ window.name = "WINDOW:popuplist"
35
+ window.wbkgd(Ncurses.COLOR_PAIR($reversecolor));
36
+ form = Canis::Form.new window
37
+
38
+ right_actions = config[:right_actions] || {}
39
+ config.delete(:right_actions)
40
+ less = 0 # earlier 0
41
+ listconfig = config[:listconfig] || {}
42
+ listconfig[:list] = list
43
+ listconfig[:width] = width - less
44
+ listconfig[:height] = height
45
+ listconfig[:selection_mode] ||= :single
46
+ listconfig.merge!(config)
47
+ listconfig.delete(:row);
48
+ listconfig.delete(:col);
49
+ # trying to pass populists block to listbox
50
+ lb = Canis::Listbox.new form, listconfig, &block
51
+ #lb.should_show_focus = true
52
+ #$row_focussed_attr = REVERSE
53
+
54
+
55
+ # added next line so caller can configure listbox with
56
+ # events such as ENTER_ROW, LEAVE_ROW or LIST_SELECTION_EVENT or PRESS
57
+ # 2011-11-11
58
+ #yield lb if block_given? # No it won't work since this returns
59
+ window.wrefresh
60
+ Ncurses::Panel.update_panels
61
+ form.repaint
62
+ window.wrefresh
63
+ display_on_enter = false
64
+ begin
65
+ windows = []
66
+ lists = []
67
+ hashes = []
68
+ choices = []
69
+ unentered_window = nil
70
+ _list = nil
71
+ while((ch = window.getchar()) != 999 )
72
+ case ch
73
+ when -1
74
+ next
75
+ when ?\C-q.getbyte(0)
76
+ break
77
+ else
78
+ lb.handle_key ch
79
+ lb.form.repaint
80
+ if ch == Ncurses::KEY_DOWN or ch == Ncurses::KEY_UP
81
+ if unentered_window
82
+ unentered_window.destroy
83
+ unentered_window = nil
84
+ end
85
+ # we need to update hash as we go along and back it up.
86
+ if display_on_enter
87
+ # removed since cursor goes in
88
+ end
89
+ elsif ch == Ncurses::KEY_RIGHT
90
+ if hash.is_a? Hash
91
+ val = hash[lb.current_value]
92
+ if val.is_a? Hash or val.is_a? Array
93
+ unentered_hash = val
94
+ choices << lb.current_value
95
+ unentered_window, _list = display_submenu val, :row => lb.current_index, :col => lb.width, :relative_to => lb,
96
+ :bgcolor => :cyan
97
+ end
98
+ else
99
+ x = right_actions[lb.current_value]
100
+ val = nil
101
+ if x.respond_to? :call
102
+ val = x.call
103
+ elsif x.is_a? Symbol
104
+ val = send(x)
105
+ end
106
+ if val
107
+ choices << lb.current_value
108
+ unentered_hash = val
109
+ unentered_window, _list = display_submenu val, :row => lb.current_index, :col => lb.width, :relative_to => lb,
110
+ :bgcolor => :cyan
111
+ end
112
+
113
+ end
114
+ # move into unentered
115
+ if unentered_window
116
+ lists << lb
117
+ hashes << hash
118
+ hash = unentered_hash
119
+ lb = _list
120
+ windows << unentered_window
121
+ unentered_window = nil
122
+ _list = nil
123
+ end
124
+ elsif ch == Ncurses::KEY_LEFT
125
+ if unentered_window
126
+ unentered_window.destroy
127
+ unentered_window = nil
128
+ end
129
+ # close current window
130
+ curr = nil
131
+ curr = windows.pop unless windows.empty?
132
+ curr.destroy if curr
133
+ lb = lists.pop unless lists.empty?
134
+ hash = hashes.pop unless hashes.empty?
135
+ choices.pop unless choices.empty?
136
+ unless windows.empty?
137
+ #form = windows.last
138
+ #lb - lists.pop
139
+ end
140
+ end
141
+ if ch == 13 || ch == 10
142
+ val = lb.current_value
143
+ if hash.is_a? Hash
144
+ val = hash[val]
145
+ if val.is_a? Symbol
146
+ #alert "got #{val}"
147
+ #return val
148
+ choices << val
149
+ return choices
150
+ end
151
+ else
152
+ #alert "got value #{val}"
153
+ #return val
154
+ choices << val
155
+ return choices
156
+ end
157
+ break
158
+ end
159
+ end
160
+ end
161
+ ensure
162
+ window.destroy if window
163
+ windows.each do |w| w.destroy if w ; end
164
+ end
165
+ return nil
166
+ end
167
+ def display_submenu hash, config={}, &block
168
+ if hash.is_a? Hash
169
+ list = hash.keys
170
+ else
171
+ list = hash
172
+ end
173
+ raise ArgumentError, "Nil list received by popuplist" unless list
174
+
175
+ max_visible_items = config[:max_visible_items]
176
+ # FIXME have to ensure that row and col don't exceed FFI::NCurses.LINES and cols that is the window
177
+ # should not FINISH outside or padrefresh will fail.
178
+ row = config[:row] || 1
179
+ col = config[:col] || 0
180
+ relative_to = config[:relative_to]
181
+ if relative_to
182
+ layout = relative_to.form.window.layout
183
+ row += layout[:top]
184
+ col += layout[:left]
185
+ end
186
+ config.delete :relative_to
187
+ extra = 2 # trying to space the popup slightly, too narrow
188
+ width = config[:width] || longest_in_list(list)+4 # borders take 2
189
+ if config[:title]
190
+ width = config[:title].size + 4 if width < config[:title].size + 4
191
+ end
192
+ height = config[:height]
193
+ height ||= [max_visible_items || 10+2, list.length+2].min
194
+ #layout(1+height, width+4, row, col)
195
+ layout = { :height => 0+height, :width => 0+width, :top => row, :left => col }
196
+ window = Canis::Window.new(layout)
197
+ window.name = "WINDOW:popuplist"
198
+ window.wbkgd(Ncurses.COLOR_PAIR($reversecolor));
199
+ form = Canis::Form.new window
200
+
201
+ less = 0 # earlier 0
202
+ listconfig = config[:listconfig] || {}
203
+ listconfig[:list] = list
204
+ listconfig[:width] = width - less
205
+ listconfig[:height] = height
206
+ listconfig[:selection_mode] ||= :single
207
+ listconfig.merge!(config)
208
+ listconfig.delete(:row);
209
+ listconfig.delete(:col);
210
+ # trying to pass populists block to listbox
211
+ lb = Canis::Listbox.new form, listconfig, &block
212
+
213
+
214
+ # added next line so caller can configure listbox with
215
+ # events such as ENTER_ROW, LEAVE_ROW or LIST_SELECTION_EVENT or PRESS
216
+ # 2011-11-11
217
+ #yield lb if block_given? # No it won't work since this returns
218
+ window.wrefresh
219
+ Ncurses::Panel.update_panels
220
+ form.repaint
221
+ window.wrefresh
222
+ return window, lb
223
+ end
224
+ # TODO : if no data give an alert
225
+ #
6
226
  # @return array of table names from selected db file
7
227
  def get_table_names
8
228
  raise "No database file selected." unless $current_db
@@ -24,7 +244,7 @@ end
24
244
  def get_data sql
25
245
  $log.debug "SQL: #{sql} "
26
246
  $columns, *rows = $db.execute2(sql)
27
- $log.debug "XXX COLUMNS #{sql} "
247
+ $log.debug "XXX COLUMNS #{sql}, #{rows.count} "
28
248
  content = rows
29
249
  return nil if content.nil? or content[0].nil?
30
250
  $datatypes = content[0].types #if @datatypes.nil?
@@ -58,7 +278,54 @@ def create_popup array, selection_mode=:single, &blk
58
278
  end
59
279
  end
60
280
  end
281
+ # this is just a dead simple attenpt at a menu sans all the complexity of a menubar,
282
+ # but issue is that we need to select, and the menu disappears.
283
+ # We need to see the submenu as we traverse, and the tree should not disappear.
284
+ # # get actions working
285
+ # # accelerator
286
+ # # hotkey
287
+ # # separator
288
+ # # enabled disabled
289
+ # # > to come automatically at end if hash
290
+ # # work with horizlist
291
+ def create_menu
292
+ items = Hash.new
293
+ # action shd be a hash
294
+ # menu should have array of hashes (or just a string)
295
+ #db = { :name => "Databases", :accelerator => "M-d", :enabled = true, :on_right => :get_databases }
296
+ #or = { :name => "Open Recent", :accelerator => "M-o", :enabled = true, :on_right => :get_recent }
297
+ #find_array = {"Find ..." => :find, "Find Next" => :find_next, "Find Previous" => :find_prev}
298
+ items["File >"] = ["Open ... C-o" , "Open Recent", "Databases" , "Tables", "Exit"]
299
+ items["Window >"] = { "Tile" => nil, "Find >" => {"Find ..." => :find, "Find Next" => :find_next, "Find Previous" => :find_prev},
300
+ "Edit" => nil, "Whatever" => nil}
301
+ items["Others >"] = { "Shell Output ..." => :shell_output, "Suspend ..." => :suspend , "View File" => :choose_file_and_view}
302
+
303
+ # in the case of generated names how will call back know that it is a db name or a table name
304
+ # We get back an array containing the entire path of selections
305
+ right_actions = {}
306
+ right_actions["Databases"] = Proc.new { Dir.glob("**/*.{sqlite,db}") }
307
+ right_actions["Tables"] = :get_table_names
308
+
309
+ ret = popupmenu items, :row => 1, :col => 0, :bgcolor => :cyan, :color => :white, :right_actions => right_actions
310
+ # ret can be nil, or have a symbol to execute, or a String for an item with no leaf/symbol
311
+ if ret
312
+ alert "Got #{ret}"
313
+ last = ret.last
314
+ if last.is_a? Symbol
315
+ if respond_to?(last, true)
316
+ send(last)
317
+ end
318
+ end
319
+ end
61
320
 
321
+ return
322
+ r = 1
323
+ ix = popuplist( top , :title => " Menu " , :row => r, :col => 0, :bgcolor => :cyan, :color => :white)
324
+ if ix
325
+ value = top[ix]
326
+ ix = popuplist( items[value] , :row => r + 2 + ix, :col => 10, :bgcolor => :cyan, :color => :white)
327
+ end
328
+ end
62
329
  #
63
330
  # changed order of name and fields, thanks hramrach
64
331
  def view_data name, fields="*"
@@ -69,10 +336,21 @@ def view_data name, fields="*"
69
336
  view_sql stmt
70
337
  @form.by_name['tarea'] << stmt if @form # nil when called from menu
71
338
  end
339
+ def view_schema tablename
340
+ string = `sqlite3 #{$current_db} ".schema #{tablename}"`
341
+ string = $db.get_first_value "select sql from sqlite_master where name = '#{tablename}'"
342
+
343
+ string = string.split("\n")
344
+ if string.size == 1
345
+ string = string.first.split(",")
346
+ end
347
+ view string
348
+ end
72
349
  def view_sql stmt
73
350
  begin
74
351
  content = get_data stmt
75
352
  if content.nil?
353
+ alert "No data for query"
76
354
  else
77
355
  require 'canis/core/widgets/tabular'
78
356
  t = Tabular.new do |t|
@@ -89,6 +367,7 @@ def view_sql stmt
89
367
  end
90
368
 
91
369
  App.new do
370
+ $log = create_logger "canisdb.log"
92
371
  #header = app_header "canis #{Canis::VERSION}", :text_center => "Database Demo", :text_right =>"enabled"
93
372
  form = @form
94
373
  mylabel = "a field"
@@ -154,11 +433,14 @@ App.new do
154
433
  def ask_databases
155
434
  names = Dir.glob("*.{sqlite,db}")
156
435
  if names
157
- ix = popuplist( names )
436
+ ix = popuplist( names , :row => 1, :col => 0, :bgcolor => :cyan, :color => :white, :title => "Databases")
158
437
  if ix
159
438
  value = names[ix]
160
439
  connect(value);
161
440
  @form.by_name["tlist"].list(get_table_names)
441
+ @form.by_name["tlist"].clear_selection
442
+ @form.by_name["clist"].clear_selection
443
+ @form.by_name["clist"].remove_all
162
444
  end
163
445
 
164
446
  else
@@ -185,6 +467,9 @@ App.new do
185
467
  command do |menuitem, text|
186
468
  connect text
187
469
  form.by_name["tlist"].list(get_table_names)
470
+ form.by_name["tlist"].clear_selection
471
+ form.by_name["clist"].clear_selection
472
+ form.by_name["clist"].remove_all
188
473
  end
189
474
  end
190
475
  menu "Tables" do
@@ -202,6 +487,7 @@ App.new do
202
487
  item "New", "N"
203
488
  separator
204
489
  item "Exit", "x" do
490
+ accelerator "F10"
205
491
  command do
206
492
  throw(:close)
207
493
  end
@@ -211,36 +497,50 @@ App.new do
211
497
  end
212
498
 
213
499
  end # menu
214
- menu "Window" do
215
- item "Tile", "T"
500
+ menu "Edit" do
501
+ item "Paste", "P"
502
+ menu "Paste Special" do
503
+ item "Paste Slowly"
504
+ separator
505
+ item "Paste Faster"
506
+ item "Paste Slower"
507
+ end
216
508
  menu "Find" do
217
- item "More", "M"
218
- $x = item "Less", "L" do
509
+ item "Find ...", "F"
510
+ $x = item "Find Next", "N" do
219
511
  #accelerator "Ctrl-X"
220
512
  command do
221
- alert "You clickses on Less"
513
+ alert "You clicked on Find Next "
222
514
  end
223
515
  end
224
- menu "Size" do
516
+ item "Find Previous", "P"
517
+ menu "Window" do
225
518
  item "Zoom", "Z"
226
519
  item "Maximize", "X"
227
520
  item "Minimize", "N"
228
521
  end
229
522
  end
230
523
  end
231
- menu "Others" do
524
+ menu "Shell" do
232
525
  require 'canis/core/include/appmethods.rb'
233
- item "Shell Output" do
526
+ require './common/devel.rb'
527
+ item "Shell Output ..." do
234
528
  command { shell_output }
235
529
  end
236
- item "Suspend" do
530
+ item "Suspend ..." do
237
531
  command { suspend }
238
532
  end
533
+ item "System ..." do
534
+ command { shell_out }
535
+ end
536
+ item "View File ..." do
537
+ command { choose_file_and_view }
538
+ end
239
539
  end
240
540
  end # menubar
241
541
  mb.toggle_key = FFI::NCurses::KEY_F2
242
542
  mb.color = :white
243
- mb.bgcolor = :blue
543
+ mb.bgcolor = :magenta
244
544
  @form.set_menu_bar mb
245
545
  tv = nil
246
546
  flow :margin_top => 1 do
@@ -251,7 +551,7 @@ App.new do
251
551
  text = ["Select DB first.","Press Alt-D or ENTER"]
252
552
  end
253
553
  tlist = listbox :name => "tlist", :list => text, :title => "Tables", :height => 10,
254
- :selected_color => 'cyan', :selected_bgcolor => 'black' , :selected_attr => Ncurses::A_REVERSE,
554
+ :selected_color => :cyan, :selected_bgcolor => :white , :selected_attr => Ncurses::A_REVERSE,
255
555
  :help_text => "<ENTER> to View complete table, 'v' to select table and view columns",
256
556
  :should_show_focus => true,
257
557
  :selection_mode => :single
@@ -270,11 +570,25 @@ App.new do
270
570
  #end
271
571
  clist = listbox :name => "clist", :list => ["No columns"], :title => "Columns", :height => 14,
272
572
  :selection_mode => :multiple,
273
- :selected_color => 'cyan', :selected_bgcolor => 'black' , :selected_attr => Ncurses::A_REVERSE,
573
+ :selected_color => :cyan, :selected_bgcolor => :white , :selected_attr => Ncurses::A_REVERSE,
274
574
  :help_text => "Enter to View selected fields, 'v' to select columns, w - where, o-order"
575
+
576
+
577
+ # change selected color when user enters or exits
578
+ [clist , tlist].each do |o|
579
+ o.bind(:ENTER) do
580
+ # reduce flicker by only modifying if necesssary
581
+ o.selected_color = :cyan if o.selected_color != :cyan
582
+ end
583
+ o.bind(:LEAVE) do
584
+ # reduce flicker by only modifying if necesssary
585
+ o.selected_color = :blue unless o.selected_indices.empty?
586
+ end
587
+ end
275
588
  tlist.bind(:LIST_SELECTION_EVENT) do |eve|
276
589
  $selected_table = eve.source[eve.firstrow]
277
590
  $current_table = $selected_table
591
+ clist.clear_selection
278
592
  clist.list( get_column_names $selected_table)
279
593
  end
280
594
  clist.bind(:PRESS) do |eve|
@@ -288,7 +602,7 @@ App.new do
288
602
  end
289
603
  view_data $selected_table, cols
290
604
  else
291
- alert "Select a table first."
605
+ alert "Select a table first ('v' selects)."
292
606
  end
293
607
  end
294
608
  clist.bind_key('w', 'add to where condition') {
@@ -314,24 +628,17 @@ App.new do
314
628
  message "order: #{$order_columns.last}"
315
629
  $log.debug "XXX: ORDER: #{$order_columns}. Press F4 when done"
316
630
  }
317
- @statusline = status_line
318
- #wg = get_color($datacolor, 'white','green')
319
- #wb = get_color($datacolor, 'white','blue')
631
+ @statusline = status_line :row => -3, :bgcolor => :magenta, :color => :black
320
632
  @statusline.command {
321
633
  # trying this out. If you want a persistent message that remains till the next on
322
634
  # then send it in as $status_message
323
635
  text = $status_message.value || ""
324
636
  if !$current_db
325
- #"[%-s] %s" % [ "Select a Database", text]
326
- "[%-s] %s" % [ "#[bg=red,fg=yellow]Select a Database#[end]", text]
327
- #[ [nil, "%-22s" % Time.now, nil], [$errorcolor, " [Select a Database ]", FFI::NCurses::A_BOLD], [nil, text, nil] ]
637
+ "[%-s] %s" % [ "#[bg=red,fg=white,bold]Select a Database#[end]", text]
328
638
  elsif !$current_table
329
- "[DB: #[fg=white,bg=blue]%-s#[end] | %-s ] %s" % [ $current_db || "None", $current_table || "#[bg=red,fg=yellow]Select a table#[end]", text]
330
- #[ [nil, "%-22s [DB: %-s | " % [Time.now, $current_db || "None" ],nil], [$errorcolor, " Select a Table ]", FFI::NCurses::A_BOLD], [nil, text, nil] ]
639
+ "[DB: #[fg=white,bg=blue]%-s#[end] | %-s ] %s" % [ $current_db || "None", $current_table || "#[bg=red,fg=white]Select a table#[end]", text]
331
640
  else
332
- "DB: #[fg=white,bg=green,bold]%-s#[end] | #[bold]%-s#[end] ] %s" % [ $current_db || "None", $current_table || "----", text]
333
- #[ [nil, "%-22s [DB: " % Time.now, nil], [wb, " #{$current_db} ", FFI::NCurses::A_BOLD],
334
- #[wg, $current_table || "----", FFI::NCurses::A_BOLD], [nil, text, nil] ]
641
+ "DB: #[fg=white,bg=green,bold]%-s#[end] | #[fg=white,bold]%-s#[end] ] %s" % [ $current_db || "None", $current_table || "----", text]
335
642
  end
336
643
  }
337
644
  @adock = nil
@@ -341,9 +648,9 @@ App.new do
341
648
  ["M-d", "Database"], ["M-t", "Table"],
342
649
  ["M-x", "Command"], nil
343
650
  ]
344
- tlist_keyarray = keyarray + [ ["Sp", "Select"], nil, ["Enter","View"] ]
651
+ tlist_keyarray = keyarray + [ ["v", "Select"], nil, ["Enter","View"] ]
345
652
 
346
- clist_keyarray = keyarray + [ ["Sp", "Select"], ["C-sp", "Range Sel"],
653
+ clist_keyarray = keyarray + [ ["v", "Select"], ["V", "Range Sel"],
347
654
  ["Enter","View"], ['w', 'where'],
348
655
  ["o","order by"], ['O', 'order desc']
349
656
  ]
@@ -396,6 +703,25 @@ App.new do
396
703
  @form.bind_key(?\M-d, 'select database') do
397
704
  ask_databases
398
705
  end
706
+ @form.bind_key(?\M-s, 'Enter SQL') do
707
+ str = get_text "Enter SQL"
708
+ if str
709
+ str = str.join " "
710
+ view_sql str
711
+ end
712
+ end
713
+ @form.bind_key(FFI::NCurses::KEY_F3, 'Menu') do
714
+ create_menu
715
+ end
716
+ @form.bind_key(FFI::NCurses::KEY_F5, 'view schema') do
717
+ view_schema $current_table
718
+ end
719
+ @form.bind_key(FFI::NCurses::KEY_F6, 'view properties') do
720
+ view_properties @form.get_current_field
721
+ end
722
+ @form.bind_key(FFI::NCurses::KEY_F7, 'view properties as tree') do
723
+ view_properties_as_tree @form.get_current_field
724
+ end
399
725
  @form.bind_key(FFI::NCurses::KEY_F4, 'view data') do
400
726
  $where_string = nil
401
727
  $order_string = nil