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.
- checksums.yaml +7 -0
- data/.gitignore +45 -0
- data/CHANGES +52 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +24 -0
- data/Rakefile +2 -0
- data/canis.gemspec +25 -0
- data/examples/alpmenu.rb +46 -0
- data/examples/app.sample +19 -0
- data/examples/appemail.rb +191 -0
- data/examples/atree.rb +105 -0
- data/examples/bline.rb +181 -0
- data/examples/common/devel.rb +319 -0
- data/examples/common/file.rb +93 -0
- data/examples/data/README.markdown +9 -0
- data/examples/data/brew.txt +38 -0
- data/examples/data/color.2 +37 -0
- data/examples/data/gemlist.txt +59 -0
- data/examples/data/lotr.txt +12 -0
- data/examples/data/ports.txt +136 -0
- data/examples/data/table.txt +37 -0
- data/examples/data/tasks.csv +88 -0
- data/examples/data/tasks.txt +27 -0
- data/examples/data/todo.txt +16 -0
- data/examples/data/todocsv.csv +28 -0
- data/examples/data/unix1.txt +21 -0
- data/examples/data/unix2.txt +11 -0
- data/examples/dbdemo.rb +506 -0
- data/examples/dirtree.rb +177 -0
- data/examples/newtabbedwindow.rb +100 -0
- data/examples/newtesttabp.rb +92 -0
- data/examples/tabular.rb +212 -0
- data/examples/tasks.rb +179 -0
- data/examples/term2.rb +88 -0
- data/examples/testbuttons.rb +307 -0
- data/examples/testcombo.rb +102 -0
- data/examples/testdb.rb +182 -0
- data/examples/testfields.rb +208 -0
- data/examples/testflowlayout.rb +43 -0
- data/examples/testkeypress.rb +98 -0
- data/examples/testlistbox.rb +187 -0
- data/examples/testlistbox1.rb +199 -0
- data/examples/testmessagebox.rb +144 -0
- data/examples/testprogress.rb +116 -0
- data/examples/testree.rb +107 -0
- data/examples/testsplitlayout.rb +53 -0
- data/examples/testsplitlayout1.rb +49 -0
- data/examples/teststacklayout.rb +48 -0
- data/examples/testwsshortcuts.rb +68 -0
- data/examples/testwsshortcuts2.rb +129 -0
- data/lib/canis.rb +16 -0
- data/lib/canis/core/docs/index.txt +104 -0
- data/lib/canis/core/docs/list.txt +16 -0
- data/lib/canis/core/docs/style_help.yml +34 -0
- data/lib/canis/core/docs/tabbedpane.txt +15 -0
- data/lib/canis/core/docs/table.txt +31 -0
- data/lib/canis/core/docs/textpad.txt +48 -0
- data/lib/canis/core/docs/tree.txt +23 -0
- data/lib/canis/core/include/.DS_Store +0 -0
- data/lib/canis/core/include/action.rb +83 -0
- data/lib/canis/core/include/actionmanager.rb +49 -0
- data/lib/canis/core/include/appmethods.rb +179 -0
- data/lib/canis/core/include/bordertitle.rb +49 -0
- data/lib/canis/core/include/canisparser.rb +100 -0
- data/lib/canis/core/include/colorparser.rb +437 -0
- data/lib/canis/core/include/defaultfilerenderer.rb +64 -0
- data/lib/canis/core/include/io.rb +320 -0
- data/lib/canis/core/include/layouts/SplitLayout.rb +161 -0
- data/lib/canis/core/include/layouts/abstractlayout.rb +213 -0
- data/lib/canis/core/include/layouts/flowlayout.rb +104 -0
- data/lib/canis/core/include/layouts/stacklayout.rb +109 -0
- data/lib/canis/core/include/listbindings.rb +89 -0
- data/lib/canis/core/include/listeditable.rb +319 -0
- data/lib/canis/core/include/listoperations.rb +61 -0
- data/lib/canis/core/include/listselectionmodel.rb +388 -0
- data/lib/canis/core/include/multibuffer.rb +173 -0
- data/lib/canis/core/include/ractionevent.rb +73 -0
- data/lib/canis/core/include/rchangeevent.rb +27 -0
- data/lib/canis/core/include/rhistory.rb +95 -0
- data/lib/canis/core/include/rinputdataevent.rb +47 -0
- data/lib/canis/core/include/textdocument.rb +111 -0
- data/lib/canis/core/include/vieditable.rb +175 -0
- data/lib/canis/core/include/widgetmenu.rb +66 -0
- data/lib/canis/core/system/colormap.rb +165 -0
- data/lib/canis/core/system/keydefs.rb +32 -0
- data/lib/canis/core/system/ncurses.rb +237 -0
- data/lib/canis/core/system/panel.rb +129 -0
- data/lib/canis/core/system/window.rb +1081 -0
- data/lib/canis/core/util/ansiparser.rb +119 -0
- data/lib/canis/core/util/app.rb +696 -0
- data/lib/canis/core/util/basestack.rb +412 -0
- data/lib/canis/core/util/defaultcolorparser.rb +84 -0
- data/lib/canis/core/util/extras/README +5 -0
- data/lib/canis/core/util/extras/bottomline.rb +1815 -0
- data/lib/canis/core/util/extras/padreader.rb +192 -0
- data/lib/canis/core/util/focusmanager.rb +31 -0
- data/lib/canis/core/util/helpmanager.rb +160 -0
- data/lib/canis/core/util/oldwidgetshortcuts.rb +304 -0
- data/lib/canis/core/util/promptmenu.rb +235 -0
- data/lib/canis/core/util/rcommandwindow.rb +933 -0
- data/lib/canis/core/util/rdialogs.rb +520 -0
- data/lib/canis/core/util/textutils.rb +74 -0
- data/lib/canis/core/util/viewer.rb +238 -0
- data/lib/canis/core/util/widgetshortcuts.rb +508 -0
- data/lib/canis/core/widgets/applicationheader.rb +103 -0
- data/lib/canis/core/widgets/box.rb +58 -0
- data/lib/canis/core/widgets/divider.rb +310 -0
- data/lib/canis/core/widgets/extras/README.md +12 -0
- data/lib/canis/core/widgets/extras/rtextarea.rb +960 -0
- data/lib/canis/core/widgets/extras/stackflow.rb +474 -0
- data/lib/canis/core/widgets/keylabelprinter.rb +194 -0
- data/lib/canis/core/widgets/listbox.rb +326 -0
- data/lib/canis/core/widgets/listfooter.rb +86 -0
- data/lib/canis/core/widgets/rcombo.rb +210 -0
- data/lib/canis/core/widgets/rcontainer.rb +415 -0
- data/lib/canis/core/widgets/rlink.rb +30 -0
- data/lib/canis/core/widgets/rmenu.rb +970 -0
- data/lib/canis/core/widgets/rmenulink.rb +30 -0
- data/lib/canis/core/widgets/rmessagebox.rb +400 -0
- data/lib/canis/core/widgets/rprogress.rb +118 -0
- data/lib/canis/core/widgets/rtabbedpane.rb +631 -0
- data/lib/canis/core/widgets/rtabbedwindow.rb +70 -0
- data/lib/canis/core/widgets/rwidget.rb +3634 -0
- data/lib/canis/core/widgets/scrollbar.rb +147 -0
- data/lib/canis/core/widgets/statusline.rb +113 -0
- data/lib/canis/core/widgets/table.rb +1072 -0
- data/lib/canis/core/widgets/tabular.rb +264 -0
- data/lib/canis/core/widgets/textpad.rb +1674 -0
- data/lib/canis/core/widgets/tree.rb +690 -0
- data/lib/canis/core/widgets/tree/treecellrenderer.rb +150 -0
- data/lib/canis/core/widgets/tree/treemodel.rb +432 -0
- data/lib/canis/version.rb +3 -0
- 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
|