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 +4 -4
- data/CHANGES +10 -0
- data/README.md +99 -4
- data/examples/common/devel.rb +84 -1
- data/examples/dbdemo.rb +356 -30
- data/lib/canis/core/include/appmethods.rb +13 -4
- data/lib/canis/core/include/colorparser.rb +31 -10
- data/lib/canis/core/include/listselectionmodel.rb +2 -2
- data/lib/canis/core/include/rhistory.rb +2 -2
- data/lib/canis/core/system/colormap.rb +3 -3
- data/lib/canis/core/system/window.rb +8 -3
- data/lib/canis/core/util/app.rb +94 -93
- data/lib/canis/core/util/defaultcolorparser.rb +7 -3
- data/lib/canis/core/util/rdialogs.rb +325 -5
- data/lib/canis/core/util/viewer.rb +1 -1
- data/lib/canis/core/util/widgetshortcuts.rb +3 -3
- data/lib/canis/core/widgets/applicationheader.rb +2 -4
- data/lib/canis/core/widgets/extras/rtextarea.rb +3 -1
- data/lib/canis/core/widgets/listbox.rb +2 -1
- data/lib/canis/core/widgets/rmenu.rb +45 -31
- data/lib/canis/core/widgets/rmessagebox.rb +51 -2
- data/lib/canis/core/widgets/rwidget.rb +23 -15
- data/lib/canis/core/widgets/statusline.rb +19 -9
- data/lib/canis/core/widgets/textpad.rb +2 -2
- data/lib/canis/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f94ffa2e37387dc50fb96d5f40cbd6b97cd60143
|
4
|
+
data.tar.gz: 2323ea61ad5c4403628a6932665aed0a5ab7901d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
3
|
+
wrapper over ruby ffi-ncurses library with essential components/controls only.
|
4
4
|
|
5
|
-
|
6
|
-
|
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
|
-
|
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'`)
|
data/examples/common/devel.rb
CHANGED
@@ -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-
|
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
|
data/examples/dbdemo.rb
CHANGED
@@ -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 "
|
215
|
-
item "
|
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 "
|
218
|
-
$x = item "
|
509
|
+
item "Find ...", "F"
|
510
|
+
$x = item "Find Next", "N" do
|
219
511
|
#accelerator "Ctrl-X"
|
220
512
|
command do
|
221
|
-
alert "You
|
513
|
+
alert "You clicked on Find Next "
|
222
514
|
end
|
223
515
|
end
|
224
|
-
|
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 "
|
524
|
+
menu "Shell" do
|
232
525
|
require 'canis/core/include/appmethods.rb'
|
233
|
-
|
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 = :
|
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 =>
|
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 =>
|
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
|
-
|
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=
|
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 + [ ["
|
651
|
+
tlist_keyarray = keyarray + [ ["v", "Select"], nil, ["Enter","View"] ]
|
345
652
|
|
346
|
-
clist_keyarray = keyarray + [ ["
|
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
|