keepyourhead 0.2.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/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
|