jota 0.8.0
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/INSTALL +145 -0
- data/bin/jota +12 -0
- data/bin/jotacli +12 -0
- data/lib/app_error.rb +5 -0
- data/lib/cli.rb +277 -0
- data/lib/clip.rb +137 -0
- data/lib/clip_array.rb +393 -0
- data/lib/gui.rb +792 -0
- data/lib/helper.rb +129 -0
- data/lib/jota.glade +730 -0
- data/lib/jota.rb +176 -0
- data/lib/preferences.rb +83 -0
- data/lib/svn_info.rb +16 -0
- data/lib/version.rb +59 -0
- data/tests/test_all.rb +10 -0
- data/tests/test_clip.rb +172 -0
- data/tests/test_clip_array.rb +177 -0
- data/tests/test_preferences.rb +185 -0
- metadata +81 -0
data/lib/clip_array.rb
ADDED
@@ -0,0 +1,393 @@
|
|
1
|
+
|
2
|
+
# $Id: clip_array.rb 37 2009-02-09 10:06:02Z dz $
|
3
|
+
|
4
|
+
# if you use vim and don't like folds type zR
|
5
|
+
|
6
|
+
|
7
|
+
require 'app_error'
|
8
|
+
require 'clip'
|
9
|
+
require 'helper'
|
10
|
+
require 'preferences'
|
11
|
+
|
12
|
+
class ClipArray < Array
|
13
|
+
|
14
|
+
# Instance Variables
|
15
|
+
# self Array [Clip.new] the array of clips
|
16
|
+
# current_index Integer 0 the position of current clip
|
17
|
+
# pref Preferences Preferences.defaults preferences object
|
18
|
+
# file_and_path String ni name and path of current file
|
19
|
+
# filename String ni name of current file
|
20
|
+
# dirname String ni directory of current file
|
21
|
+
# file File ni current File object
|
22
|
+
# autosave_thread Thread nil thread for autosaving
|
23
|
+
|
24
|
+
|
25
|
+
attr_reader :current_index, :file, :filename, :dirname, :file_and_path
|
26
|
+
attr_accessor :pref
|
27
|
+
|
28
|
+
# Clear array and reset index
|
29
|
+
def clear
|
30
|
+
#{{{1
|
31
|
+
super
|
32
|
+
@current_index = -1
|
33
|
+
end #}}}1
|
34
|
+
|
35
|
+
# Clear array and create one empty clip
|
36
|
+
def clear1
|
37
|
+
#{{{1
|
38
|
+
clear
|
39
|
+
self << Clip.new
|
40
|
+
end #}}}1
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
#{{{1
|
44
|
+
clear
|
45
|
+
@file = nil
|
46
|
+
@filename = nil
|
47
|
+
@pref = Preferences.defaults
|
48
|
+
@autosave_thread = nil
|
49
|
+
print_verbose "initializing clip array"
|
50
|
+
end #}}}1
|
51
|
+
|
52
|
+
def <<(clip)
|
53
|
+
#{{{1
|
54
|
+
push(clip)
|
55
|
+
@current_index = length-1
|
56
|
+
return self
|
57
|
+
end #}}}1
|
58
|
+
|
59
|
+
def set_file(file,file_and_path)
|
60
|
+
#{{{1
|
61
|
+
@file = file
|
62
|
+
@file_and_path = file_and_path
|
63
|
+
@filename = File.basename(file_and_path)
|
64
|
+
@dirname = File.dirname(file_and_path)
|
65
|
+
end #}}}1
|
66
|
+
|
67
|
+
#
|
68
|
+
# Export
|
69
|
+
#
|
70
|
+
|
71
|
+
#def save_as_mbox(filename)
|
72
|
+
##{{{1
|
73
|
+
# filename = get_filename(filename,:save)
|
74
|
+
#
|
75
|
+
# puts "# saving clips to '#{filename}', format mbox" if $VERBOSE
|
76
|
+
#
|
77
|
+
# f = open(filename,"w")
|
78
|
+
# write_mbox(f)
|
79
|
+
# self.each do | clip |
|
80
|
+
# f.print "From %s@localhost %s\n" %
|
81
|
+
# [Version::PROGRAM_NAME, Time.now.ctime]
|
82
|
+
# f.print(escape_from(clip.to_mbox.chomp))
|
83
|
+
# f.print("\n")
|
84
|
+
# end
|
85
|
+
# f.close
|
86
|
+
#end #}}}1
|
87
|
+
#
|
88
|
+
#def save_as_yaml(filename)
|
89
|
+
##{{{1
|
90
|
+
# filename = get_filename(filename,:save)
|
91
|
+
#
|
92
|
+
# arr = Array.new
|
93
|
+
# self.each_index do | i |
|
94
|
+
# arr[i] = self[i].to_hash
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# puts "# saving clips to '#{filename}', format yaml" if $VERBOSE
|
98
|
+
#
|
99
|
+
# File.open(filename,"w") do | file |
|
100
|
+
# YAML::dump(arr, file)
|
101
|
+
# end
|
102
|
+
#end #}}}1
|
103
|
+
|
104
|
+
#def ClipArray.load(filename,format)
|
105
|
+
##{{{1
|
106
|
+
# case format
|
107
|
+
# when :mbox
|
108
|
+
# return ClipArray.load_from_mbox(filename)
|
109
|
+
# when :yaml
|
110
|
+
# return ClipArray.load_from_yaml(filename)
|
111
|
+
# end
|
112
|
+
#end #}}}1
|
113
|
+
|
114
|
+
def current_index=(num)
|
115
|
+
#{{{1
|
116
|
+
if num < 0 then
|
117
|
+
@current_index = 0
|
118
|
+
elsif num >= length then
|
119
|
+
@current_index = length - 1
|
120
|
+
else
|
121
|
+
@current_index = num
|
122
|
+
end
|
123
|
+
print_verbose "going to clip #{@current_index+1}"
|
124
|
+
end #}}}1
|
125
|
+
|
126
|
+
def next
|
127
|
+
#{{{1
|
128
|
+
if @current_index < length-1 then
|
129
|
+
@current_index += 1
|
130
|
+
print_verbose "going to next clip #{@current_index+1}"
|
131
|
+
end
|
132
|
+
return self
|
133
|
+
end #}}}1
|
134
|
+
|
135
|
+
def prev
|
136
|
+
#{{{1
|
137
|
+
if @current_index > 0 then
|
138
|
+
@current_index -= 1
|
139
|
+
print_verbose "going to previous clip #{@current_index+1}"
|
140
|
+
end
|
141
|
+
return self
|
142
|
+
end #}}}1
|
143
|
+
|
144
|
+
def delete
|
145
|
+
#{{{1
|
146
|
+
if @current_index == length-1 then
|
147
|
+
new_current_index = @current_index - 1
|
148
|
+
else
|
149
|
+
new_current_index = @current_index
|
150
|
+
end
|
151
|
+
|
152
|
+
print_verbose "deleting clip #{@current_index+1}"
|
153
|
+
delete_at(@current_index)
|
154
|
+
|
155
|
+
if length == 0 then
|
156
|
+
clear1
|
157
|
+
else
|
158
|
+
@current_index = new_current_index
|
159
|
+
end
|
160
|
+
|
161
|
+
end #}}}1
|
162
|
+
|
163
|
+
def new
|
164
|
+
#{{{1
|
165
|
+
c = Clip.new
|
166
|
+
push(c)
|
167
|
+
@current_index = length-1
|
168
|
+
print_verbose "creating new clip #{@current_index+1}"
|
169
|
+
end #}}}1
|
170
|
+
|
171
|
+
def current
|
172
|
+
#{{{1
|
173
|
+
return self[@current_index]
|
174
|
+
end #}}}1
|
175
|
+
|
176
|
+
#def ClipArray.load_from_yaml(filename)
|
177
|
+
##{{{1
|
178
|
+
# filename = get_filename(filename,:open)
|
179
|
+
# ca = self.new
|
180
|
+
#
|
181
|
+
# puts "# loading clips from '#{filename}', format yaml" if $VERBOSE
|
182
|
+
#
|
183
|
+
# begin
|
184
|
+
# yaml = YAML::load_file(filename)
|
185
|
+
# yaml.each_index do | i |
|
186
|
+
# clip = Clip.from_hash(yaml[i])
|
187
|
+
# puts "# '#{clip.title}' loaded" if $VERBOSE
|
188
|
+
# ca[i] = clip
|
189
|
+
# end
|
190
|
+
# rescue SystemCallError
|
191
|
+
# puts "# loading failed, continuing with empty clips" if $VERBOSE
|
192
|
+
# end
|
193
|
+
#
|
194
|
+
# ca.current_index = ca.length - 1
|
195
|
+
# return ca
|
196
|
+
#end #}}}1
|
197
|
+
#
|
198
|
+
#def ClipArray.load_from_mbox(filename)
|
199
|
+
##{{{1
|
200
|
+
# ca = self.new
|
201
|
+
# str = nil
|
202
|
+
#
|
203
|
+
# puts "# loading clips from '#{filename}', format mbox" if $VERBOSE
|
204
|
+
#
|
205
|
+
# begin
|
206
|
+
# file = open(get_filename(filename,:open),"r")
|
207
|
+
# ca = ClipArray.read_mbox(file)
|
208
|
+
# ensure
|
209
|
+
# file.close
|
210
|
+
# end
|
211
|
+
#
|
212
|
+
# return ca
|
213
|
+
#end #}}}1
|
214
|
+
|
215
|
+
def ClipArray.create(file_and_path)
|
216
|
+
#{{{1
|
217
|
+
print_verbose "creating file '#{file_and_path}"
|
218
|
+
|
219
|
+
if File.exists?(file_and_path) then
|
220
|
+
raise AppError, "File '#{file_and_path}' already exists"
|
221
|
+
end
|
222
|
+
|
223
|
+
begin
|
224
|
+
file = File.open(file_and_path,"w+")
|
225
|
+
ca = ClipArray.new.clear1
|
226
|
+
ca.set_file(file,file_and_path)
|
227
|
+
|
228
|
+
# test if writable
|
229
|
+
obtain_exclusive_lock(file)
|
230
|
+
|
231
|
+
obtain_shared_lock(file)
|
232
|
+
rescue Exception
|
233
|
+
file.close if file
|
234
|
+
raise
|
235
|
+
end
|
236
|
+
|
237
|
+
return ca
|
238
|
+
end #}}}1
|
239
|
+
|
240
|
+
def ClipArray.open(file_and_path)
|
241
|
+
#{{{1
|
242
|
+
print_verbose "opening file '#{file_and_path}'"
|
243
|
+
|
244
|
+
begin
|
245
|
+
file = File.open(file_and_path,"r+")
|
246
|
+
ca = ClipArray.read_mbox(file)
|
247
|
+
ca.set_file(file,file_and_path)
|
248
|
+
|
249
|
+
# test if writable
|
250
|
+
obtain_exclusive_lock(file)
|
251
|
+
|
252
|
+
obtain_shared_lock(file)
|
253
|
+
rescue Exception
|
254
|
+
file.close if file
|
255
|
+
raise
|
256
|
+
end
|
257
|
+
|
258
|
+
return ca
|
259
|
+
end #}}}1
|
260
|
+
|
261
|
+
def save
|
262
|
+
#{{{1
|
263
|
+
if @file.nil? then
|
264
|
+
return
|
265
|
+
end
|
266
|
+
|
267
|
+
print_verbose "saving file '#{@filename}'"
|
268
|
+
|
269
|
+
obtain_exclusive_lock(@file)
|
270
|
+
@file.truncate(0)
|
271
|
+
@file.rewind
|
272
|
+
write_mbox(@file)
|
273
|
+
@file.flush
|
274
|
+
obtain_shared_lock(@file)
|
275
|
+
end #}}}1
|
276
|
+
|
277
|
+
def close
|
278
|
+
#{{{1
|
279
|
+
autosave(0)
|
280
|
+
save
|
281
|
+
print_verbose "closing file '#{@filename}'"
|
282
|
+
if @file then
|
283
|
+
release_lock(@file)
|
284
|
+
@file.close
|
285
|
+
end
|
286
|
+
@file = nil
|
287
|
+
@filename = nil
|
288
|
+
clear
|
289
|
+
end #}}}1
|
290
|
+
|
291
|
+
def autosave(seconds)
|
292
|
+
#{{{1
|
293
|
+
print_debug "autosave(#{seconds}) called"
|
294
|
+
if @autosave_thread then
|
295
|
+
print_debug " killing old thread"
|
296
|
+
@autosave_thread.kill
|
297
|
+
@autosave_thread = nil
|
298
|
+
end
|
299
|
+
|
300
|
+
if seconds == 0 then
|
301
|
+
print_debug " returning because seconds==0"
|
302
|
+
return
|
303
|
+
end
|
304
|
+
|
305
|
+
print_verbose "starting autosave thread"
|
306
|
+
@autosave_thread = Thread.new do
|
307
|
+
while true
|
308
|
+
print_verbose "autosaving at #{Time.now}"
|
309
|
+
save
|
310
|
+
sleep(seconds)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end #}}}1
|
314
|
+
|
315
|
+
|
316
|
+
private
|
317
|
+
|
318
|
+
|
319
|
+
def ClipArray.read_mbox(file)
|
320
|
+
#{{{1
|
321
|
+
ca = self.new
|
322
|
+
first_line = true
|
323
|
+
str = ""
|
324
|
+
i = 1
|
325
|
+
|
326
|
+
print_debug "reading clips, mbox format"
|
327
|
+
|
328
|
+
if file.eof? or file.readline =~ /^From / then
|
329
|
+
# valid
|
330
|
+
else
|
331
|
+
raise AppError, "Not an Mbox file"
|
332
|
+
end
|
333
|
+
|
334
|
+
file.each_line do | line |
|
335
|
+
line.chomp!
|
336
|
+
if line =~ /^From / then
|
337
|
+
if not first_line then
|
338
|
+
clip = Clip.from_mbox(unescape_from(str))
|
339
|
+
if clip.type == :pref then
|
340
|
+
ca.pref = Preferences.read(clip.data)
|
341
|
+
print_debug " #{i}: Preferences"
|
342
|
+
else
|
343
|
+
ca << clip
|
344
|
+
print_debug " #{i}: '#{clip.title}'"
|
345
|
+
end
|
346
|
+
i += 1
|
347
|
+
end
|
348
|
+
str = ""
|
349
|
+
else
|
350
|
+
str << line << "\n"
|
351
|
+
end
|
352
|
+
first_line = false
|
353
|
+
end
|
354
|
+
|
355
|
+
if str != "" then
|
356
|
+
clip = Clip.from_mbox(unescape_from(str))
|
357
|
+
if clip.type == :pref then
|
358
|
+
ca.pref = Preferences.read(clip.data)
|
359
|
+
print_debug " #{i}: Preferences"
|
360
|
+
else
|
361
|
+
ca << clip
|
362
|
+
print_debug " #{i}: '#{clip.title}'"
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
ca.current_index = ca.length - 1
|
367
|
+
return ca
|
368
|
+
end #}}}1
|
369
|
+
|
370
|
+
def write_mbox(file)
|
371
|
+
#{{{1
|
372
|
+
print_debug "writing clips, mbox format"
|
373
|
+
i = 1
|
374
|
+
|
375
|
+
# Pref clip first
|
376
|
+
pc = Clip.new
|
377
|
+
pc.type = :pref
|
378
|
+
pc.title = "Preferences"
|
379
|
+
pc.data = @pref.write
|
380
|
+
|
381
|
+
([pc]+self).each do | clip |
|
382
|
+
file.print "From %s@localhost %s\n" %
|
383
|
+
[Version::PROGRAM_NAME, Time.now.ctime]
|
384
|
+
file.print(escape_from(clip.to_mbox.chomp))
|
385
|
+
file.print("\n")
|
386
|
+
print_debug " #{i}: '#{clip.title}'"
|
387
|
+
i += 1
|
388
|
+
end
|
389
|
+
end #}}}1
|
390
|
+
|
391
|
+
end # class
|
392
|
+
|
393
|
+
# vim600: set foldmethod=marker:
|