rbcurse 1.4.1 → 1.5.0.pre1

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