rbcurse 1.1.5 → 1.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. data/CHANGELOG +45 -0
  2. data/Makefile +1 -1
  3. data/Manifest.txt +91 -0
  4. data/NOTES +349 -2
  5. data/README.markdown +12 -0
  6. data/VERSION +1 -1
  7. data/examples/abasiclist.rb +25 -0
  8. data/examples/alpmenu.rb +42 -0
  9. data/examples/app.rb +883 -0
  10. data/examples/appcombo.rb +17 -0
  11. data/examples/appdirtree.rb +73 -0
  12. data/examples/appemail.rb +164 -0
  13. data/examples/appemaillb.rb +308 -0
  14. data/examples/appgcompose.rb +303 -0
  15. data/examples/appgmail.rb +951 -0
  16. data/examples/atree.rb +56 -0
  17. data/examples/dirtree.rb +78 -0
  18. data/examples/focusmanager.rb +31 -0
  19. data/examples/imap.rb +48 -0
  20. data/examples/menu1.rb +79 -0
  21. data/examples/multispl.rb +86 -0
  22. data/examples/rfe.rb +3 -4
  23. data/examples/rmail.rb +188 -0
  24. data/examples/s.rb +10 -0
  25. data/examples/scrollbar.rb +104 -0
  26. data/examples/splitp.rb +56 -0
  27. data/examples/table1.rb +30 -0
  28. data/examples/term.rb +48 -0
  29. data/examples/term2.rb +54 -0
  30. data/examples/test1.rb +4 -2
  31. data/examples/test2.rb +9 -9
  32. data/examples/testapp.rb +44 -0
  33. data/examples/testapp2.rb +51 -0
  34. data/examples/testcombo.rb +2 -2
  35. data/examples/testgmail.rb +46 -0
  36. data/examples/testlistbox.rb +0 -1
  37. data/examples/testmultispl.rb +199 -0
  38. data/examples/testree.rb +127 -0
  39. data/examples/testscroller.rb +0 -1
  40. data/examples/testscrolllb.rb +1 -1
  41. data/examples/testscrollp.rb +2 -1
  42. data/examples/testscrollta.rb +1 -1
  43. data/examples/testscrolltable.rb +1 -2
  44. data/examples/testsplit.rb +1 -1
  45. data/examples/testsplit2.rb +1 -1
  46. data/examples/testsplit3.rb +1 -1
  47. data/examples/testsplit3_1.rb +1 -1
  48. data/examples/testsplit3a.rb +1 -1
  49. data/examples/testsplit3b.rb +1 -1
  50. data/examples/testsplitta.rb +1 -1
  51. data/examples/testsplittv.rb +1 -1
  52. data/examples/testsplittvv.rb +1 -1
  53. data/examples/testtodo.rb +491 -488
  54. data/examples/testvimsplit.rb +111 -0
  55. data/examples/todo.db +0 -0
  56. data/examples/todocsv.csv +28 -0
  57. data/examples/viewtodo.rb +408 -403
  58. data/lib/rbcurse/action.rb +1 -0
  59. data/lib/rbcurse/app.rb +1294 -0
  60. data/lib/rbcurse/applicationheader.rb +7 -2
  61. data/lib/rbcurse/checkboxcellrenderer.rb +0 -12
  62. data/lib/rbcurse/colormap.rb +34 -8
  63. data/lib/rbcurse/comboboxcellrenderer.rb +0 -11
  64. data/lib/rbcurse/defaultlistselectionmodel.rb +23 -7
  65. data/lib/rbcurse/extras/bottomline.rb +1681 -0
  66. data/lib/rbcurse/extras/directorylist.rb +445 -0
  67. data/lib/rbcurse/extras/directorytree.rb +69 -0
  68. data/lib/rbcurse/extras/divider.rb +310 -0
  69. data/lib/rbcurse/extras/focusmanager.rb +31 -0
  70. data/lib/rbcurse/extras/listselectable.rb +222 -0
  71. data/lib/rbcurse/extras/masterdetail.rb +164 -0
  72. data/lib/rbcurse/extras/menutree.rb +63 -0
  73. data/lib/rbcurse/extras/rlink.rb +27 -0
  74. data/lib/rbcurse/extras/rmenulink.rb +21 -0
  75. data/lib/rbcurse/extras/scrollbar.rb +134 -0
  76. data/lib/rbcurse/extras/stdscrwindow.rb +247 -0
  77. data/lib/rbcurse/extras/tabular.rb +258 -0
  78. data/lib/rbcurse/extras/tabularwidget.rb +1070 -0
  79. data/lib/rbcurse/extras/viewer.rb +106 -0
  80. data/lib/rbcurse/io.rb +137 -80
  81. data/lib/rbcurse/keylabelprinter.rb +4 -0
  82. data/lib/rbcurse/listcellrenderer.rb +91 -59
  83. data/lib/rbcurse/listscrollable.rb +93 -95
  84. data/lib/rbcurse/listselectable.rb +60 -7
  85. data/lib/rbcurse/ractionevent.rb +67 -0
  86. data/lib/rbcurse/rbasiclistbox.rb +688 -0
  87. data/lib/rbcurse/rcombo.rb +5 -5
  88. data/lib/rbcurse/rcommandwindow.rb +555 -0
  89. data/lib/rbcurse/rinputdataevent.rb +12 -0
  90. data/lib/rbcurse/rlistbox.rb +305 -124
  91. data/lib/rbcurse/rmenu.rb +99 -46
  92. data/lib/rbcurse/rmessagebox.rb +13 -6
  93. data/lib/rbcurse/rmulticontainer.rb +54 -93
  94. data/lib/rbcurse/rmultisplit.rb +731 -0
  95. data/lib/rbcurse/rmultitextview.rb +3 -2
  96. data/lib/rbcurse/rpopupmenu.rb +0 -1
  97. data/lib/rbcurse/rprogress.rb +117 -0
  98. data/lib/rbcurse/rscrollpane.rb +2 -1
  99. data/lib/rbcurse/rsplitpane.rb +94 -20
  100. data/lib/rbcurse/rsplitpane2.rb +1009 -0
  101. data/lib/rbcurse/rtabbedpane.rb +3 -2
  102. data/lib/rbcurse/rtabbedwindow.rb +0 -1
  103. data/lib/rbcurse/rtable.rb +92 -64
  104. data/lib/rbcurse/rtextarea.rb +91 -57
  105. data/lib/rbcurse/rtextview.rb +223 -70
  106. data/lib/rbcurse/rtree.rb +723 -0
  107. data/lib/rbcurse/rviewport.rb +2 -1
  108. data/lib/rbcurse/rvimsplit.rb +768 -0
  109. data/lib/rbcurse/rwidget.rb +524 -325
  110. data/lib/rbcurse/table/tablecellrenderer.rb +1 -1
  111. data/lib/rbcurse/table/tabledatecellrenderer.rb +0 -1
  112. data/lib/rbcurse/tree/treecellrenderer.rb +137 -0
  113. data/lib/rbcurse/tree/treemodel.rb +428 -0
  114. data/lib/rbcurse/vieditable.rb +14 -13
  115. data/lib/ver/ncurses.rb +6 -0
  116. data/lib/ver/window.rb +67 -32
  117. metadata +99 -23
  118. data/bin/rbcurse +0 -0
  119. data/examples/rvimsplit.rb +0 -376
  120. data/examples/todo.rb +0 -1
  121. data/lib/rbcurse/rform.rb +0 -845
  122. data/lib/rbcurse/selectable.rb +0 -94
  123. data/rbcurse.gemspec +0 -188
@@ -0,0 +1,303 @@
1
+ require 'rbcurse/app'
2
+ require 'gmail'
3
+
4
+ def insert_file
5
+ str = ask("File? ", Pathname) do |q|
6
+ q.completion_proc = Proc.new {|str| Dir.glob(str +"*").collect { |f| File.directory?(f) ? f+"/" : f } }
7
+ q.helptext = "Enter start of filename and tab to get completion"
8
+ end
9
+ message "we got #{str} "
10
+ end
11
+ ################
12
+ # Allow a field to have gmail like completion
13
+ # As a user types, a window highlights matches. Up and down arrow key may be used to select with Enter.
14
+ # A user may type Multiple email ids in field (space or comma sep), and matches for last word will be
15
+ # displayed.
16
+ # If you wish to use this in other ways:
17
+ # - match entire field not last word, then set matching to false or single
18
+ # - match_from_start will match your array values from start. If you want
19
+ # the pattern to match anywhere in array, set to false.
20
+ # - set height, width, top and left in config hash
21
+ # - block is intended to allow caller to override matching TODO
22
+ class GmailField
23
+ def initialize field, list=nil, config={}, &block
24
+ @field = field
25
+ @list = list
26
+ @config = config
27
+ @matching = config.fetch(:matching, :multiple)
28
+ @match_from_start = config.fetch(:match_from_start, true)
29
+ setup
30
+ end
31
+ def setup
32
+ @field.bind(:CHANGE) do |eve|
33
+ c = eve.source
34
+ text = c.getvalue
35
+ if text[-1] == " "
36
+ clear_list
37
+ else
38
+ pos = text.rindex(/, /)
39
+ if pos
40
+ text = text[pos+1..-1].strip if @matching == :multiple
41
+ end
42
+ @textsize = text.size
43
+ # \W instead of ^, will match start of word, lastname, emailid
44
+ patt = @match_from_start ? %r{\W#{text}}i : %r{#{text}}i
45
+ matches = @list.grep patt
46
+ @matches = matches
47
+ if @matches.size == 1 && @matches.first == text
48
+ clear_list
49
+ else
50
+ #$log.debug "XXXX #{self.class} matches #{matches} " if $log.debug?
51
+ #@caller = app.config[:caller]
52
+ if !matches.empty?
53
+ say " matches: #{matches.size}: #{matches} "
54
+ @list_object = udisplay_list matches
55
+ else
56
+ clear_list
57
+ #@caller.clear_list
58
+ end
59
+ end
60
+ end
61
+ end
62
+ @field.bind(:LEAVE) do |eve|
63
+ clear_list
64
+ end
65
+ @field.bind_key(KEY_DOWN) do
66
+ if @list_object
67
+ @list_object.press(KEY_DOWN)
68
+ #current_index = @list_object.current_index
69
+ @list_object.display_content
70
+ else
71
+ :UNHANDLED
72
+ end
73
+ end
74
+ @field.bind_key(KEY_UP) do
75
+ if @list_object
76
+ @list_object.press(KEY_UP)
77
+ #current_index = @list_object.current_index
78
+ @list_object.display_content
79
+ else
80
+ :UNHANDLED
81
+ end
82
+ end
83
+ @field.bind_key(13) do
84
+ #if @caller
85
+ if @list_object
86
+ @list_object.press(KEY_ENTER)
87
+ current_index = @list_object.current_index
88
+ if current_index > @matches.size-1
89
+ current_index = 0
90
+ end
91
+ sel = @matches[current_index]
92
+ if sel
93
+ #$log.debug "XXXX sel #{sel}, #{@textsize} " if $log.debug?
94
+ if @textsize
95
+ t = @field.getvalue
96
+ $log.debug "XXXX t #{t}:: matches:: #{@matches} " if $log.debug?
97
+ t.slice!(-@textsize..-1)
98
+ t << sel
99
+
100
+ #t << sel[@textsize..-1]
101
+ @field.set_buffer(t)
102
+ else
103
+ @field.set_buffer(@matches[current_index])
104
+ end
105
+ @field.cursor_end
106
+ end # sel , otherwise matches was nil
107
+ # need to set cursor correctly.
108
+ clear_list
109
+ end
110
+ #end
111
+ end
112
+ end
113
+
114
+ def udisplay_list list1
115
+ unless @commandwin
116
+ require 'rbcurse/rcommandwindow'
117
+ if @style == :old
118
+ h = @config.fetch(:height, 5)
119
+ w = @config.fetch(:width, Ncurses.COLS-1)
120
+ top = @config.fetch(:top, Ncurses.LINES-6)
121
+ left = @config.fetch(:left, 0)
122
+ else
123
+ hj = list1.size+2
124
+ hj = [hj, 10].min
125
+ h = @config.fetch(:height, hj)
126
+ w = @config.fetch(:width, list1[0].size+5)
127
+ top = @config.fetch(:top, @field.row+1 )
128
+ left = @config.fetch(:left, @field.col)
129
+ end
130
+ layout = { :height => h, :width => w, :top => top, :left => left }
131
+ rc = CommandWindow.new nil, :layout => layout, :box => :border #, :title => config[:title]
132
+ @commandwin = rc
133
+ end
134
+ begin
135
+ @commandwin.clear
136
+ @commandwin.udisplay_list list1
137
+ ensure
138
+ #rc.destroy
139
+ #rc = nil
140
+ end
141
+ end
142
+ def clear_list
143
+ if @commandwin
144
+ @commandwin.destroy
145
+ @commandwin = nil
146
+ end
147
+ @list_object = nil
148
+ end
149
+ end
150
+ module AppgCompose
151
+ class GmailCompose
152
+ def initialize gmail=nil, config={}
153
+ @gmail = gmail
154
+ @username = config[:username]
155
+ @password = config[:password]
156
+ @config = config
157
+ #@to = config[:to]
158
+ #@cc = config[:cc]
159
+ #@bcc = config[:bcc]
160
+ #@subject = config[:subject]
161
+ yield self if block_given?
162
+ end
163
+ def udisplay_list list1
164
+ unless @commandwin
165
+ require 'rbcurse/rcommandwindow'
166
+ layout = { :height => 5, :width => Ncurses.COLS-1, :top => Ncurses.LINES-6, :left => 0 }
167
+ rc = CommandWindow.new nil, :layout => layout, :box => true #, :title => config[:title]
168
+ @commandwin = rc
169
+ end
170
+ begin
171
+ #w = rc.window
172
+ # may need to clear first
173
+ @commandwin.clear
174
+ @commandwin.udisplay_list list1
175
+ ensure
176
+ #rc.destroy
177
+ #rc = nil
178
+ end
179
+ end
180
+ def clear_list
181
+ if @commandwin
182
+ @commandwin.destroy
183
+ @commandwin = nil
184
+ end
185
+ @list_object = nil
186
+ end
187
+ def run
188
+ ss = self
189
+ #$log.debug "XXX self #{ss.class} " if $log.debug?
190
+ App.new(:caller => ss) do |app|
191
+ #@caller = app.config[:caller]
192
+ #$log.debug " inside XXX new app #{@caller} "
193
+ $log.debug " inside XXX new app config #{app.config} "
194
+ def get_commands
195
+ %w{ insert_file}
196
+ end
197
+ header = app.app_header "rbcurse 1.2.0", :text_center => "Compose Mail", :text_right =>"27% Stronger", :color => :black, :bgcolor => :white, :attr => :bold
198
+ app.message "Press F1 to exit from here"
199
+ app.stack :margin_top => 2, :margin => 5, :width => 15 do |xxx|
200
+ fg = :white
201
+ bg = :blue
202
+ app.label :text => "To", :color => fg, :bgcolor => bg, :display_length => 10
203
+ app.label :text => "Subject", :color => fg, :bgcolor => bg, :display_length => 10
204
+ app.label :text => "Cc", :color => fg, :bgcolor => bg, :display_length => 10
205
+ app.label :text => "Body", :color => fg, :bgcolor => bg, :display_length => 10
206
+ end
207
+
208
+
209
+ ww = 80
210
+ app.stack :margin_top => 2, :margin => 25, :width => ww do |xxx|
211
+ file="contacts.yml"
212
+ to_list = []
213
+ if File.exists? file
214
+ require 'yaml'
215
+ contacts = YAML::load( File.open(file))
216
+ contacts.each { |e| to_list << "\"#{e[0]}\" <#{e[1]}>" }
217
+ else
218
+ to_list = %w{ oneness@gmail.com rahul@gmail.com rajiv@gmail.com amba@me.com andy@me.com }
219
+ end
220
+ @to = app.field "to", :maxlen => 100, :bgcolor => :white, :color => :black
221
+ GmailField.new @to, to_list
222
+ @to.text = @config[:to]
223
+ @subject = app.field "subject", :maxlen => 100, :display_length => nil , :bgcolor => :white, :color => :black
224
+ @subject.text = @config[:subject]
225
+ @cc = app.field "cc", :maxlen => 100, :display_length => nil , :bgcolor => :white, :color => :black
226
+ GmailField.new @cc, to_list #, {:top => @cc.row+1, :left => @cc.col, :width => 40 }
227
+ @cc.text = @config[:cc]
228
+ @body = app.textarea :height => 10 do |e|
229
+ #e.source.get_text
230
+ end
231
+ app.hline :width => ww
232
+ app.flow do |xxx|
233
+ app.button "&Post" do
234
+ alert "To field is nil in button itself" if @to.nil?
235
+ app.message "posting letter"
236
+ #@caller = app.config[:caller]
237
+ status = post_letter(self, :to => @to, :subject => @subject, :body => @body)
238
+ app.message "posted email: status #{status} "
239
+ end
240
+ app.button "&Save" do
241
+ app.message "saving to file"
242
+ end
243
+ end
244
+
245
+ end # stack
246
+ end # app
247
+
248
+ end # run
249
+ # now we can access to and subject and body directly
250
+ # post an email
251
+ def post_letter app, config={}
252
+ #alert "i can access to directly. #{@to.getvalue} " if @to
253
+ to = config[:to]
254
+ subject = config[:subject]
255
+ body = config[:body]
256
+ if to.nil?
257
+ alert "To is nil. Some programming error"
258
+ return false
259
+ end
260
+ unless @username
261
+ ww = ENV['GMAIL_USER'] + "@gmail.com"
262
+ @username = ask("Username: ") { |q| q.default = ww }
263
+ end
264
+ unless @password
265
+ ww = ENV['GMAIL_PASS']
266
+ @password = ask("Password: ") { |q| q.default = ww; q.echo ="*" }
267
+ end
268
+ if @username.nil? || @password.nil?
269
+ say_with_pause "Cannot proceeed without user and pass"
270
+ return false
271
+ end
272
+ unless @gmail
273
+ say "Connecting to gmail..."
274
+ @gmail = Gmail.connect!(@username, @password)
275
+ end
276
+ unless @gmail
277
+ say_with_pause "Cannot proceeed without connection" unless @gmail
278
+ return false
279
+ end
280
+
281
+ body = body.get_text
282
+ to = to.getvalue
283
+ subject = subject.getvalue
284
+ say "Posting #{subject} to #{to} "
285
+ if body
286
+ @gmail.deliver do
287
+ to to
288
+ subject subject
289
+ body body
290
+ end
291
+ else
292
+ alert "Got no body"
293
+ end # body
294
+ true
295
+ end # post_letter
296
+
297
+ end # class
298
+ end # module
299
+ if __FILE__ == $PROGRAM_NAME
300
+ a = AppgCompose::GmailCompose.new
301
+ a.run
302
+ end
303
+
@@ -0,0 +1,951 @@
1
+ require 'rbcurse/app'
2
+ require 'fileutils'
3
+ require 'yaml'
4
+ require 'gmail'
5
+ # You need gmail gem. (one of them depends on i18n gem).
6
+ # TODO start putting commands in a popup or menu bar
7
+ # TODO what if i want to hide sidebar and bring it back on later
8
+ # TODO switch mailbox or label on command line, with prompt letter indexing
9
+ # TODO
10
+ # TODO body does not show cc and date from reply_to etc
11
+ # TODO seems like gmail web preloads body so no delay, yet it remains unread XXX
12
+ # TODO: compose a message - what of contacts ? cc bcc
13
+ # TODO: reply.
14
+ # TODOx refresh, perhaps get unread and compare UIDS
15
+ # FIXME: reconnect gave allmain count as inbox count, clicking on lb2 gave nilclass
16
+ # TODOx : x highlight / select if pressing enter, so we know which one is being shwon
17
+ # _ should work with select also. Now we have a kind of mismatch between select
18
+ # and press (spacebar and enter)
19
+ # TODOx : cache envelope and body so not read repeatedly
20
+ # TODOD: C-w C-w should go between current and last. it does left and right
21
+ # TODO: option for only unseen mails
22
+ # FIX column widths so date also showm TODO
23
+ # # reduce width of left
24
+ # TODO: upper right should say NEW if N, also deleted can be D and not removed ??
25
+ # but there's no way to undelete.
26
+ # TODO: handling of packed (munpack) - temporary fix in place
27
+ # TODO : catch connectionreset and relogin : Connection reset by peer (Errno::ECONNRESET)
28
+ # TODO: unread on top, or only unread
29
+ # TODO: offline, download all mails (body too)
30
+ # TODO: select read, unread, by same author, starred, unstarred
31
+
32
+ class OpenedMessage < Struct.new(:uid, :message, :index)
33
+ def set uid, mess, index
34
+ $log.debug "XXX opened got index #{index} " if $log.debug?
35
+ @uid = uid
36
+ @message = mess
37
+ @index = index
38
+ $log.debug "XXX opened got @index #{@index} " if $log.debug?
39
+ end
40
+ end
41
+
42
+ #module RubyCurses
43
+ #class App
44
+ # putting some commands here so we can call from command_line
45
+ # ADD
46
+ def get_commands
47
+ opts = %w{ test testend archive delete markread markunread spam star unstar open header savecontact connect connectas compose refresh }
48
+ current_component = @vim.current_component
49
+ case current_component
50
+ when @lb2
51
+ opts.push *%w{ select nextm prev nextunread prevunread savecontact header }
52
+ when @tv
53
+ opts.push *%w{ saveas reply replyall nextm prev nextunread prevunread munpack }
54
+ end
55
+ opts
56
+ end
57
+ # indices can be array or range
58
+ # can pass one row as array, @lb2.selected_indices or range 20..25
59
+ # . delete
60
+ # .,+20 delete
61
+ # 1,4 archive
62
+ # .,$ select mark etc
63
+ def nextm n=1
64
+ ri = @lb2.real_index
65
+ ri = @opened_message.index
66
+ alert "ri in nil in nextm " unless ri
67
+ return unless ri
68
+ #@lb2.down
69
+ #return if ri == @lb2.real_index
70
+ if ri + n < @lb2.row_count
71
+ #@lb2.current_index += 1
72
+ open_mail(ri+n) #@lb2.real_index
73
+ else
74
+ raw_message "No more messages"
75
+ end
76
+ end
77
+ def prev
78
+ ri = @lb2.real_index
79
+ ri = @opened_message.index
80
+ return unless ri
81
+ #@lb2.up # this moves cursor to lb2
82
+ #return if ri == @lb2.real_index
83
+ if ri > 0
84
+ @lb2.current_index -= 1
85
+ open_mail @lb2.real_index
86
+ else
87
+ say "No previous messages", :color_pair => $prompt_color
88
+ end
89
+ end
90
+
91
+ # fetch body of message and display in textview
92
+ def open_mail index
93
+ @current_opened_index = index
94
+ row = @lb2[index]
95
+ unless row
96
+ say "Invalid row.", :color_pair => $prompt_color
97
+ return
98
+ end
99
+ if index >= 0
100
+ uid = row[UID_OFFSET] # UID_OFFSET=5
101
+ #uid = @message_uids[index]
102
+ #body = @gmail.connection.uid_fetch(uid, "BODY[TEXT]")[0].attr['BODY[TEXT]']
103
+ message_immediate "Fetching body from server ..."
104
+ body = uid_to_message( uid ).body # this uses gmail gem's cache
105
+ body = body.decoded.encode("ASCII-8BIT", :invalid => :replace, :undef => :replace, :replace => "?")
106
+ #@tv.set_content(body.to_s, :WRAP_WORD)
107
+ @current_uid = uid
108
+ @current_message = uid_to_message(uid)
109
+ env = @current_message.envelope
110
+ f = env.from[0]
111
+ t = env.to[0]
112
+ from = "From: #{env.from[0].name.to_s} <#{env.from[0].mailbox.to_s}@#{f.host.to_s}> "
113
+ to = "To: #{env.to[0].name.to_s} <#{env.to[0].mailbox.to_s}@#{t.host.to_s}> "
114
+ str = to
115
+ str << "\n" << from << "\n" << "Date: " << env.date << "\n" << "Subject: " << env.subject << "\n\n"
116
+ str << body
117
+ @tv.set_content(str, :WRAP_WORD)
118
+ @opened_message.set(uid, uid_to_message(uid), index)
119
+ @opened_message.uid = uid
120
+ @opened_message.message = uid_to_message(uid)
121
+ @opened_message.index = index
122
+ @current_body = body
123
+ row[0][0] = " " if row[0][0] == "N"
124
+
125
+ message "Done. "
126
+ @lb2.repaint_required true
127
+ @form.repaint
128
+ end
129
+ end
130
+ # i partial command entered then returns matches
131
+ def _resolve_command opts, cmd
132
+ return cmd if opts.include? cmd
133
+ matches = opts.grep Regexp.new("^#{cmd}")
134
+ end
135
+
136
+ def select which=nil
137
+ unless which
138
+ opts = %w{ all none read unread starred unstarred from subject invert current n}
139
+ which = ask("Select (TAB for options): ", opts) #{ |q| q.default = @previous_command }
140
+ end
141
+ which = which.to_sym
142
+ case which
143
+ when :all
144
+ @lb2.select_all
145
+ when :none
146
+ @lb2.clear_selection
147
+ when :invert
148
+ @lb2.invert_selection
149
+ when :unread
150
+ list = @lb2.list
151
+ list.each_with_index { |row, i|
152
+ if row[0][0]=="N"
153
+ @lb2.add_row_selection_interval(i,i)
154
+ end
155
+ }
156
+ when :read
157
+ list = @lb2.list
158
+ list.each_with_index { |row, i|
159
+ if row[0][0]=="N"
160
+ else
161
+ @lb2.add_row_selection_interval(i,i)
162
+ end
163
+ }
164
+ when :from
165
+ cv = @lb2.current_value
166
+ from = cv[1]
167
+ list = @lb2.list
168
+ list.each_with_index { |row, i|
169
+ if row[1] == from
170
+ @lb2.add_row_selection_interval(i,i)
171
+ end
172
+ }
173
+ when :n
174
+ n = ask("How many? ", Integer) {|q| q.in = 1..100}
175
+ ci = @lb2.current_index - 1 # header_adjustment
176
+ @lb2.add_row_selection_interval ci, ci+n-1
177
+ end
178
+ end
179
+
180
+ # just experimental, we load all mails on clicking a label anyway
181
+ # pick out all new mails in INBOX, compare to unread ids list
182
+ # and only add in those not in that list. Nothing great.
183
+
184
+ def refresh
185
+ #@gmail.inbox.find(:unread) do |email|
186
+ raw_message "Fetching ..."
187
+ ctr = 0
188
+ begin
189
+ @gmail.label("INBOX") do |mailbox|
190
+ unread = mailbox.emails(:unread)
191
+ total = unread.size
192
+ unread.each_with_index do |email, index|
193
+ uid = email.uid
194
+ if !@unreaduids.include? uid
195
+ @unreaduids << uid
196
+ ctr += 1
197
+ env = email.envelope
198
+ row = convert_message_to_row env, uid
199
+ @lb2.insert 0, row
200
+ end
201
+ raw_progress([index, total])
202
+ end
203
+ end
204
+ raw_message "#{ctr} new messages in inbox."
205
+ #refresh_labels
206
+ rescue => ex
207
+ $log.debug( "EXC refresh rescue reached. ")
208
+ print_error ex
209
+ end
210
+
211
+ end
212
+ # one place to write and display exception
213
+ def print_error ex
214
+ if ex
215
+ $log.debug( ex)
216
+ $log.debug(ex.backtrace.join("\n"))
217
+ message "EXCEPTION : #{ex} "
218
+ @message_label.repaint
219
+ @window.refresh
220
+ end
221
+ end
222
+ def convert_message_to_row envelope, uid
223
+ e = envelope
224
+ flag = @unreaduids.include?(uid) ? "N " : " "
225
+ if @starred_uids.include?(uid)
226
+ flag[1]="+"
227
+ end
228
+ date = e.date # .to_s #[5..10]
229
+ date = Date.parse(date).strftime("%b %d")
230
+ # name returns an Array, which crashes sort - therefore to_s, but says String
231
+ from = e.from[0].name.to_s
232
+ from = e.from[0].mailbox.to_s if from == ""
233
+ [ flag, from, e.subject ,date, uid]
234
+ end
235
+
236
+ def compose
237
+ # TODO make a separate screen damn you !
238
+ name = ask("To: ") # choices should be names from contacts
239
+ subject = ask("Subject: ")
240
+ # shell vim from here using temporary file
241
+ body = edit_text nil
242
+ message_immediate "sending message ... "
243
+ if body
244
+ @gmail.deliver do
245
+ to name
246
+ subject subject
247
+ body body
248
+ end
249
+ end
250
+ message "sent message to #{name} "
251
+ end
252
+
253
+ # needs to go into utils
254
+ def edit_text text
255
+ # 2010-06-29 10:24
256
+ require 'fileutils'
257
+ require 'tempfile'
258
+ ed = ENV['EDITOR'] || "vim"
259
+ temp = Tempfile.new "tmp"
260
+ File.open(temp,"w"){ |f| f.write text } if text
261
+ mtime = File.mtime(temp.path)
262
+ suspend() do
263
+ system(ed, temp.path)
264
+ end
265
+
266
+ newmtime = File.mtime(temp.path)
267
+ newstr = nil
268
+ if mtime < newmtime
269
+ # check timestamp, if updated ..
270
+ newstr = File.read(temp)
271
+ else
272
+ #puts "user quit without saving"
273
+ return nil
274
+ end
275
+ return newstr.chomp if newstr
276
+ return nil
277
+ end
278
+ def header
279
+ e = @lb2
280
+ env = get_current_message e
281
+ message_immediate "Fetching header ..."
282
+ header = env.header.to_s
283
+ case header
284
+ when String
285
+ header = header.split "\n"
286
+ end
287
+ view(header)
288
+ end
289
+ def savecontact
290
+ e = @lb2
291
+ env = get_current_message e
292
+ $log.debug "XXX ENV #{env} " if $log.debug?
293
+ name = env.from[0].name
294
+ id = "#{env.from[0].mailbox}@#{env.from[0].host}"
295
+
296
+ obj = nil
297
+ filename = "contacts.yml"
298
+ if File.exists? filename
299
+ obj = YAML::load_file( filename )
300
+ end
301
+ obj ||=[]
302
+ obj << [name, id]
303
+ File.open(filename, 'w' ) do |f|
304
+ f << obj.to_yaml
305
+ end
306
+ message "Written #{name} #{id} to #{filename} "
307
+ end
308
+ def connectas
309
+ user = ask "Emailid: "
310
+ return unless user
311
+ pass = ask("Password", String){ |q| q.echo = '*' }
312
+ return unless pass
313
+ gmail_connect(user, pass)
314
+ end
315
+ def connect
316
+ gmail_connect
317
+ end
318
+ def test
319
+ # creating a scratch window. should be put a textview in it ? or label ?
320
+ require 'rbcurse/rcommandwindow'
321
+ @layout = { :height => 5, :width => Ncurses.COLS-1, :top => Ncurses.LINES-5, :left => 0 }
322
+ rc = CommandWindow.new nil, :layout => @layout
323
+ w = rc.window
324
+ w.box(0,0)
325
+ w.printstring 1,1, "hello there!", $normalcolor, 'normal'
326
+ #rc.handle_keys
327
+ @rc = rc
328
+ end
329
+ def testend
330
+ @rc.destroy if @rc
331
+ @rc = nil
332
+ end
333
+ def archive
334
+ if @vim.current_component == @tv
335
+ archive_current
336
+ return
337
+ end
338
+ inds, ms = do_selected_rows(@lb2)
339
+ inds = inds.sort.reverse
340
+ inds.each { |e| @lb2.delete_at e; @messages.delete_at e }
341
+ @lb2.clear_selection
342
+ say " #{inds.size} messages archived"
343
+ Thread.new {
344
+ ms.each { |m|
345
+ m.archive!
346
+ }
347
+ }
348
+ end
349
+ # delete current mail, should be called from tv for opened row
350
+ # FIXME what if delete repeatedly. what if no next should be check UID
351
+ # when we fetch row
352
+ def delete_current
353
+ do_with_opened do |ri, message|
354
+ @lb2.delete_at ri
355
+ Thread.new { message.delete! }
356
+ end
357
+ # if we've delete then automatically next falls into place, no need to
358
+ # add one to idnex
359
+ nextm 0
360
+ end
361
+ def archive_current
362
+ do_with_opened do |ri, message|
363
+ @lb2.delete_at ri
364
+ Thread.new { message.archive! }
365
+ end
366
+ nextm 0
367
+
368
+ end
369
+ def do_with_opened
370
+ if @vim.current_component != @tv
371
+ say "not on tv. please open a mail and then delete"
372
+ return
373
+ end
374
+ ri = @lb2.real_index
375
+ ri = @opened_message.index
376
+ return if ri.nil? || ri < 1 # header_adjust
377
+ row = @lb2[ri]
378
+ rowuid = row[UID_OFFSET]
379
+ if @opened_message.uid != rowuid
380
+ alert "something wrong, uid not matching"
381
+ end
382
+ message = get_current_message
383
+ return unless message
384
+ yield ri, message if block_given?
385
+ end
386
+ def delete
387
+ if @vim.current_component == @tv
388
+ delete_current
389
+ return
390
+ end
391
+ e = @lb2
392
+ aproc = lambda {|m| m.delete! }
393
+ inds, ms = for_selected_rows(e, aproc) { |e| @lb2.delete_at e; @messages.delete_at e }
394
+ @lb2.clear_selection
395
+ say " #{inds.size} messages deleted" if inds
396
+ end
397
+ def saveas *args
398
+ @tv.saveas *args
399
+ end
400
+ def remove_current_label m
401
+ label = @current_label
402
+ m.remove_label! label
403
+ end
404
+ # return message object for a UID
405
+ # m.envelope
406
+ # m.header # < take a little time
407
+ def uid_to_message uid
408
+ m = @uid_message[uid]
409
+ end
410
+ # calls block for selected rows
411
+ def do_selected_rows(w) # :yield: row, msg
412
+ indices = []
413
+ messages = []
414
+ w.selected_rows.each { |row|
415
+ uid = w[row][UID_OFFSET] # UID_OFFSET
416
+ message = uid_to_message uid
417
+ next unless message
418
+ yield row, message if block_given?
419
+ indices << row
420
+ messages << message
421
+ }
422
+ return indices, messages
423
+ end
424
+ def get_current_uid w=@lb2
425
+ row = w.current_value
426
+ uid = row[UID_OFFSET] # UID_OFFSET
427
+ end
428
+ def get_current_message w=@lb2
429
+ uid = get_current_uid w
430
+ message = uid_to_message uid
431
+ end
432
+ def for_rows(indices, messageproc=nil)
433
+ case indices
434
+ when Integer
435
+ indices = [indices]
436
+ when Range
437
+ indices = indices.to_a
438
+ when :selected
439
+ indices = w.selected_rows
440
+ end
441
+ w = @lb2
442
+ messages = []
443
+ indices = indices.sort.reverse
444
+ indices.each { |row|
445
+ uid = w[row][UID_OFFSET] # UID_OFFSET
446
+ message = uid_to_message uid
447
+ next unless message
448
+ messages << message
449
+ }
450
+ return false unless indices
451
+ indices.each { |i| yield i if block_given? }
452
+ if messageproc
453
+ thr = Thread.new{
454
+ begin
455
+ messages.each { |m| messageproc.call(m) }
456
+ rescue => ex
457
+ $log.debug( "EXC for_selected_row rescue reached. ")
458
+ if ex
459
+ $log.debug( ex)
460
+ $log.debug(ex.backtrace.join("\n"))
461
+ message "EXCEPTION IN THREAD: #{ex} "
462
+ @message_label.repaint
463
+ @window.refresh
464
+ end
465
+ end
466
+ }
467
+ thr.abort_on_exception = true
468
+ end
469
+ return indices, messages
470
+ end
471
+ # for each selected row, execute the yield row indices to block
472
+ # typically for deleting from table, or updating visual status.
473
+ # Also call messageproc in a thread for each message since imap
474
+ # operations take a little time.
475
+ # indices are given in reverse order, so delete of rows in table
476
+ # works correctly.
477
+ #@return [false] if no selected rows
478
+ #@return [Array<Fixnum>, Array<messages>] visual indices in listbox, and related messages (envelopes)
479
+ def for_selected_rows(w, messageproc=nil)
480
+ indices = []
481
+ messages = []
482
+ w.selected_rows.each { |row|
483
+ uid = w[row][UID_OFFSET] # UID_OFFSET
484
+ message = uid_to_message uid
485
+ next unless message
486
+ indices << row
487
+ messages << message
488
+ }
489
+ return false unless indices
490
+ indices = indices.sort.reverse
491
+ indices.each { |i| yield i if block_given? }
492
+ if messageproc
493
+ thr = Thread.new{
494
+ begin
495
+ messages.each { |m| messageproc.call(m) }
496
+ rescue => ex
497
+ $log.debug( "EXC for_selected_row rescue reached. ")
498
+ if ex
499
+ $log.debug( ex)
500
+ $log.debug(ex.backtrace.join("\n"))
501
+ message "EXCEPTION IN THREAD: #{ex} "
502
+ @message_label.repaint
503
+ @window.refresh
504
+ end
505
+ end
506
+ }
507
+ thr.abort_on_exception = true
508
+ end
509
+ return indices, messages
510
+ end
511
+ # fetch envelopes for a label name
512
+ # and populates the right table
513
+ def get_envelopes text
514
+ @current_label = text
515
+ $break_fetch = false
516
+ #@message_uids = []
517
+ @messages = [] # hopefully unused
518
+ #Thread.new {
519
+ begin
520
+ ctr = 0
521
+ @gmail.label(text) do |mailbox|
522
+ # TODO. praps a progress bar also.
523
+ unreaduids = []
524
+ unread = mailbox.emails(:unread)
525
+ urc = unread.size
526
+ dispstr = " #{text} : unread #{urc} "
527
+ message_immediate dispstr
528
+ $unread_hash[text] = urc
529
+ allmails = mailbox.emails(:read)
530
+ allmails.insert 0, *unread
531
+ total = allmails.size
532
+ dispstr << " total: #{total} "
533
+ unread.each do |email|
534
+ unreaduids << email.uid
535
+ end
536
+ @unreaduids = unreaduids
537
+ dispstr << " getting UIDs .."
538
+ message_immediate dispstr
539
+ uids = []
540
+ allmails.each do |email|
541
+ uids << email.uid
542
+ @uid_message[email.uid] = email
543
+ end
544
+ message_immediate "getting envelopes. unread: #{urc} total: #{total} "
545
+ raw_progress 0.25
546
+
547
+ envelopes = @gmail.connection.uid_fetch(uids, "(UID ENVELOPE)")
548
+ # may need to reverse sort this !! TODO
549
+ # reversing means we've lost the link to UID !!! so we redo the list
550
+ return unless envelopes
551
+ lines = []
552
+ envelopes.reverse.each_with_index { |ee, index|
553
+ raw_progress([index+1, total])
554
+ e = ee.attr["ENVELOPE"]
555
+ uid = ee.attr["UID"]
556
+ #@message_uids << uid UNUSED
557
+ flag = unreaduids.include?(uid) ? "N " : " "
558
+ if @starred_uids.include?(uid)
559
+ flag[1]="+"
560
+ end
561
+ date = e.date # .to_s #[5..10]
562
+ date = Date.parse(date).strftime("%b %d")
563
+ #$log.debug "name: XXX #{e.from[0].name} "
564
+ #$log.debug "name: XXX #{e.from[0].class} " unless e.from[0].nil?
565
+ # name returns an Array, which crashes sort - therefore to_s, but says String
566
+ from = e.from[0].name.to_s # why blank some times FIXME
567
+ from = e.from[0].mailbox.to_s if from == ""
568
+ #@lb2.append([ flag, ctr+1 , from, e.subject ,date])
569
+ #lines << [ flag, ctr+1 , from, e.subject ,date, uid]
570
+ lines << [ flag, from, e.subject ,date, uid]
571
+ ctr+=1
572
+ @messages << e
573
+ break if ctr >= @max_to_read
574
+ break if $break_fetch # can only happen in threaded mode
575
+ }
576
+ @lb2.estimate_column_widths=true # this sort of fails if we keep pushing a row at a time
577
+ @lb2.set_content lines
578
+ #message " #{text} showing #{ctr} of #{total} messages"
579
+ #@message_label.repaint
580
+ @form.repaint
581
+ end
582
+ rescue => ex
583
+ $log.debug( "EXC thread.rb rescue reached. ")
584
+ if ex
585
+ $log.debug( ex)
586
+ $log.debug(ex.backtrace.join("\n"))
587
+ message "EXCEPTION IN THREAD: #{ex} Reconnect using M-c"
588
+ @message_label.repaint
589
+ @window.refresh
590
+ gmail_connect # this should only happen in imap error not just any
591
+ end
592
+ end
593
+ #}
594
+ end
595
+ def get_starred gmail
596
+ @starred_uids = []
597
+ starred = @gmail.mailbox("[Gmail]/Starred")
598
+ starred.mails.each do |email|
599
+ @starred_uids << email.uid
600
+ end
601
+ $log.debug "XXX got starred #{@starred_uids.size} " if $log.debug?
602
+ end
603
+ # connect to gmail,
604
+ # but what if i want to change user - then we need to clear hashes.
605
+ def gmail_connect username=ENV['GMAIL_USER']+"@gmail.com", pass=ENV['GMAIL_PASS']
606
+ @gmail = Gmail.connect!(username, pass)
607
+ message_immediate "Connected to gmail, fetching labels ... "
608
+ @labels = @gmail.labels.all
609
+ message_immediate "Fetched labels. Click on a label. "
610
+ get_starred @gmail
611
+ @dirs.list @labels
612
+ @lb2.remove_all
613
+ @tv.remove_all
614
+ @form.repaint
615
+ # pull in inbox contents
616
+ # pull in unread for each label
617
+ #Thread.new { refresh_labels }
618
+ refresh_labels
619
+ @vim.focus @dirs
620
+ # place cursor on @dir since lb2 empty FIXME
621
+ end
622
+ # fetches labels and refreshes the unread counts
623
+ # I suspect there are issues here if this is in background, and someone presses enter
624
+ # on a label. he sees only unread -data inconsistency/race condition
625
+ def refresh_labels
626
+ #message_immediate " inside refresh labels "
627
+ raw_message "Getting label information..."
628
+ @labels ||= @gmail.labels.all
629
+ total = @labels.size
630
+ @labels.each_with_index { |text, index|
631
+ next if text == "[GMAIL]"
632
+ begin
633
+ @gmail.label(text) do |mailbox|
634
+ unread = mailbox.emails(:unread) # maybe this causes an issue internally
635
+ urc = unread.size
636
+ #message_immediate " mailbox #{text} has #{urc} unread "
637
+ $unread_hash[text] = urc
638
+ raw_progress([index+1, total])
639
+ end
640
+ rescue => ex
641
+ $log.debug " refresh_labels :: ERROR in mailbox #{text} ... #{ex}" if $log.debug?
642
+ next
643
+ end
644
+ }
645
+ @dirs.repaint_required(true)
646
+ message_immediate " Ready"
647
+ end
648
+ #end
649
+ #end
650
+
651
+ # START start
652
+ UID_OFFSET = 4
653
+ App.new do
654
+ #begin
655
+ @opened_message = OpenedMessage.new
656
+ ht = 24
657
+ @max_to_read = 100
658
+ @messages = nil # hopefully unused
659
+ @labels = nil
660
+ @current_label = nil
661
+ @message_uids = nil # uids of messages being displayed in @lb2 so as to get body
662
+ @starred_uids = nil
663
+ @uid_message = {} # map UID to a message
664
+ @unreaduids = [] # current labels unread
665
+ $unread_hash = {}
666
+ $message_hash = {}
667
+ @tv = nil
668
+ @current_body = nil
669
+ username = ENV['GMAIL_USER']+"@gmail.com"
670
+ pass = ENV['GMAIL_PASS']
671
+ @default_mailbox = "INBOX"
672
+ @gmail = nil
673
+ borderattrib = :reverse
674
+ @header = app_header "rbcurse 1.2.0", :text_center => "Yet Another Gmail Client that sucks", :text_right =>"", :color => :black, :bgcolor => :white#, :attr => Ncurses::A_BLINK
675
+ message "Press F1 to exit ...................................................."
676
+
677
+
678
+ stack :margin_top => 1, :margin => 0, :width => :EXPAND do
679
+
680
+ model = [" Fetching ..."]
681
+ # todo, get unread too and update, do that at some interval
682
+
683
+ @vim = master_detail :width => :EXPAND, :weight => 0.15 # TODO i want to change width of left container
684
+ # labels list on left
685
+ @dirs = list_box :list => model, :height => ht, :border_attrib => borderattrib, :suppress_borders => true
686
+ @dirs.one_key_selection = false
687
+
688
+ # we override so as to only print basename. Also, print unread count
689
+ def @dirs.convert_value_to_text(text, crow)
690
+ str = text.dup
691
+ if $unread_hash.has_key?(str)
692
+ str << " (#{$unread_hash[str]})"
693
+ else
694
+ str
695
+ end
696
+ end
697
+ @vim.set_left_component @dirs #, 0.25 # FIXME not having impact
698
+
699
+
700
+ #@mails = []
701
+ headings = %w{ __ From Subject Date UID }
702
+ @lb2 = tabular_widget :suppress_borders => true
703
+ # TODO set column widths since we are pushing one at a time.
704
+ @lb2.columns = headings
705
+ #@lb2.column_align 1, :right # earlier numbering as in alpine
706
+ #@lb2.column_align 0, :right
707
+ @lb2.column_hidden UID_OFFSET, true
708
+ @lb2.header_fgcolor :white
709
+ @lb2.header_bgcolor :cyan
710
+ @vim.set_right_top_component @lb2
711
+ Thread.new {
712
+ begin
713
+ @gmail = Gmail.connect!(username, pass)
714
+ message_immediate "Connected to gmail, fetching labels ... "
715
+ @labels = @gmail.labels.all
716
+ message_immediate "Fetched #{@labels.count} labels. Click on a label. "
717
+ @dirs.list @labels
718
+ @form.repaint
719
+ get_starred @gmail
720
+ message_immediate "Fetching #{@default_mailbox} " if @default_mailbox
721
+ get_envelopes @default_mailbox if @default_mailbox
722
+ #Thread.new { refresh_labels }
723
+ rescue => ex
724
+ if ex
725
+ $log.debug( ex)
726
+ $log.debug(ex.backtrace.join("\n"))
727
+ message "EXCEPTION IN THREAD: #{ex} "
728
+ @message_label.repaint
729
+ @window.refresh
730
+ end
731
+ end
732
+ }
733
+ @dirs.bind :PRESS do |e|
734
+ # TODO = methodize this so i can call it on startup
735
+ text = e.text # can this change if user goes down in dir2 YES
736
+ ci = e.source.current_index
737
+ @dirs.add_row_selection_interval ci, ci # show selected, this should happen on fire auto
738
+ message_immediate "Wait a few seconds ..."
739
+ # don't allow if alreadt inside this, since thread - or only allow one thread FIXME
740
+ # # TODO NOW WE NEED TO CACHE SINC we are not using gmail gem cache
741
+ @lines = []
742
+ @messages = [] # hopefully unused
743
+ #@lb2.remove_all
744
+ @lb2.estimate_column_widths=true # this sort of fails if we keep pushing a row at a time
745
+ # and repainting
746
+ get_envelopes text
747
+ end
748
+ # will only work in Thread mode
749
+ @dirs.bind_key(?q) { $break_fetch = true }
750
+ @dirs.bind_key(27) { $break_fetch = true }
751
+ @form.bind_key(?\M-p){
752
+ require 'live_console'
753
+
754
+ lc = LiveConsole.new :socket, :port => 4000, :bind => self.get_binding
755
+ lc.start # Starts the LiveConsole thread
756
+ alert "started console on 4000 #{self} "
757
+ # you would connect using "nc localhost 4000"
758
+ # if you use pp then it shows here too and mucks the screen.
759
+ # i think it writes on STDSCR - do not use pp and puts, just enter the variable
760
+ }
761
+ @form.bind_key(?\M-y){
762
+ # TODO previous command to be default
763
+ opts = %w{ test testend archive delete markread markunread spam star unstar open header savecontact connect connectas compose refresh }
764
+ current_component = @vim.current_component
765
+ case current_component
766
+ when @lb2
767
+ opts.push *%w{ select nextm prev nextunread prevunread savecontact header }
768
+ when @tv
769
+ opts.push *%w{ saveas reply replyall nextm prev nextunread prevunread munpack }
770
+ end
771
+ cmd = ask("Command: ", opts){ |q| q.default = @previous_command }
772
+ if cmd == ""
773
+ else
774
+ cmdline = cmd.split
775
+ cmd = cmdline.shift
776
+ # check if command is a substring of a larger command
777
+ if !opts.include?(cmd)
778
+ rcmd = _resolve_command(opts, cmd) if !opts.include?(cmd)
779
+ if rcmd.size == 1
780
+ cmd = rcmd.first
781
+ else
782
+ alert "Cannot resolve #{cmd}. Matches are: #{rcmd} "
783
+ end
784
+ end
785
+ if respond_to?(cmd, true)
786
+ @previous_command = cmd
787
+ raw_message "calling #{cmd} "
788
+ begin
789
+ send cmd, *cmdline
790
+ rescue => exc
791
+ $log.debug "ERR EXC: send throwing an exception now. Duh. IMAP keeps crashing haha !! #{exc} " if $log.debug?
792
+ print_error exc
793
+ end
794
+ else
795
+ say("Command [#{cmd}] not supported by #{self.class} ")
796
+ end
797
+ end
798
+ }
799
+
800
+ @form.bind_key(?\M-m){
801
+ @max_to_read = ask("How many mails to retrieve? ", Integer) { |q| q.in = 1..1000 }
802
+ }
803
+ # write file to disk so as to munpack it
804
+ @form.bind_key(?\M-s){
805
+ if @current_body
806
+ File.open("message.txt", 'w') {|f| f.write(@current_body) }
807
+ message_immediate "Written body as message.txt. You may use munpack on file"
808
+ end
809
+ }
810
+ @form.bind_key(?\M-c){
811
+ gmail_connect
812
+ }
813
+ @form.bind_key(?\M-C){
814
+ connectas
815
+ }
816
+ @lb2.bind :PRESS do |e|
817
+ case @lb2
818
+ when RubyCurses::TabularWidget
819
+ if e.action_command == :header
820
+ # now does sorting on multiple keys
821
+ else
822
+ ci = e.source.current_index # this should check what first data index is
823
+ index = ci - 1
824
+ open_mail index
825
+ #@current_opened_index = index
826
+ #row = @lb2[index]
827
+ #uid = row[UID_OFFSET] # UID_OFFSET=5
828
+ #if index >= 0
829
+ ##uid = @message_uids[index]
830
+ ##body = @gmail.connection.uid_fetch(uid, "BODY[TEXT]")[0].attr['BODY[TEXT]']
831
+ #message_immediate "Fetching body from server ..."
832
+ #body = uid_to_message( uid ).body # this uses gmail gem's cache
833
+ #body = body.decoded.encode("ASCII-8BIT", :invalid => :replace, :undef => :replace, :replace => "?")
834
+ #@tv.set_content(body, :WRAP_WORD)
835
+ ##@tv.set_content(body.to_s, :WRAP_WORD)
836
+ #@current_uid = uid
837
+ #@current_message = uid_to_message(uid)
838
+ #@opened_message.set(uid, uid_to_message(uid), index)
839
+ #@opened_message.uid = uid
840
+ #@opened_message.message = uid_to_message(uid)
841
+ #@opened_message.index = index
842
+ #@current_body = body
843
+ #row[0][0] = " " if row[0][0] == "N"
844
+ #$log.debug "XXX opened_message:: #{@opened_message} " if $log.debug?
845
+ #$log.debug "XXX opened_message index:: #{@opened_message.index}, #{index} " if $log.debug?
846
+
847
+ #message "Done. "
848
+ #@lb2.repaint_required true
849
+ #@form.repaint
850
+
851
+ ##@tv.set_content(@messages[index].body.to_s, :WRAP_WORD)
852
+ #end
853
+ end
854
+ else
855
+ @tv.set_content(@messages[e.source.current_index].body, :WRAP_WORD)
856
+ end
857
+ end
858
+ @lb2.bind :ENTER_ROW do |e|
859
+ @header.text_right "Row #{e.current_index} of #{@messages.size} "
860
+ end
861
+ @lb2.bind_key(?\M-a) do |e|
862
+ env = get_current_message e
863
+ $log.debug "XXX ENV #{env} " if $log.debug?
864
+ alert " #{env.from[0].name} :: #{env.from[0].mailbox}@#{env.from[0].host} "
865
+ $log.debug "XXX ENV HEADER #{env.header} " if $log.debug?
866
+ end
867
+ @lb2.bind_key(?U){ |e|
868
+ aproc = lambda {|m| m.mark(:unread) }
869
+ for_selected_rows(e, aproc) { |i|
870
+ row = @lb2[i]
871
+ row[0][0] = "N" if row[0][0] == " "
872
+ }
873
+ }
874
+ @lb2.bind_key(?I){ |e|
875
+ aproc = lambda {|m| m.mark(:read) }
876
+ for_selected_rows(e, aproc) { |i|
877
+ row = @lb2[i]
878
+ row[0][0] = " " if row[0][0] == "N"
879
+ }
880
+ }
881
+ # we have no way of knowing which ones are starred or unstarred, so can't show.
882
+ @lb2.bind_key(?s){ |e|
883
+ # looks like star and unstar don't work
884
+ raw_message "called star"
885
+ aproc = lambda {|m| m.star! }
886
+ for_selected_rows(e, aproc) { |i|
887
+ row = @lb2[i]
888
+ row[0][1] = "+" #if row[0][1] == " "
889
+ raw_message "called star for #{i} #{row[2]} "
890
+ }
891
+ }
892
+ @lb2.bind_key(?S){ |e|
893
+ aproc = lambda {|m| m.unstar! }
894
+ for_selected_rows(e, aproc) { |i|
895
+ row = @lb2[i]
896
+ row[0][1] = " " #if row[0][1] == "#"
897
+ }
898
+ }
899
+ # remove current label
900
+ @lb2.bind_key(?X){ |e|
901
+ label = @current_label
902
+ return unless label
903
+ aproc = lambda {|m| remove_current_label(m) }
904
+ inds, ms = for_selected_rows(e, aproc) { |e| @lb2.delete_at e; @messages.delete_at e }
905
+ @lb2.clear_selection
906
+ }
907
+ @lb2.bind_key(?#){ |e|
908
+ aproc = lambda {|m| m.delete! }
909
+ inds, ms = for_selected_rows(e, aproc) { |e| @lb2.delete_at e; @messages.delete_at e }
910
+ @lb2.clear_selection
911
+ }
912
+ @lb2.bind_key(?!){ |e|
913
+ aproc = lambda {|m| m.spam! }
914
+ inds, ms = for_selected_rows(e, aproc) { |e| @lb2.delete_at e; @messages.delete_at e }
915
+ @lb2.clear_selection
916
+ }
917
+ # archive
918
+ # this way of defining does not allow user to reassign this method to a key.
919
+ # we should put into a method in a class, so user can reassign
920
+ @lb2.bind_key(?\e){ |e|
921
+ inds, ms = do_selected_rows(e)
922
+ inds = inds.sort.reverse
923
+ inds.each { |e| @lb2.delete_at e; @messages.delete_at e }
924
+ @lb2.clear_selection
925
+ Thread.new {
926
+ ms.each { |m|
927
+ m.archive!
928
+ }
929
+ }
930
+
931
+ }
932
+ @lb2.bind_key(?\M-u){ @lb2.clear_selection }
933
+
934
+ @tv = @vim.set_right_bottom_component "Email body comes here. "
935
+ @tv.bind_key(?\M-m){ o = @tv.pipe_output('munpack', @current_body)
936
+ $log.debug "munpack returned #{o.size} " if $log.debug?
937
+ newfile = o.last.split(" ").first
938
+ $log.debug "munpack file #{newfile} " if $log.debug?
939
+ o = File.read(newfile) if File.exists?(newfile)
940
+ @tv.set_content o
941
+ # this leaves a file in current directory
942
+ }
943
+ @tv.bind_key(?\M-A) { |s| @tv.saveas }
944
+ @tv.suppress_borders true
945
+ @tv.border_attrib = borderattrib
946
+ end # stack
947
+ #ensure
948
+ #$log.debug "XX ENSURE !!! " if $log.debug?
949
+ #gmail.logout
950
+ #end
951
+ end # app