keepyourhead 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING.txt +674 -0
- data/LICENSE.rdoc +20 -0
- data/README.rdoc +3 -0
- data/bin/keepyourhead +6 -0
- data/data/glade/DialogEditLatex.glade +180 -0
- data/data/glade/DialogEditText.glade +66 -0
- data/data/glade/DialogTrainStart.glade +101 -0
- data/data/glade/WindowEdit.glade +1419 -0
- data/data/glade/WindowTrain.glade +271 -0
- data/data/images/error_back.png +0 -0
- data/data/images/error_front.png +0 -0
- data/data/images/loading_back.png +0 -0
- data/data/images/loading_front.png +0 -0
- data/lib/Keepyourhead.rb +62 -0
- data/lib/Keepyourhead/Application.rb +94 -0
- data/lib/Keepyourhead/Cache.rb +233 -0
- data/lib/Keepyourhead/Compilation.rb +38 -0
- data/lib/Keepyourhead/Images.rb +62 -0
- data/lib/Keepyourhead/Preferences.rb +52 -0
- data/lib/Keepyourhead/Requirements.rb +72 -0
- data/lib/Keepyourhead/Resources.rb +45 -0
- data/lib/Keepyourhead/Training.rb +132 -0
- data/lib/Keepyourhead/Utils.rb +97 -0
- data/lib/Keepyourhead/Version.rb +43 -0
- data/lib/Keepyourhead/database/Base.rb +77 -0
- data/lib/Keepyourhead/database/BaseStatistic.rb +63 -0
- data/lib/Keepyourhead/database/BaseTopicFlashcardContainer.rb +43 -0
- data/lib/Keepyourhead/database/Collection.rb +43 -0
- data/lib/Keepyourhead/database/Database.rb +139 -0
- data/lib/Keepyourhead/database/File.rb +224 -0
- data/lib/Keepyourhead/database/FileRoot.rb +44 -0
- data/lib/Keepyourhead/database/Flashcard.rb +116 -0
- data/lib/Keepyourhead/database/Topic.rb +40 -0
- data/lib/Keepyourhead/database/Visitor.rb +33 -0
- data/lib/Keepyourhead/database/XmlAccessor.rb +315 -0
- data/lib/Keepyourhead/gui/Action.rb +91 -0
- data/lib/Keepyourhead/gui/DialogEditLatex.rb +132 -0
- data/lib/Keepyourhead/gui/DialogEditText.rb +104 -0
- data/lib/Keepyourhead/gui/DialogTrainStart.rb +63 -0
- data/lib/Keepyourhead/gui/FlashcardViewController.rb +112 -0
- data/lib/Keepyourhead/gui/WindowEdit.rb +469 -0
- data/lib/Keepyourhead/gui/WindowEditActions.rb +440 -0
- data/lib/Keepyourhead/gui/WindowEditAdapters.rb +329 -0
- data/lib/Keepyourhead/gui/WindowTrain.rb +167 -0
- data/lib/Keepyourhead/style/Style.rb +48 -0
- data/lib/Keepyourhead/style/StyleLatex.rb +235 -0
- metadata +100 -0
@@ -0,0 +1,440 @@
|
|
1
|
+
# Copyright 2008 Burghard Oliver
|
2
|
+
#
|
3
|
+
# This file is part of KeepYourHead.
|
4
|
+
#
|
5
|
+
# KeepYourHead is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# KeepYourHead is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with KeepYourHead. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
|
19
|
+
module KeepYourHead
|
20
|
+
class WindowEdit
|
21
|
+
def initActions
|
22
|
+
@actions = {}
|
23
|
+
Actions.each { |name, procExecuteable|
|
24
|
+
action = Action.new self, name
|
25
|
+
action.procExecuteable = procExecuteable
|
26
|
+
|
27
|
+
name2 = name.gsub( /[A-Z]/ ) { |s| "_" + s.downcase }[1..-1]
|
28
|
+
|
29
|
+
menuItem = @glade.get_widget("menuitem_#{name2}")
|
30
|
+
toolButton = @glade.get_widget("toolbutton_#{name2}")
|
31
|
+
|
32
|
+
# print "not found menuItem for #{name2}" unless menuItem
|
33
|
+
# print "not found toolButton for #{name2}" unless toolButton
|
34
|
+
|
35
|
+
action.addActivator menuItem if menuItem
|
36
|
+
action.addActivator toolButton if toolButton
|
37
|
+
|
38
|
+
@actions[name] = action
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def callAction(name, *args)
|
43
|
+
assert @actions[name.to_s] and @actions[name.to_s].executeable?
|
44
|
+
|
45
|
+
@actions[name.to_s].activate( *args )
|
46
|
+
end
|
47
|
+
|
48
|
+
MapNameToClass = {
|
49
|
+
"flashcard" => Database::Flashcard,
|
50
|
+
"topic" => Database::Topic,
|
51
|
+
"collection" => Database::Collection,
|
52
|
+
}
|
53
|
+
AcceptStrict = {
|
54
|
+
Database::Database => [Database::File],
|
55
|
+
Database::File => [Database::Collection],
|
56
|
+
Database::Collection => [Database::Topic, Database::Flashcard],
|
57
|
+
Database::Topic => [Database::Topic, Database::Flashcard],
|
58
|
+
Database::Flashcard => []
|
59
|
+
}
|
60
|
+
def self.canInsertStrict(parent, other)
|
61
|
+
return false unless parent and other
|
62
|
+
|
63
|
+
type =
|
64
|
+
case other
|
65
|
+
when String
|
66
|
+
MapNameToClass[other.downcase]
|
67
|
+
when REXML::Element
|
68
|
+
MapNameToClass[other.name.downcase]
|
69
|
+
when Class
|
70
|
+
other
|
71
|
+
else
|
72
|
+
throw ExceptionNotImplemented.new
|
73
|
+
end
|
74
|
+
|
75
|
+
assert AcceptStrict[parent.class]
|
76
|
+
|
77
|
+
AcceptStrict[parent.class].include? type
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.canInsertInterpreted( parent, other )
|
81
|
+
return false unless parent and other
|
82
|
+
canInsertStrict( parent, other ) or ( MyAdaptor.parent(parent) and canInsertInterpreted( MyAdaptor.parent(parent), other ) )
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.insertStrictAfter( parent, node, after )
|
86
|
+
assert node
|
87
|
+
assert node.kind_of?(REXML::Element)
|
88
|
+
assert canInsertStrict( parent, node.name )
|
89
|
+
MyAdaptor.insertAfter( parent, node, after )
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.insertInterpretedAfter( parent, node, after )
|
93
|
+
assert node
|
94
|
+
assert node.kind_of?(REXML::Element)
|
95
|
+
assert canInsertInterpreted( parent, node.name)
|
96
|
+
|
97
|
+
if canInsertStrict( parent, node ) then
|
98
|
+
insertStrictAfter( parent, node, after )
|
99
|
+
else
|
100
|
+
assert MyAdaptor.parent(parent)
|
101
|
+
insertInterpretedAfter( MyAdaptor.parent(parent), node, parent )
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
Actions = [
|
110
|
+
[ "New", proc { true } ],
|
111
|
+
[ "Open", proc { true } ],
|
112
|
+
[ "Save", proc { |obj|
|
113
|
+
obj.selected && [Database::File, Database::Collection, Database::Topic,Database::Flashcard].include?(obj.selected.class) &&
|
114
|
+
obj.selected.file.changed
|
115
|
+
} ],
|
116
|
+
[ "SaveAs", proc { |obj|
|
117
|
+
obj.selected && [Database::File, Database::Collection, Database::Topic,Database::Flashcard].include?(obj.selected.class)
|
118
|
+
} ],
|
119
|
+
[ "SaveAll", proc { |obj|
|
120
|
+
true
|
121
|
+
} ],
|
122
|
+
[ "Close", proc { |obj|
|
123
|
+
obj.selected && [Database::File, Database::Collection, Database::Topic,Database::Flashcard].include?(obj.selected.class)
|
124
|
+
} ],
|
125
|
+
|
126
|
+
[ "Paste", proc { |obj|
|
127
|
+
text = Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD).wait_for_text
|
128
|
+
|
129
|
+
dom = REXML::Document.new text
|
130
|
+
node = dom.root
|
131
|
+
|
132
|
+
node && canInsertInterpreted( obj.selected, node.name )
|
133
|
+
} ],
|
134
|
+
[ "Copy", proc { |obj|
|
135
|
+
obj.selected && [Database::Collection, Database::Topic,Database::Flashcard].include?(obj.selected.class)
|
136
|
+
} ],
|
137
|
+
[ "Cut", proc { |obj|
|
138
|
+
obj.selected && [Database::Collection, Database::Topic,Database::Flashcard].include?(obj.selected.class)
|
139
|
+
} ],
|
140
|
+
[ "AddTopic", proc { |obj|
|
141
|
+
obj.selected && canInsertInterpreted( obj.selected, Database::Topic)
|
142
|
+
}],
|
143
|
+
[ "AddFlashcard", proc { |obj|
|
144
|
+
obj.selected && canInsertInterpreted( obj.selected, Database::Flashcard)
|
145
|
+
}],
|
146
|
+
[ "AddCollection", proc { |obj|
|
147
|
+
obj.selected && canInsertInterpreted( obj.selected, Database::Collection)
|
148
|
+
}],
|
149
|
+
[ "Delete", proc { |obj|
|
150
|
+
obj.selected && [Database::Collection, Database::Topic, Database::Flashcard].include?(obj.selected.class)
|
151
|
+
}],
|
152
|
+
[ "Left", proc { |obj|
|
153
|
+
if obj.selected then
|
154
|
+
parent = MyAdaptor.parent(obj.selected)
|
155
|
+
if parent then
|
156
|
+
parent2 = MyAdaptor.parent(parent)
|
157
|
+
if parent2 then
|
158
|
+
return canInsertStrict( parent2, obj.selected.class)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
false
|
163
|
+
}],
|
164
|
+
[ "Right", proc { |obj|
|
165
|
+
if obj.selected then
|
166
|
+
parent = MyAdaptor.nextSibling(obj.selected)
|
167
|
+
if parent then
|
168
|
+
return canInsertStrict( parent, obj.selected.class)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
false
|
172
|
+
}],
|
173
|
+
[ "Up", proc { |obj|
|
174
|
+
if obj.selected && [Database::File, Database::Collection, Database::Topic, Database::Flashcard].include?(obj.selected.class) then
|
175
|
+
other = MyAdaptor.previousSibling obj.selected
|
176
|
+
if other then
|
177
|
+
return true
|
178
|
+
end
|
179
|
+
end
|
180
|
+
false
|
181
|
+
}],
|
182
|
+
[ "Down", proc { |obj|
|
183
|
+
if obj.selected && [Database::File, Database::Collection, Database::Topic, Database::Flashcard].include?(obj.selected.class) then
|
184
|
+
other = MyAdaptor.nextSibling obj.selected
|
185
|
+
if other then
|
186
|
+
return true
|
187
|
+
end
|
188
|
+
end
|
189
|
+
false
|
190
|
+
}],
|
191
|
+
[ "Train", proc { |obj|
|
192
|
+
true
|
193
|
+
}],
|
194
|
+
[ "About", proc { |obj|
|
195
|
+
true
|
196
|
+
}],
|
197
|
+
[ "RemoveStatistics", proc { |obj|
|
198
|
+
obj.selected && [Database::File, Database::Collection, Database::Topic, Database::Flashcard].include?(obj.selected.class)
|
199
|
+
}],
|
200
|
+
[ "Quit", proc { |obj|
|
201
|
+
true
|
202
|
+
}],
|
203
|
+
]
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
|
208
|
+
def actionBefore
|
209
|
+
end
|
210
|
+
def actionAfter
|
211
|
+
updateView
|
212
|
+
end
|
213
|
+
|
214
|
+
def onActionNew
|
215
|
+
self.database.fileNew
|
216
|
+
end
|
217
|
+
def onActionOpen
|
218
|
+
dialog = Gtk::FileChooserDialog.new("Open File",
|
219
|
+
@window,
|
220
|
+
Gtk::FileChooser::ACTION_OPEN,
|
221
|
+
nil,
|
222
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
223
|
+
[Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
|
224
|
+
|
225
|
+
# dialog.current_folder = ::File.expand_path "~"
|
226
|
+
|
227
|
+
filter = Gtk::FileFilter.new
|
228
|
+
filter.name = "Flashcards (*.xfc)"
|
229
|
+
filter.add_pattern "*.xfc"
|
230
|
+
dialog.add_filter( filter )
|
231
|
+
filter = Gtk::FileFilter.new
|
232
|
+
filter.name = "all"
|
233
|
+
filter.add_pattern "*"
|
234
|
+
dialog.add_filter( filter )
|
235
|
+
|
236
|
+
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
|
237
|
+
filename = ::File.expand_path dialog.filename
|
238
|
+
self.database.fileOpen( filename )
|
239
|
+
end
|
240
|
+
dialog.destroy
|
241
|
+
end
|
242
|
+
|
243
|
+
def onActionSaveAll
|
244
|
+
database.files.each { |file|
|
245
|
+
fileSave file if file.changed
|
246
|
+
}
|
247
|
+
end
|
248
|
+
|
249
|
+
def fileSaveAs(file)
|
250
|
+
dialog = Gtk::FileChooserDialog.new("Save File",
|
251
|
+
@window,
|
252
|
+
Gtk::FileChooser::ACTION_SAVE,
|
253
|
+
nil,
|
254
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
255
|
+
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT])
|
256
|
+
|
257
|
+
# dialog.current_folder = ::File.expand_path "~"
|
258
|
+
|
259
|
+
filter = Gtk::FileFilter.new
|
260
|
+
filter.name = "Flashcards (*.xfc)"
|
261
|
+
filter.add_pattern "*.xfc"
|
262
|
+
dialog.add_filter( filter )
|
263
|
+
filter = Gtk::FileFilter.new
|
264
|
+
filter.name = "all"
|
265
|
+
filter.add_pattern "*"
|
266
|
+
dialog.add_filter( filter )
|
267
|
+
|
268
|
+
dialog.current_name = "unnamed.xfc"
|
269
|
+
|
270
|
+
response = dialog.run
|
271
|
+
filename = dialog.filename
|
272
|
+
dialog.destroy
|
273
|
+
|
274
|
+
if response == Gtk::Dialog::RESPONSE_ACCEPT
|
275
|
+
file.saveAs filename
|
276
|
+
true
|
277
|
+
else
|
278
|
+
false
|
279
|
+
end
|
280
|
+
end
|
281
|
+
def fileSave(file)
|
282
|
+
if file.filename then
|
283
|
+
file.save
|
284
|
+
else
|
285
|
+
fileSaveAs file
|
286
|
+
end
|
287
|
+
end
|
288
|
+
def filePrepareToClose( file )
|
289
|
+
if file.changed then
|
290
|
+
dialog = Gtk::MessageDialog.new(
|
291
|
+
@window, Gtk::Dialog::MODAL, Gtk::MessageDialog::WARNING,
|
292
|
+
Gtk::MessageDialog::BUTTONS_NONE,
|
293
|
+
"Save changes ?" )
|
294
|
+
dialog.add_button( "Ja", Gtk::Dialog::RESPONSE_YES )
|
295
|
+
dialog.add_button( "Nein", Gtk::Dialog::RESPONSE_NO )
|
296
|
+
dialog.add_button( "Abbrechen", Gtk::Dialog::RESPONSE_CANCEL )
|
297
|
+
|
298
|
+
response = dialog.run
|
299
|
+
dialog.destroy
|
300
|
+
|
301
|
+
case response
|
302
|
+
when Gtk::Dialog::RESPONSE_YES
|
303
|
+
fileSave file
|
304
|
+
not file.changed
|
305
|
+
when Gtk::Dialog::RESPONSE_NO
|
306
|
+
true
|
307
|
+
when Gtk::Dialog::RESPONSE_CANCEL,
|
308
|
+
Gtk::Dialog::RESPONSE_DELETE_EVENT
|
309
|
+
false
|
310
|
+
else
|
311
|
+
false
|
312
|
+
end
|
313
|
+
|
314
|
+
else
|
315
|
+
true
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def onActionSave
|
320
|
+
fileSave self.selected.file
|
321
|
+
end
|
322
|
+
def onActionSaveAs
|
323
|
+
fileSaveAs self.selected.file
|
324
|
+
end
|
325
|
+
def onActionClose
|
326
|
+
if filePrepareToClose( self.selected.file ) then
|
327
|
+
database.fileClose self.selected.file
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def onActionPaste
|
332
|
+
Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD).request_text{ |clipboard, text|
|
333
|
+
dom = REXML::Document.new text
|
334
|
+
node = dom.root
|
335
|
+
|
336
|
+
self.class.insertInterpretedAfter self.selected, node, MyAdaptor.lastChild(self.selected)
|
337
|
+
}
|
338
|
+
end
|
339
|
+
def onActionCut
|
340
|
+
text = ""
|
341
|
+
REXML::Formatters::Default.new.write @selected.node, text
|
342
|
+
|
343
|
+
Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD).text = text
|
344
|
+
MyAdaptor.remove self.selected
|
345
|
+
end
|
346
|
+
def onActionCopy
|
347
|
+
text = ""
|
348
|
+
REXML::Formatters::Default.new.write @selected.node, text
|
349
|
+
Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD).text = text
|
350
|
+
end
|
351
|
+
|
352
|
+
def onActionAddTopic
|
353
|
+
self.class.insertInterpretedAfter self.selected, Database::Topic.createNode(self.selected.file), MyAdaptor.lastChild(self.selected)
|
354
|
+
end
|
355
|
+
def onActionAddFlashcard
|
356
|
+
self.class.insertInterpretedAfter self.selected, Database::Flashcard.createNode(self.selected.file), MyAdaptor.lastChild(self.selected)
|
357
|
+
end
|
358
|
+
def onActionAddCollection
|
359
|
+
self.class.insertInterpretedAfter self.selected, Database::Collection.createNode(self.selected.file), MyAdaptor.lastChild(self.selected)
|
360
|
+
end
|
361
|
+
def onActionDelete
|
362
|
+
MyAdaptor.remove self.selected
|
363
|
+
end
|
364
|
+
def onActionLeft
|
365
|
+
parent = MyAdaptor.parent(self.selected)
|
366
|
+
parent2 = MyAdaptor.parent(parent)
|
367
|
+
other = MyAdaptor.previousSibling( parent )
|
368
|
+
node = MyAdaptor.remove( self.selected )
|
369
|
+
self.class.insertStrictAfter( parent2, node, other)
|
370
|
+
end
|
371
|
+
def onActionRight
|
372
|
+
parent = MyAdaptor.nextSibling(self.selected)
|
373
|
+
node = MyAdaptor.remove( self.selected )
|
374
|
+
self.class.insertStrictAfter( parent, node, nil)
|
375
|
+
end
|
376
|
+
def onActionUp
|
377
|
+
if @selected.class == Database::File then
|
378
|
+
database.moveUp @selected
|
379
|
+
else
|
380
|
+
parent = MyAdaptor.parent @selected
|
381
|
+
other = MyAdaptor.previousSibling @selected
|
382
|
+
other = MyAdaptor.previousSibling other
|
383
|
+
|
384
|
+
node = MyAdaptor.remove @selected
|
385
|
+
MyAdaptor.insertAfter parent, node, other
|
386
|
+
end
|
387
|
+
end
|
388
|
+
def onActionDown
|
389
|
+
if @selected.class == Database::File then
|
390
|
+
database.moveDown @selected
|
391
|
+
else
|
392
|
+
parent = MyAdaptor.parent @selected
|
393
|
+
other = MyAdaptor.nextSibling @selected
|
394
|
+
|
395
|
+
node = MyAdaptor.remove @selected
|
396
|
+
MyAdaptor.insertAfter parent, node, other
|
397
|
+
end
|
398
|
+
end
|
399
|
+
def onActionTrain
|
400
|
+
dialog = DialogQuestionStart.new
|
401
|
+
if dialog.run then
|
402
|
+
max_questions, max_minutes = dialog.max_questions, dialog.max_minutes
|
403
|
+
dialog.destroy
|
404
|
+
|
405
|
+
training = FlashcardTraining.new(database, max_questions, max_minutes * 60)
|
406
|
+
WindowQuestion.new(self, training)
|
407
|
+
else
|
408
|
+
dialog.destroy
|
409
|
+
end
|
410
|
+
end
|
411
|
+
def onActionAbout
|
412
|
+
# aboutDialog = Gtk::AboutDialog.new
|
413
|
+
Gtk::AboutDialog.show @window, {
|
414
|
+
# :artists => nil,
|
415
|
+
:authors => [Project::AUTHOR_WITH_EMAIL],
|
416
|
+
:comments => Project::SUMMARY,
|
417
|
+
:copyright => Project::COPYRIGHT,
|
418
|
+
:license => Project::LICENSE,
|
419
|
+
:name => Project::NAME,
|
420
|
+
:version => Project::VERSION,
|
421
|
+
:website => Project::HOMEPAGE,
|
422
|
+
"program-name" => Project::NAME,
|
423
|
+
}
|
424
|
+
end
|
425
|
+
def onActionRemoveStatistics
|
426
|
+
@selected.removeStatistics
|
427
|
+
end
|
428
|
+
def onActionQuit
|
429
|
+
ret = true
|
430
|
+
|
431
|
+
database.files.inject(true) { |ret, file|
|
432
|
+
ret &&= filePrepareToClose( file )
|
433
|
+
}
|
434
|
+
|
435
|
+
@window.destroy if ret
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
|
@@ -0,0 +1,329 @@
|
|
1
|
+
# Copyright 2008 Burghard Oliver
|
2
|
+
#
|
3
|
+
# This file is part of KeepYourHead.
|
4
|
+
#
|
5
|
+
# KeepYourHead is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# KeepYourHead is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with KeepYourHead. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
|
19
|
+
module KeepYourHead
|
20
|
+
class Adaptor
|
21
|
+
def name(object)
|
22
|
+
throw ExceptionNotImplimented.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def parent(object)
|
26
|
+
throw ExceptionNotImplimented.new
|
27
|
+
end
|
28
|
+
def firstChild(object)
|
29
|
+
throw ExceptionNotImplimented.new
|
30
|
+
end
|
31
|
+
def nextSibling(object)
|
32
|
+
throw ExceptionNotImplimented.new
|
33
|
+
end
|
34
|
+
def previousSibling(object)
|
35
|
+
throw ExceptionNotImplimented.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def createChild(parent)
|
39
|
+
throw ExceptionNotImplimented.new
|
40
|
+
end
|
41
|
+
def insertAfter(parent,item,other)
|
42
|
+
throw ExceptionNotImplimented.new
|
43
|
+
end
|
44
|
+
def remove(item)
|
45
|
+
throw ExceptionNotImplimented.new
|
46
|
+
end
|
47
|
+
|
48
|
+
def realization(object)
|
49
|
+
throw ExceptionNotImplimented.new
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class AdaptorDatabase < Adaptor
|
54
|
+
def name(object)
|
55
|
+
throw ExceptionNotImplimented.new
|
56
|
+
end
|
57
|
+
|
58
|
+
def parent(object)
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
def firstChild(object)
|
62
|
+
object.files[0]
|
63
|
+
end
|
64
|
+
def nextSibling(object)
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
def previousSibling(object)
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def createChild(parent)
|
72
|
+
throw ExceptionNotImplimented.new
|
73
|
+
end
|
74
|
+
def insertAfter(parent,item,other)
|
75
|
+
throw ExceptionNotImplimented.new
|
76
|
+
end
|
77
|
+
def remove(item)
|
78
|
+
throw ExceptionNotImplimented.new
|
79
|
+
end
|
80
|
+
|
81
|
+
def realization(object)
|
82
|
+
object
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class AdaptorFile < Adaptor
|
87
|
+
def name(object)
|
88
|
+
object.filename ? ::File.basename(object.filename) : "(unbenannt)"
|
89
|
+
end
|
90
|
+
|
91
|
+
def parent(object)
|
92
|
+
object.database
|
93
|
+
end
|
94
|
+
def firstChild(object)
|
95
|
+
object.root.collections[0]
|
96
|
+
end
|
97
|
+
def nextSibling(object)
|
98
|
+
files = object.database.files
|
99
|
+
idx = files.index object
|
100
|
+
if idx == files.length - 1 then
|
101
|
+
nil
|
102
|
+
else
|
103
|
+
files[idx+1]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
def previousSibling(object)
|
107
|
+
files = object.database.files
|
108
|
+
idx = files.index object
|
109
|
+
if idx == 0 then
|
110
|
+
nil
|
111
|
+
else
|
112
|
+
files[idx-1]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def insertAfter(parent,item,other)
|
117
|
+
if other then
|
118
|
+
parent.root.collectionInsertAfter(item,other)
|
119
|
+
else
|
120
|
+
parent.root.collectionInsertFirst(item)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
def remove(item)
|
124
|
+
throw ExceptionNotImplimented.new
|
125
|
+
end
|
126
|
+
def realization(object)
|
127
|
+
object
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class AdaptorCollection < Adaptor
|
132
|
+
def name(object)
|
133
|
+
object.name
|
134
|
+
end
|
135
|
+
|
136
|
+
def parent(object)
|
137
|
+
object.file
|
138
|
+
end
|
139
|
+
def firstChild(object)
|
140
|
+
object.items[0]
|
141
|
+
end
|
142
|
+
def nextSibling(object)
|
143
|
+
object.parent.collectionNext(object)
|
144
|
+
end
|
145
|
+
def previousSibling(object)
|
146
|
+
object.parent.collectionPrevious(object)
|
147
|
+
end
|
148
|
+
|
149
|
+
def insertAfter(parent,item,other)
|
150
|
+
if other then
|
151
|
+
parent.itemInsertAfter(item,other)
|
152
|
+
else
|
153
|
+
parent.itemInsertFirst(item)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
def remove(item)
|
157
|
+
item.parent.collectionRemove item
|
158
|
+
end
|
159
|
+
def realization(object)
|
160
|
+
object.node
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class AdaptorTopic < Adaptor
|
165
|
+
def name(object)
|
166
|
+
object.name
|
167
|
+
end
|
168
|
+
|
169
|
+
def parent(object)
|
170
|
+
object.parent
|
171
|
+
end
|
172
|
+
def firstChild(object)
|
173
|
+
object.items[0]
|
174
|
+
end
|
175
|
+
def nextSibling(object)
|
176
|
+
object.parent.itemNext object
|
177
|
+
end
|
178
|
+
def previousSibling(object)
|
179
|
+
object.parent.itemPrevious object
|
180
|
+
end
|
181
|
+
|
182
|
+
def createChild(parent)
|
183
|
+
end
|
184
|
+
def insertAfter(parent,item,other)
|
185
|
+
if other then
|
186
|
+
parent.itemInsertAfter(item,other)
|
187
|
+
else
|
188
|
+
parent.itemInsertFirst(item)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
def remove(item)
|
192
|
+
item.parent.itemRemove item
|
193
|
+
end
|
194
|
+
def realization(object)
|
195
|
+
object.node
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
class AdaptorFlashcard < Adaptor
|
200
|
+
def name(object)
|
201
|
+
object.name
|
202
|
+
end
|
203
|
+
|
204
|
+
def parent(object)
|
205
|
+
object.parent
|
206
|
+
end
|
207
|
+
def firstChild(object)
|
208
|
+
nil
|
209
|
+
end
|
210
|
+
def nextSibling(object)
|
211
|
+
object.parent.itemNext object
|
212
|
+
end
|
213
|
+
def previousSibling(object)
|
214
|
+
object.parent.itemPrevious object
|
215
|
+
end
|
216
|
+
|
217
|
+
def createChild(parent)
|
218
|
+
end
|
219
|
+
def insertAfter(parent,item,other)
|
220
|
+
if other then
|
221
|
+
parent.itemInsertAfter(item,other)
|
222
|
+
else
|
223
|
+
parent.itemInsertFirst(item)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
def remove(item)
|
227
|
+
item.parent.itemRemove item
|
228
|
+
end
|
229
|
+
def realization(object)
|
230
|
+
object.node
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
class AdaptorHandler
|
235
|
+
Adaptors = {
|
236
|
+
Database::Database => AdaptorDatabase.new,
|
237
|
+
Database::File => AdaptorFile.new,
|
238
|
+
Database::Collection => AdaptorCollection.new,
|
239
|
+
Database::Topic => AdaptorTopic.new,
|
240
|
+
Database::Flashcard => AdaptorFlashcard.new,
|
241
|
+
}
|
242
|
+
|
243
|
+
def name(object)
|
244
|
+
Adaptors[object.class].name object
|
245
|
+
end
|
246
|
+
|
247
|
+
def parent(object)
|
248
|
+
Adaptors[object.class].parent object
|
249
|
+
end
|
250
|
+
def firstChild(object)
|
251
|
+
Adaptors[object.class].firstChild object
|
252
|
+
end
|
253
|
+
def nextSibling(object)
|
254
|
+
Adaptors[object.class].nextSibling object
|
255
|
+
end
|
256
|
+
def previousSibling(object)
|
257
|
+
Adaptors[object.class].previousSibling object
|
258
|
+
end
|
259
|
+
|
260
|
+
def createChild(parent)
|
261
|
+
Adaptors[object.class].createChild object
|
262
|
+
end
|
263
|
+
def insertAfter(parent,item,other)
|
264
|
+
Adaptors[parent.class].insertAfter parent, item, other
|
265
|
+
end
|
266
|
+
def remove(object)
|
267
|
+
Adaptors[object.class].remove object
|
268
|
+
end
|
269
|
+
|
270
|
+
def realization(object)
|
271
|
+
Adaptors[object.class].realization object
|
272
|
+
end
|
273
|
+
|
274
|
+
#for better use
|
275
|
+
|
276
|
+
def insertLast(parent, item)
|
277
|
+
insertAfter(parent, item, lastChild(parent) )
|
278
|
+
end
|
279
|
+
def lastChild(parent)
|
280
|
+
c = firstChild(parent)
|
281
|
+
|
282
|
+
return nil unless c
|
283
|
+
|
284
|
+
while o = nextSibling(c)
|
285
|
+
c = o
|
286
|
+
end
|
287
|
+
c
|
288
|
+
end
|
289
|
+
def children(parent)
|
290
|
+
children = []
|
291
|
+
c = firstChild(parent)
|
292
|
+
while c do
|
293
|
+
children << c
|
294
|
+
c = nextSibling(c)
|
295
|
+
end
|
296
|
+
children
|
297
|
+
end
|
298
|
+
|
299
|
+
|
300
|
+
def moveUp(item)
|
301
|
+
if item.class == Database::File then
|
302
|
+
item.database.moveUp item
|
303
|
+
else
|
304
|
+
parent = self.parent item
|
305
|
+
other = self.previousSibling item
|
306
|
+
other = self.previousSibling other
|
307
|
+
|
308
|
+
node = self.remove item
|
309
|
+
self.insertAfter parent, node, other
|
310
|
+
end
|
311
|
+
end
|
312
|
+
def moveDown(item)
|
313
|
+
if item.class == Database::File then
|
314
|
+
item.database.moveDown item
|
315
|
+
else
|
316
|
+
parent = self.parent item
|
317
|
+
other = self.nextSibling item
|
318
|
+
|
319
|
+
node = self.remove item
|
320
|
+
self.insertAfter parent, node, other
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
MyAdaptor = AdaptorHandler.new
|
327
|
+
|
328
|
+
|
329
|
+
end
|