rbcurse 1.5.0 → 1.5.2
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.
- data/Makefile +21 -0
- data/Manifest.txt +6 -0
- data/README.markdown +6 -4
- data/TODO +372 -0
- data/TODO2.txt +121 -0
- data/VERSION +1 -1
- data/examples/README.txt +67 -0
- data/examples/abasiclist.rb +33 -0
- data/examples/alpmenu.rb +42 -0
- data/examples/app.rb +859 -0
- data/examples/app.sample +17 -0
- data/examples/appdirtree.rb +74 -0
- data/examples/appemail.rb +191 -0
- data/examples/appemaillb.rb +308 -0
- data/examples/appgcompose.rb +315 -0
- data/examples/atree.rb +64 -0
- data/examples/common/file.rb +40 -0
- data/examples/common/rmail.rb +257 -0
- data/examples/data.txt +683 -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 +60 -0
- data/examples/data/lotr.txt +12 -0
- data/examples/data/ports.txt +136 -0
- data/examples/data/tasks.txt +27 -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 +495 -0
- data/examples/deprecated/appgmail.rb +952 -0
- data/examples/deprecated/splitp.rb +56 -0
- data/examples/deprecated/testscrolllb.rb +86 -0
- data/examples/deprecated/testscrollp.rb +88 -0
- data/examples/deprecated/testscrollta.rb +80 -0
- data/examples/deprecated/testscrolltable.rb +165 -0
- data/examples/deprecated/testsplit.rb +87 -0
- data/examples/deprecated/testsplit2.rb +123 -0
- data/examples/deprecated/testsplit3.rb +215 -0
- data/examples/deprecated/testsplit3_1.rb +244 -0
- data/examples/deprecated/testsplit3a.rb +215 -0
- data/examples/deprecated/testsplit3b.rb +237 -0
- data/examples/deprecated/testsplitta.rb +148 -0
- data/examples/deprecated/testsplittv.rb +142 -0
- data/examples/deprecated/testsplittvv.rb +144 -0
- data/examples/deprecated/testtpane.rb +215 -0
- data/examples/deprecated/testtpane2.rb +145 -0
- data/examples/deprecated/testtpanetable.rb +203 -0
- data/examples/dirtree.rb +88 -0
- data/examples/experimental/resultsetdemo.rb +280 -0
- data/examples/experimental/testmform.rb +35 -0
- data/examples/experimental/testscroller.rb +117 -0
- data/examples/experimental/teststackflow.rb +111 -0
- data/examples/menu1.rb +112 -0
- data/examples/multispl.rb +86 -0
- data/examples/newmessagebox.rb +131 -0
- data/examples/newtabbedwindow.rb +100 -0
- data/examples/newtesttabp.rb +121 -0
- data/examples/qdfilechooser.rb +68 -0
- data/examples/rfe.rb +1239 -0
- data/examples/rfe_renderer.rb +121 -0
- data/examples/sqlc.rb +454 -0
- data/examples/sqlm.rb +437 -0
- data/examples/sqlt.rb +408 -0
- data/examples/status.txt +68 -0
- data/examples/table1.rb +24 -0
- data/examples/term2.rb +84 -0
- data/examples/test1.rb +239 -0
- data/examples/test2.rb +674 -0
- data/examples/testapp.rb +44 -0
- data/examples/testapp2.rb +58 -0
- data/examples/testchars.rb +137 -0
- data/examples/testcombo.rb +91 -0
- data/examples/testkeypress.rb +66 -0
- data/examples/testlistbox.rb +113 -0
- data/examples/testmenu.rb +101 -0
- data/examples/testmulticomp.rb +70 -0
- data/examples/testmulticontainer.rb +94 -0
- data/examples/testmultispl.rb +199 -0
- data/examples/testree.rb +106 -0
- data/examples/testtable.rb +264 -0
- data/examples/testtabp.rb +107 -0
- data/examples/testtodo.rb +584 -0
- data/examples/testvimsplit.rb +112 -0
- data/examples/testwsshortcuts.rb +64 -0
- data/examples/testwsshortcuts2.rb +126 -0
- data/examples/todo.db +0 -0
- data/examples/todo.yml +191 -0
- data/examples/viewtodo.rb +574 -0
- data/lib/rbcurse/deprecated/README.markdown +12 -0
- data/lib/rbcurse/deprecated/rpad.rb +375 -0
- data/lib/rbcurse/deprecated/rscrollpane.rb +512 -0
- data/lib/rbcurse/deprecated/rsplitpane.rb +894 -0
- data/lib/rbcurse/deprecated/rsplitpane2.rb +1009 -0
- data/lib/rbcurse/deprecated/rviewport.rb +204 -0
- data/lib/rbcurse/deprecated/widgets/mapper.rb +130 -0
- data/lib/rbcurse/deprecated/widgets/rmessagebox.rb +348 -0
- data/lib/rbcurse/deprecated/widgets/rtabbedpane.rb +1158 -0
- data/lib/rbcurse/deprecated/widgets/rtabbedwindow.rb +167 -0
- data/lib/rbcurse/deprecated/widgets/scrollable.rb +301 -0
- data/lib/rbcurse/deprecated/widgets/stdscrwindow.rb +309 -0
- data/lib/ver/keyboard2.rb +170 -0
- data/test/test_rbcurse.rb +0 -0
- metadata +131 -9
@@ -0,0 +1,315 @@
|
|
1
|
+
require 'rbcurse/core/util/app'
|
2
|
+
require 'gmail'
|
3
|
+
|
4
|
+
# requires gmail gem
|
5
|
+
|
6
|
+
def insert_file
|
7
|
+
str = ask("File? ", Pathname) do |q|
|
8
|
+
q.completion_proc = Proc.new {|str| Dir.glob(str +"*").collect { |f| File.directory?(f) ? f+"/" : f } }
|
9
|
+
q.helptext = "Enter start of filename and tab to get completion"
|
10
|
+
end
|
11
|
+
message "we got #{str} "
|
12
|
+
end
|
13
|
+
################
|
14
|
+
# Allow a field to have gmail like completion
|
15
|
+
# As a user types, a window highlights matches. Up and down arrow key may be used to select with Enter.
|
16
|
+
# A user may type Multiple email ids in field (space or comma sep), and matches for last word will be
|
17
|
+
# displayed.
|
18
|
+
# If you wish to use this in other ways:
|
19
|
+
# - match entire field not last word, then set matching to false or single
|
20
|
+
# - match_from_start will match your array values from start. If you want
|
21
|
+
# the pattern to match anywhere in array, set to false.
|
22
|
+
# - set height, width, top and left in config hash
|
23
|
+
# - block is intended to allow caller to override matching TODO
|
24
|
+
class GmailField
|
25
|
+
def initialize field, list=nil, config={}, &block
|
26
|
+
@field = field
|
27
|
+
@list = list
|
28
|
+
@config = config
|
29
|
+
@matching = config.fetch(:matching, :multiple)
|
30
|
+
@match_from_start = config.fetch(:match_from_start, true)
|
31
|
+
setup
|
32
|
+
end
|
33
|
+
def setup # gmailfield
|
34
|
+
@field.bind(:CHANGE) do |eve|
|
35
|
+
c = eve.source
|
36
|
+
text = c.getvalue
|
37
|
+
if text[-1] == " "
|
38
|
+
clear_list
|
39
|
+
else
|
40
|
+
pos = text.rindex(/, /)
|
41
|
+
if pos
|
42
|
+
text = text[pos+1..-1].strip if @matching == :multiple
|
43
|
+
end
|
44
|
+
@textsize = text.size
|
45
|
+
# \W instead of ^, will match start of word, lastname, emailid
|
46
|
+
patt = @match_from_start ? %r{\W#{text}}i : %r{#{text}}i
|
47
|
+
matches = @list.grep patt
|
48
|
+
@matches = matches
|
49
|
+
if @matches.size == 1 && @matches.first == text
|
50
|
+
clear_list
|
51
|
+
else
|
52
|
+
#$log.debug "XXXX #{self.class} matches #{matches} " if $log.debug?
|
53
|
+
#@caller = app.config[:caller]
|
54
|
+
if !matches.empty?
|
55
|
+
say " matches: #{matches.size}: #{matches} "
|
56
|
+
@list_object = udisplay_list matches
|
57
|
+
else
|
58
|
+
clear_list
|
59
|
+
#@caller.clear_list
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
@field.bind(:LEAVE) do |eve|
|
65
|
+
clear_list
|
66
|
+
end
|
67
|
+
@field.bind_key(KEY_DOWN) do
|
68
|
+
if @list_object
|
69
|
+
@list_object.press(KEY_DOWN)
|
70
|
+
#current_index = @list_object.current_index
|
71
|
+
@list_object.display_content
|
72
|
+
else
|
73
|
+
:UNHANDLED
|
74
|
+
end
|
75
|
+
end
|
76
|
+
@field.bind_key(KEY_UP) do
|
77
|
+
if @list_object
|
78
|
+
@list_object.press(KEY_UP)
|
79
|
+
#current_index = @list_object.current_index
|
80
|
+
@list_object.display_content
|
81
|
+
else
|
82
|
+
:UNHANDLED
|
83
|
+
end
|
84
|
+
end
|
85
|
+
@field.bind_key(13) do
|
86
|
+
#if @caller
|
87
|
+
if @list_object
|
88
|
+
@list_object.press(KEY_ENTER)
|
89
|
+
current_index = @list_object.current_index
|
90
|
+
if current_index > @matches.size-1
|
91
|
+
current_index = 0
|
92
|
+
end
|
93
|
+
sel = @matches[current_index]
|
94
|
+
if sel
|
95
|
+
#$log.debug "XXXX sel #{sel}, #{@textsize} " if $log.debug?
|
96
|
+
if @textsize
|
97
|
+
t = @field.getvalue
|
98
|
+
$log.debug "XXXX t #{t}:: matches:: #{@matches} " if $log.debug?
|
99
|
+
t.slice!(-@textsize..-1)
|
100
|
+
t << sel
|
101
|
+
|
102
|
+
#t << sel[@textsize..-1]
|
103
|
+
@field.set_buffer(t)
|
104
|
+
else
|
105
|
+
@field.set_buffer(@matches[current_index])
|
106
|
+
end
|
107
|
+
@field.cursor_end
|
108
|
+
end # sel , otherwise matches was nil
|
109
|
+
# need to set cursor correctly.
|
110
|
+
clear_list
|
111
|
+
end
|
112
|
+
#end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def udisplay_list list1 # gmailfield
|
117
|
+
unless @commandwin
|
118
|
+
require 'rbcurse/core/util/rcommandwindow'
|
119
|
+
if @style == :old
|
120
|
+
h = @config.fetch(:height, 5)
|
121
|
+
w = @config.fetch(:width, Ncurses.COLS-1)
|
122
|
+
top = @config.fetch(:top, Ncurses.LINES-6)
|
123
|
+
left = @config.fetch(:left, 0)
|
124
|
+
else
|
125
|
+
hj = list1.size+2
|
126
|
+
hj = [hj, 10].min
|
127
|
+
h = @config.fetch(:height, hj)
|
128
|
+
w = @config.fetch(:width, list1[0].size+5)
|
129
|
+
top = @config.fetch(:top, @field.row+1 )
|
130
|
+
left = @config.fetch(:left, @field.col)
|
131
|
+
end
|
132
|
+
layout = { :height => h, :width => w, :top => top, :left => left }
|
133
|
+
rc = CommandWindow.new nil, :layout => layout, :box => :border #, :title => config[:title]
|
134
|
+
@commandwin = rc
|
135
|
+
end
|
136
|
+
begin
|
137
|
+
@commandwin.clear
|
138
|
+
@commandwin.udisplay_list list1
|
139
|
+
ensure
|
140
|
+
#rc.destroy
|
141
|
+
#rc = nil
|
142
|
+
end
|
143
|
+
end
|
144
|
+
def clear_list # gmailfield
|
145
|
+
if @commandwin
|
146
|
+
@commandwin.destroy
|
147
|
+
@commandwin = nil
|
148
|
+
end
|
149
|
+
@list_object = nil
|
150
|
+
end
|
151
|
+
end
|
152
|
+
module AppgCompose
|
153
|
+
class GmailCompose
|
154
|
+
def initialize gmail=nil, config={}
|
155
|
+
@gmail = gmail
|
156
|
+
@username = config[:username]
|
157
|
+
@password = config[:password]
|
158
|
+
@config = config
|
159
|
+
#@to = config[:to]
|
160
|
+
#@cc = config[:cc]
|
161
|
+
#@bcc = config[:bcc]
|
162
|
+
#@subject = config[:subject]
|
163
|
+
yield self if block_given?
|
164
|
+
end
|
165
|
+
def __udisplay_list list1 # gmailcompose
|
166
|
+
warn "is this used ?"
|
167
|
+
unless @commandwin
|
168
|
+
require 'rbcurse/core/util/rcommandwindow'
|
169
|
+
layout = { :height => 5, :width => Ncurses.COLS-1, :top => Ncurses.LINES-6, :left => 0 }
|
170
|
+
rc = CommandWindow.new nil, :layout => layout, :box => true #, :title => config[:title]
|
171
|
+
@commandwin = rc
|
172
|
+
end
|
173
|
+
begin
|
174
|
+
#w = rc.window
|
175
|
+
# may need to clear first
|
176
|
+
@commandwin.clear
|
177
|
+
@commandwin.udisplay_list list1
|
178
|
+
ensure
|
179
|
+
#rc.destroy
|
180
|
+
#rc = nil
|
181
|
+
end
|
182
|
+
end
|
183
|
+
def __clear_list # gmailcompose
|
184
|
+
warn "is this used"
|
185
|
+
if @commandwin
|
186
|
+
@commandwin.destroy
|
187
|
+
@commandwin = nil
|
188
|
+
end
|
189
|
+
@list_object = nil
|
190
|
+
end
|
191
|
+
def run
|
192
|
+
ss = self
|
193
|
+
#$log.debug "XXX self #{ss.class} " if $log.debug?
|
194
|
+
App.new(:caller => ss) do |app|
|
195
|
+
#@caller = app.config[:caller]
|
196
|
+
#$log.debug " inside XXX new app #{@caller} "
|
197
|
+
$log.debug " inside XXX new app config #{app.config} "
|
198
|
+
def get_commands
|
199
|
+
%w{ insert_file}
|
200
|
+
end
|
201
|
+
header = app.app_header "rbcurse #{Rbcurse::VERSION}", :text_center => "Compose Mail", :text_right =>"27% Stronger", :color => :black, :bgcolor => :white, :attr => :bold
|
202
|
+
app.message "Press F10 to exit from here"
|
203
|
+
app.stack :margin_top => 2, :margin => 5, :width => 15 do |xxx|
|
204
|
+
fg = :white
|
205
|
+
bg = :blue
|
206
|
+
app.label :text => "To", :color => fg, :bgcolor => bg, :display_length => 10
|
207
|
+
app.label :text => "Subject", :color => fg, :bgcolor => bg, :display_length => 10
|
208
|
+
app.label :text => "Cc", :color => fg, :bgcolor => bg, :display_length => 10
|
209
|
+
app.label :text => "Body", :color => fg, :bgcolor => bg, :display_length => 10
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
ww = 80
|
214
|
+
app.stack :margin_top => 2, :margin => 25, :width => ww do |xxx|
|
215
|
+
file="contacts.yml"
|
216
|
+
to_list = []
|
217
|
+
if File.exists? file
|
218
|
+
require 'yaml'
|
219
|
+
contacts = YAML::load( File.open(file))
|
220
|
+
contacts.each { |e| to_list << "\"#{e[0]}\" <#{e[1]}>" }
|
221
|
+
else
|
222
|
+
to_list = ['"Matz " <matz@ruby.com>', '"James E Gray" <james@gmail.com>', '"Steve Jobs" <jobs@apple.com>', '"Bram Moolenar", <bmool@vim.com>', '"Mental Guy", <mentalguy@me.com>' ]
|
223
|
+
end
|
224
|
+
@to = app.field "to", :maxlen => 100, :bgcolor => :white, :color => :black
|
225
|
+
GmailField.new @to, to_list
|
226
|
+
@to.text = @config[:to]
|
227
|
+
@subject = app.field "subject", :maxlen => 100, :display_length => nil , :bgcolor => :white, :color => :black
|
228
|
+
@subject.text = @config[:subject]
|
229
|
+
@cc = app.field "cc", :maxlen => 100, :display_length => nil , :bgcolor => :white, :color => :black
|
230
|
+
GmailField.new @cc, to_list #, {:top => @cc.row+1, :left => @cc.col, :width => 40 }
|
231
|
+
@cc.text = @config[:cc]
|
232
|
+
@body = app.textarea :height => 10 do |e|
|
233
|
+
#e.source.get_text
|
234
|
+
end
|
235
|
+
app.hline :width => ww
|
236
|
+
app.flow do |xxx|
|
237
|
+
app.button "&Post" do
|
238
|
+
alert "To field is nil in button itself" if @to.nil?
|
239
|
+
app.message "posting letter"
|
240
|
+
#@caller = app.config[:caller]
|
241
|
+
status = post_letter(self, :to => @to, :subject => @subject, :body => @body)
|
242
|
+
app.message "posted email: status #{status} "
|
243
|
+
end
|
244
|
+
app.button "&Save" do
|
245
|
+
app.message "saving to file"
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
end # stack
|
250
|
+
end # app
|
251
|
+
|
252
|
+
end # run
|
253
|
+
# now we can access to and subject and body directly
|
254
|
+
# post an email
|
255
|
+
def post_letter app, config={}
|
256
|
+
#alert "i can access to directly. #{@to.getvalue} " if @to
|
257
|
+
to = config[:to]
|
258
|
+
subject = config[:subject]
|
259
|
+
body = config[:body]
|
260
|
+
if to.nil?
|
261
|
+
alert "To is nil. Some programming error"
|
262
|
+
return false
|
263
|
+
end
|
264
|
+
unless @username
|
265
|
+
ww = ENV['GMAIL_USER']
|
266
|
+
#ww << "@gmail.com" if ww # suddnely giving frozen string error
|
267
|
+
ww = ww + "@gmail.com" if ww
|
268
|
+
@username = ask("Username: ") { |q| q.default = ww }
|
269
|
+
end
|
270
|
+
unless @password
|
271
|
+
ww = ENV['GMAIL_PASS']
|
272
|
+
@password = ask("Password: ") { |q| q.default = ww; q.echo ="*" }
|
273
|
+
end
|
274
|
+
if @username.nil? || @password.nil?
|
275
|
+
say_with_pause "Cannot proceeed without user and pass"
|
276
|
+
return false
|
277
|
+
end
|
278
|
+
unless @gmail
|
279
|
+
say "Connecting to gmail..."
|
280
|
+
begin
|
281
|
+
@gmail = Gmail.connect!(@username, @password)
|
282
|
+
rescue => ex
|
283
|
+
@password = @username = nil
|
284
|
+
$log.debug( ex) if ex
|
285
|
+
$log.debug(ex.backtrace.join("\n")) if ex
|
286
|
+
end
|
287
|
+
end
|
288
|
+
unless @gmail
|
289
|
+
say_with_pause "Cannot proceeed without connection" unless @gmail
|
290
|
+
return false
|
291
|
+
end
|
292
|
+
|
293
|
+
body = body.get_text
|
294
|
+
to = to.getvalue
|
295
|
+
subject = subject.getvalue
|
296
|
+
say "Posting #{subject} to #{to} "
|
297
|
+
if body
|
298
|
+
@gmail.deliver do
|
299
|
+
to to
|
300
|
+
subject subject
|
301
|
+
body body
|
302
|
+
end
|
303
|
+
else
|
304
|
+
alert "Got no body"
|
305
|
+
end # body
|
306
|
+
true
|
307
|
+
end # post_letter
|
308
|
+
|
309
|
+
end # class
|
310
|
+
end # module
|
311
|
+
if __FILE__ == $PROGRAM_NAME
|
312
|
+
a = AppgCompose::GmailCompose.new
|
313
|
+
a.run
|
314
|
+
end
|
315
|
+
|
data/examples/atree.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rbcurse/core/util/app'
|
2
|
+
|
3
|
+
App.new do
|
4
|
+
header = app_header "rbcurse #{Rbcurse::VERSION}", :text_center => "Tree Demo", :text_right =>"New Improved!", :color => :black, :bgcolor => :white, :attr => :bold
|
5
|
+
message "Press Enter to expand/collapse"
|
6
|
+
@form.bind_key(FFI::NCurses::KEY_F3) {
|
7
|
+
require 'rbcurse/core/util/viewer'
|
8
|
+
RubyCurses::Viewer.view("rbc13.log", :close_key => KEY_RETURN, :title => "<Enter> to close")
|
9
|
+
}
|
10
|
+
|
11
|
+
stack :margin_top => 2, :margin => 5, :width => 30 do
|
12
|
+
tm = nil
|
13
|
+
atree = tree :height => 10, :title => "ruby way" do
|
14
|
+
root "root" do
|
15
|
+
branch "hello" do
|
16
|
+
leaf "ruby"
|
17
|
+
end
|
18
|
+
branch "goodbye" do
|
19
|
+
leaf "java"
|
20
|
+
leaf "verbosity"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
found=atree.get_node_for_path "goodbye"
|
25
|
+
atree.set_expanded_state(atree.root, true)
|
26
|
+
atree.set_expanded_state(found,true)
|
27
|
+
|
28
|
+
# using a Hash
|
29
|
+
model = { :ruby => [ "jruby", {:mri => %W[ 1.8.6 1.8.7]}, {:yarv => %W[1.9.1 1.9.2]}, "rubinius", "macruby" ], :python => %W[ cpython jython laden-swallow ] }
|
30
|
+
tree :data => model, :title => "Hash"
|
31
|
+
|
32
|
+
end # stack
|
33
|
+
stack :margin_top => 2, :margin => 40, :width => 30 do
|
34
|
+
|
35
|
+
# using an Array
|
36
|
+
tree :data => %W[ ruby cobol jruby smalltalk fortran piethon purrl lithp ], :title=> "Array"
|
37
|
+
|
38
|
+
# long way ISO 9001 certifed, SEI CMM 5 compliant
|
39
|
+
#
|
40
|
+
root = TreeNode.new "ROOT"
|
41
|
+
subroot = TreeNode.new "subroot"
|
42
|
+
leaf1 = TreeNode.new "leaf 1"
|
43
|
+
leaf2 = TreeNode.new "leaf 2"
|
44
|
+
model = DefaultTreeModel.new root
|
45
|
+
#model.insert_node_into(subroot, root, 0) # BLEAH JAVA !!
|
46
|
+
|
47
|
+
# slightly better, since we return self in ruby
|
48
|
+
root << subroot
|
49
|
+
subroot << leaf1 << leaf2
|
50
|
+
leaf1 << "leaf11"
|
51
|
+
leaf1 << "leaf12"
|
52
|
+
|
53
|
+
# more rubyish way
|
54
|
+
root.add "blocky", true do
|
55
|
+
add "block2"
|
56
|
+
add "block3" do
|
57
|
+
add "block31"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
tree :data => model, :title => "legacy way"
|
62
|
+
|
63
|
+
end
|
64
|
+
end # app
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# ----------------------------------------------------------------------------- #
|
2
|
+
# File: file.rb
|
3
|
+
# Description: some common file related methods which can be used across
|
4
|
+
# file manager demos, since we seems to have a lot of them :)
|
5
|
+
# Author: rkumar http://github.com/rkumar/rbcurse/
|
6
|
+
# Date: 2011-11-15 - 19:54
|
7
|
+
# License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
|
8
|
+
# Last update: use ,,L
|
9
|
+
# ----------------------------------------------------------------------------- #
|
10
|
+
#
|
11
|
+
|
12
|
+
require 'rbcurse/core/include/appmethods'
|
13
|
+
module RubyCurses
|
14
|
+
def file_edit fp #=@current_list.filepath
|
15
|
+
#$log.debug " edit #{fp}"
|
16
|
+
editor = ENV['EDITOR'] || 'vi'
|
17
|
+
vimp = %x[which #{editor}].chomp
|
18
|
+
shell_out "#{vimp} #{fp}"
|
19
|
+
end
|
20
|
+
|
21
|
+
# TODO we need to move these to some common file so differnt programs and demos
|
22
|
+
# can use them on pressing space or enter.
|
23
|
+
def file_page fp #=@current_list.filepath
|
24
|
+
ft=%x[file #{fp}]
|
25
|
+
if ft.index("text")
|
26
|
+
pager = ENV['PAGER'] || 'less'
|
27
|
+
vimp = %x[which #{pager}].chomp
|
28
|
+
shell_out "#{vimp} #{fp}"
|
29
|
+
elsif ft.index(/zip/i)
|
30
|
+
shell_out "tar tvf #{fp} | less"
|
31
|
+
elsif ft.index(/directory/i)
|
32
|
+
shell_out "ls -lh #{fp} | less"
|
33
|
+
else
|
34
|
+
alert "#{fp} is not text, not paging "
|
35
|
+
#use_on_file "als", fp # only zip or archive
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end # module
|
40
|
+
include RubyCurses
|
@@ -0,0 +1,257 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# WARNING. This file is required by appemail.rb.
|
3
|
+
#
|
4
|
+
# Supply the path of any mbox file such as ~/mbox or ~/mail/read-mail
|
5
|
+
# and see a listing of mails, then give a msg number and see body
|
6
|
+
# You may run this with -EASCII-8BIT if it crashes with a UTF8 error.
|
7
|
+
#require 'mailread' # i've copied it in here 2011-09-18
|
8
|
+
require 'date'
|
9
|
+
|
10
|
+
# a sample mail formatter class
|
11
|
+
# Does formatting just like alpine
|
12
|
+
# Each message is passed in the format method, thus this is reused
|
13
|
+
class MailFormatter
|
14
|
+
attr_reader :message
|
15
|
+
attr_reader :header
|
16
|
+
def initialize #message
|
17
|
+
#@message = message
|
18
|
+
#@header = message.header
|
19
|
+
end
|
20
|
+
|
21
|
+
def date
|
22
|
+
raise StandardError, "pass message first using format()" unless @header
|
23
|
+
m = @header
|
24
|
+
date = Date.parse(m['Date'])
|
25
|
+
date = date.strftime("%b %d")
|
26
|
+
end
|
27
|
+
|
28
|
+
def from
|
29
|
+
raise StandardError, "pass message first using format()" unless @header
|
30
|
+
m = @header
|
31
|
+
match = m['From'].to_s.match(/\((.*)\)/)
|
32
|
+
from = m['From']
|
33
|
+
if match
|
34
|
+
from = match[1]
|
35
|
+
else
|
36
|
+
match = m['From'].to_s.match(/(.*)</)
|
37
|
+
from = match[1] if match
|
38
|
+
end
|
39
|
+
from
|
40
|
+
end
|
41
|
+
def body
|
42
|
+
raise StandardError, "pass message first using format()" unless @message
|
43
|
+
@message.body
|
44
|
+
end
|
45
|
+
|
46
|
+
def subject
|
47
|
+
raise StandardError, "pass message first using format()" unless @header
|
48
|
+
m = @header
|
49
|
+
m['Subject']
|
50
|
+
end
|
51
|
+
def status
|
52
|
+
raise StandardError, "pass message first using format()" unless @header
|
53
|
+
m = @header
|
54
|
+
m['Status']
|
55
|
+
end
|
56
|
+
|
57
|
+
def format message, count
|
58
|
+
@header = message.header
|
59
|
+
#@message = message
|
60
|
+
m = message.header
|
61
|
+
"%2s %2d %s %-25s %-s\n" % [ status, count, date, from, subject ]
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_array message, count
|
65
|
+
@header = message.header
|
66
|
+
#@message = message
|
67
|
+
m = message.header
|
68
|
+
#printf("#{attr}%2s %2d %s |%-25s| %-s#{attre}\n", m['Status'], count, date, from, msg.header['Subject'])
|
69
|
+
row = [ status, count, date, from, m['Subject'] ]
|
70
|
+
end
|
71
|
+
end # class
|
72
|
+
# opens and maintains an mbox mailbox
|
73
|
+
class Mbox
|
74
|
+
include Enumerable
|
75
|
+
attr_reader :folder
|
76
|
+
attr_reader :mails
|
77
|
+
attr_reader :unread_count
|
78
|
+
attr_reader :read_count
|
79
|
+
|
80
|
+
# takes a mailbox name, e,g., mbox
|
81
|
+
# Does not update, since this is just a demo
|
82
|
+
def initialize folder
|
83
|
+
raise ArgumentError, "#{folder} not a valid file. Pls supply correct path." if !File.exists? folder
|
84
|
+
@folder = folder
|
85
|
+
@unread_count = 0
|
86
|
+
@read_count = 0
|
87
|
+
@mboxh = {}
|
88
|
+
@mbox_counts = {}
|
89
|
+
@mails = nil
|
90
|
+
@formatter = nil
|
91
|
+
parse
|
92
|
+
end
|
93
|
+
def parse &block # :yields: Mail
|
94
|
+
mbox = File.open(@folder)
|
95
|
+
count = lines = 0
|
96
|
+
unread_count = 0
|
97
|
+
read_count = 0
|
98
|
+
# array of mails
|
99
|
+
mails = []
|
100
|
+
|
101
|
+
# read up the warning message, we don't want it in our array
|
102
|
+
msg = Mail.new(mbox)
|
103
|
+
|
104
|
+
while !mbox.eof?
|
105
|
+
msg = Mail.new(mbox)
|
106
|
+
count += 1
|
107
|
+
s = msg.header['Status']
|
108
|
+
if s == "O"
|
109
|
+
unread_count += 1
|
110
|
+
else
|
111
|
+
read_count += 1
|
112
|
+
end
|
113
|
+
mails << msg
|
114
|
+
yield msg if block_given?
|
115
|
+
end
|
116
|
+
@mails = mails
|
117
|
+
#@mbox_counts[@folder] = [unread_count, read_count]
|
118
|
+
@unread_count, @read_count = unread_count, read_count
|
119
|
+
mbox.close
|
120
|
+
end
|
121
|
+
def each # :yields: message
|
122
|
+
parse unless @mails
|
123
|
+
@mails.each { |e| yield e }
|
124
|
+
end
|
125
|
+
def formatted_each &block
|
126
|
+
fmt = @formatter || MailFormatter.new
|
127
|
+
each_with_index do |msg, index|
|
128
|
+
yield fmt.format msg, index + 1
|
129
|
+
end
|
130
|
+
end
|
131
|
+
# returns an array for each entry which can be used with tabularwidget
|
132
|
+
def array_each &block
|
133
|
+
fmt = @formatter || MailFormatter.new
|
134
|
+
each_with_index do |msg, index|
|
135
|
+
yield fmt.create_array msg, index + 1
|
136
|
+
end
|
137
|
+
end
|
138
|
+
# set a formatter object, if you wish to override default one
|
139
|
+
def formatter fmt
|
140
|
+
@formatter = fmt
|
141
|
+
end
|
142
|
+
end
|
143
|
+
# opens and maintains a hash of mboxes
|
144
|
+
# does not see to add any value other than being just a hash !
|
145
|
+
class MboxManager
|
146
|
+
def initialize
|
147
|
+
@boxes = {}
|
148
|
+
@current_name = nil
|
149
|
+
end
|
150
|
+
def use folder
|
151
|
+
if !@boxes.has_key? folder
|
152
|
+
@mails = Mbox.new folder
|
153
|
+
@boxes[folder] = @mails
|
154
|
+
else
|
155
|
+
@mails = @boxes[folder]
|
156
|
+
end
|
157
|
+
@current_name = folder
|
158
|
+
@mails
|
159
|
+
end
|
160
|
+
def mails folder=@current_name
|
161
|
+
use folder
|
162
|
+
@mails
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# The Mail class represents an internet mail message (as per RFC822, RFC2822)
|
167
|
+
# with headers and a body.
|
168
|
+
class Mail
|
169
|
+
|
170
|
+
# Create a new Mail where +f+ is either a stream which responds to gets(),
|
171
|
+
# or a path to a file. If +f+ is a path it will be opened.
|
172
|
+
#
|
173
|
+
# The whole message is read so it can be made available through the #header,
|
174
|
+
# #[] and #body methods.
|
175
|
+
#
|
176
|
+
# The "From " line is ignored if the mail is in mbox format.
|
177
|
+
def initialize(f)
|
178
|
+
unless defined? f.gets
|
179
|
+
f = open(f, "r")
|
180
|
+
opened = true
|
181
|
+
end
|
182
|
+
|
183
|
+
@header = {}
|
184
|
+
@body = []
|
185
|
+
begin
|
186
|
+
while line = f.gets()
|
187
|
+
line.chop!
|
188
|
+
# Added encode by RK since crashing with UTF-8 error
|
189
|
+
line = line.encode("ASCII-8BIT", :invalid => :replace, :undef => :replace, :replace => "?")
|
190
|
+
next if /^From /=~line # skip From-line
|
191
|
+
break if /^$/=~line # end of header
|
192
|
+
|
193
|
+
if /^(\S+?):\s*(.*)/=~line
|
194
|
+
(attr = $1).capitalize!
|
195
|
+
@header[attr] = $2
|
196
|
+
elsif attr
|
197
|
+
line.sub!(/^\s*/, '')
|
198
|
+
@header[attr] += "\n" + line
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
return unless line
|
203
|
+
|
204
|
+
while line = f.gets()
|
205
|
+
# Added encode by RK since crashing with UTF-8 error
|
206
|
+
line = line.encode("ASCII-8BIT", :invalid => :replace, :undef => :replace, :replace => "?")
|
207
|
+
#line = line.encode('ASCII-8BIT') # added RK
|
208
|
+
break if /^From /=~line
|
209
|
+
@body.push(line)
|
210
|
+
end
|
211
|
+
ensure
|
212
|
+
f.close if opened
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Return the headers as a Hash.
|
217
|
+
def header
|
218
|
+
return @header
|
219
|
+
end
|
220
|
+
|
221
|
+
# Return the message body as an Array of lines
|
222
|
+
def body
|
223
|
+
return @body
|
224
|
+
end
|
225
|
+
|
226
|
+
# Return the header corresponding to +field+.
|
227
|
+
#
|
228
|
+
# Matching is case-insensitive.
|
229
|
+
def [](field)
|
230
|
+
@header[field.capitalize]
|
231
|
+
end
|
232
|
+
end
|
233
|
+
#puts mails.size
|
234
|
+
if __FILE__ == $PROGRAM_NAME
|
235
|
+
MAILBOX = ARGV[0] || "mbox"
|
236
|
+
mx = Mbox.new MAILBOX
|
237
|
+
mx.formatted_each do |str|
|
238
|
+
puts str
|
239
|
+
end
|
240
|
+
mails = mx.mails
|
241
|
+
BOLD = "\e[1m"
|
242
|
+
CLEAR = "\e[0m"
|
243
|
+
|
244
|
+
# ask user for a number and print body for that
|
245
|
+
while true
|
246
|
+
print "Enter a mail number [1 to #{mails.size}]:"
|
247
|
+
n = STDIN.gets.chomp
|
248
|
+
break if n.nil? || n.empty?
|
249
|
+
msg = mails[n.to_i-1]
|
250
|
+
body = msg.body
|
251
|
+
puts
|
252
|
+
string= "#{msg.header['Subject']}"
|
253
|
+
puts "#{BOLD}#{string}#{CLEAR}"
|
254
|
+
puts "-" * string.length
|
255
|
+
puts body
|
256
|
+
end
|
257
|
+
end
|