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,952 @@
|
|
1
|
+
require 'rbcurse/core/util/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/core/util/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 = listbox :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
|