alexandria-book-collection-manager 0.7.2 → 0.7.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +9 -0
- data/.github/workflows/ruby.yml +77 -0
- data/.gitignore +5 -1
- data/.hound.yml +2 -0
- data/.rubocop.yml +87 -37
- data/.rubocop_todo.yml +62 -191
- data/.simplecov +5 -2
- data/CHANGELOG.md +63 -0
- data/Gemfile +4 -3
- data/INSTALL.md +26 -14
- data/README.md +52 -42
- data/Rakefile +93 -109
- data/TODO.md +9 -1
- data/alexandria-book-collection-manager.gemspec +50 -43
- data/bin/alexandria +30 -53
- data/doc/FAQ +2 -6
- data/doc/dependency_decisions.yml +27 -8
- data/lib/alexandria.rb +27 -37
- data/lib/alexandria/about.rb +50 -50
- data/lib/alexandria/book_providers.rb +90 -97
- data/lib/alexandria/book_providers/adlibris.rb +41 -76
- data/lib/alexandria/book_providers/amazon_aws.rb +96 -100
- data/lib/alexandria/book_providers/amazon_ecs_util.rb +295 -322
- data/lib/alexandria/book_providers/barnes_and_noble.rb +48 -45
- data/lib/alexandria/book_providers/douban.rb +26 -42
- data/lib/alexandria/book_providers/proxis.rb +44 -55
- data/lib/alexandria/book_providers/pseudomarc.rb +77 -85
- data/lib/alexandria/book_providers/siciliano.rb +64 -65
- data/lib/alexandria/book_providers/thalia.rb +42 -41
- data/lib/alexandria/book_providers/web.rb +15 -33
- data/lib/alexandria/book_providers/worldcat.rb +70 -97
- data/lib/alexandria/book_providers/z3950.rb +160 -173
- data/lib/alexandria/config.rb +1 -1
- data/lib/alexandria/console.rb +8 -21
- data/lib/alexandria/default_preferences.rb +37 -0
- data/lib/alexandria/execution_queue.rb +15 -13
- data/lib/alexandria/export_format.rb +47 -0
- data/lib/alexandria/export_library.rb +193 -300
- data/lib/alexandria/import_library.rb +108 -141
- data/lib/alexandria/import_library_csv.rb +43 -46
- data/lib/alexandria/library_collection.rb +79 -0
- data/lib/alexandria/library_sort_order.rb +45 -0
- data/lib/alexandria/library_store.rb +233 -0
- data/lib/alexandria/logging.rb +11 -13
- data/lib/alexandria/models/book.rb +13 -20
- data/lib/alexandria/models/library.rb +81 -353
- data/lib/alexandria/net.rb +5 -6
- data/lib/alexandria/preferences.rb +73 -87
- data/lib/alexandria/scanners.rb +2 -2
- data/lib/alexandria/scanners/{cuecat.rb → cue_cat.rb} +20 -18
- data/lib/alexandria/scanners/keyboard.rb +8 -8
- data/lib/alexandria/smart_library.rb +133 -170
- data/lib/alexandria/ui.rb +15 -15
- data/lib/alexandria/ui/about_dialog.rb +49 -0
- data/lib/alexandria/ui/{dialogs/acquire_dialog.rb → acquire_dialog.rb} +119 -136
- data/lib/alexandria/ui/alert_dialog.rb +64 -0
- data/lib/alexandria/ui/bad_isbns_dialog.rb +41 -0
- data/lib/alexandria/ui/{dialogs/barcode_animation.rb → barcode_animation.rb} +16 -15
- data/lib/alexandria/ui/{dialogs/book_properties_dialog.rb → book_properties_dialog.rb} +39 -52
- data/lib/alexandria/ui/book_properties_dialog_base.rb +318 -0
- data/lib/alexandria/ui/builder_base.rb +7 -27
- data/lib/alexandria/ui/calendar_popup.rb +58 -0
- data/lib/alexandria/ui/callbacks.rb +189 -183
- data/lib/alexandria/ui/completion_models.rb +10 -23
- data/lib/alexandria/ui/confirm_erase_dialog.rb +33 -0
- data/lib/alexandria/ui/conflict_while_copying_dialog.rb +34 -0
- data/lib/alexandria/ui/dndable.rb +7 -7
- data/lib/alexandria/ui/error_dialog.rb +25 -0
- data/lib/alexandria/ui/export_dialog.rb +142 -0
- data/lib/alexandria/ui/icons.rb +47 -63
- data/lib/alexandria/ui/iconview.rb +12 -10
- data/lib/alexandria/ui/iconview_tooltips.rb +41 -54
- data/lib/alexandria/ui/import_dialog.rb +157 -0
- data/lib/alexandria/ui/init.rb +21 -33
- data/lib/alexandria/ui/keep_bad_isbn_dialog.rb +36 -0
- data/lib/alexandria/ui/libraries_combo.rb +16 -14
- data/lib/alexandria/ui/listview.rb +73 -87
- data/lib/alexandria/ui/main_app.rb +24 -26
- data/lib/alexandria/ui/misc_dialogs.rb +10 -0
- data/lib/alexandria/ui/multi_drag_treeview.rb +28 -41
- data/lib/alexandria/ui/{dialogs/new_book_dialog.rb → new_book_dialog.rb} +156 -194
- data/lib/alexandria/ui/new_book_dialog_manual.rb +139 -0
- data/lib/alexandria/ui/new_provider_dialog.rb +100 -0
- data/lib/alexandria/ui/new_smart_library_dialog.rb +74 -0
- data/lib/alexandria/ui/preferences_dialog.rb +313 -0
- data/lib/alexandria/ui/provider_preferences_base_dialog.rb +95 -0
- data/lib/alexandria/ui/provider_preferences_dialog.rb +35 -0
- data/lib/alexandria/ui/really_delete_dialog.rb +53 -0
- data/lib/alexandria/ui/{sidepane.rb → sidepane_manager.rb} +56 -68
- data/lib/alexandria/ui/skip_entry_dialog.rb +33 -0
- data/lib/alexandria/ui/smart_library_properties_dialog.rb +60 -0
- data/lib/alexandria/ui/smart_library_properties_dialog_base.rb +242 -0
- data/lib/alexandria/ui/smart_library_rule_box.rb +119 -0
- data/lib/alexandria/ui/sound.rb +11 -13
- data/lib/alexandria/ui/ui_manager.rb +236 -251
- data/lib/alexandria/undo_manager.rb +1 -0
- data/lib/alexandria/version.rb +4 -19
- data/lib/alexandria/web_themes.rb +22 -21
- data/po/Makefile +2 -2
- data/po/cs.po +993 -880
- data/po/cy.po +957 -874
- data/po/de.po +990 -869
- data/po/el.po +989 -869
- data/po/es.po +985 -865
- data/po/fr.po +986 -870
- data/po/ga.po +907 -823
- data/po/gl.po +981 -865
- data/po/it.po +986 -868
- data/po/ja.po +969 -853
- data/po/mk.po +983 -863
- data/po/nb.po +979 -863
- data/po/nl.po +983 -864
- data/po/pl.po +1017 -974
- data/po/pt.po +988 -861
- data/po/pt_BR.po +984 -868
- data/po/ru.po +992 -873
- data/po/sk.po +987 -869
- data/po/sv.po +977 -861
- data/po/uk.po +975 -865
- data/po/zh_TW.po +976 -860
- data/schemas/alexandria.schemas +25 -3
- data/share/alexandria/glade/acquire_dialog__builder.glade +15 -12
- data/share/alexandria/glade/book_properties_dialog__builder.glade +171 -299
- data/share/alexandria/glade/main_app__builder.glade +24 -33
- data/share/alexandria/glade/new_book_dialog__builder.glade +27 -59
- data/share/alexandria/glade/preferences_dialog__builder.glade +250 -290
- data/share/gnome/help/alexandria/C/introduction.xml +0 -8
- data/share/gnome/help/alexandria/C/searching.xml +1 -1
- data/share/gnome/help/alexandria/C/smart-libraries.xml +2 -2
- data/share/gnome/help/alexandria/C/working-with-libraries.xml +1 -1
- data/share/gnome/help/alexandria/fr/alexandria.xml +1 -1
- data/share/gnome/help/alexandria/ja/introduction.xml +0 -8
- data/share/gnome/help/alexandria/ja/smart-libraries.xml +1 -1
- data/spec/alexandria/book_providers/world_cat_provider_spec.rb +160 -0
- data/spec/alexandria/book_providers_spec.rb +75 -171
- data/spec/alexandria/book_spec.rb +12 -10
- data/spec/alexandria/console_spec.rb +27 -0
- data/spec/alexandria/export_library_spec.rb +130 -0
- data/spec/alexandria/library_spec.rb +128 -172
- data/spec/alexandria/library_store_spec.rb +37 -0
- data/spec/alexandria/preferences_spec.rb +44 -17
- data/spec/alexandria/scanners/cue_cat_spec.rb +52 -0
- data/spec/alexandria/smart_library_spec.rb +30 -25
- data/spec/alexandria/ui/about_dialog_spec.rb +14 -0
- data/spec/alexandria/ui/acquire_dialog_spec.rb +14 -0
- data/spec/alexandria/ui/alert_dialog_spec.rb +16 -0
- data/spec/alexandria/ui/bad_isbns_dialog_spec.rb +14 -0
- data/spec/alexandria/ui/book_properties_dialog_spec.rb +17 -0
- data/spec/alexandria/ui/confirm_erase_dialog_spec.rb +14 -0
- data/spec/alexandria/ui/conflict_while_copying_dialog_spec.rb +16 -0
- data/spec/alexandria/ui/error_dialog_spec.rb +14 -0
- data/spec/alexandria/ui/export_dialog_spec.rb +36 -0
- data/spec/alexandria/ui/icons_spec.rb +26 -0
- data/spec/alexandria/ui/iconview_spec.rb +7 -21
- data/spec/alexandria/ui/import_dialog_spec.rb +46 -0
- data/spec/alexandria/ui/keep_bad_isbn_dialog_spec.rb +17 -0
- data/spec/alexandria/ui/main_app_spec.rb +7 -34
- data/spec/alexandria/ui/new_book_dialog_manual_spec.rb +15 -0
- data/spec/alexandria/ui/new_book_dialog_spec.rb +22 -0
- data/spec/alexandria/ui/new_provider_dialog_spec.rb +30 -0
- data/spec/alexandria/ui/new_smart_library_dialog_spec.rb +39 -0
- data/spec/alexandria/ui/preferences_dialog_spec.rb +14 -0
- data/spec/alexandria/ui/provider_preferences_dialog_spec.rb +34 -0
- data/spec/alexandria/ui/really_delete_dialog_spec.rb +16 -0
- data/spec/alexandria/ui/sidepane_manager_spec.rb +15 -0
- data/spec/alexandria/ui/skip_entry_dialog_spec.rb +14 -0
- data/spec/alexandria/ui/smart_library_properties_dialog_spec.rb +49 -0
- data/spec/alexandria/ui/sound_spec.rb +2 -2
- data/spec/alexandria/ui/ui_manager_spec.rb +43 -20
- data/spec/end_to_end/basic_run_spec.rb +52 -0
- data/spec/spec_helper.rb +65 -33
- data/tasks/setup.rb +2 -2
- data/tasks/spec.rake +16 -3
- data/util/rake/fileinstall.rb +39 -35
- data/util/rake/gettextgenerate.rb +7 -7
- data/util/rake/omfgenerate.rb +7 -7
- metadata +178 -45
- data/dogtail/basic_run_test.py +0 -9
- data/lib/alexandria/book_providers/deastore.rb +0 -265
- data/lib/alexandria/book_providers/mcu.rb +0 -182
- data/lib/alexandria/book_providers/renaud.rb +0 -149
- data/lib/alexandria/ui/dialogs/about_dialog.rb +0 -61
- data/lib/alexandria/ui/dialogs/alert_dialog.rb +0 -72
- data/lib/alexandria/ui/dialogs/bad_isbns_dialog.rb +0 -51
- data/lib/alexandria/ui/dialogs/book_properties_dialog_base.rb +0 -426
- data/lib/alexandria/ui/dialogs/export_dialog.rb +0 -171
- data/lib/alexandria/ui/dialogs/import_dialog.rb +0 -196
- data/lib/alexandria/ui/dialogs/misc_dialogs.rb +0 -87
- data/lib/alexandria/ui/dialogs/new_book_dialog_manual.rb +0 -154
- data/lib/alexandria/ui/dialogs/new_smart_library_dialog.rb +0 -74
- data/lib/alexandria/ui/dialogs/preferences_dialog.rb +0 -568
- data/lib/alexandria/ui/dialogs/smart_library_properties_dialog.rb +0 -59
- data/lib/alexandria/ui/dialogs/smart_library_properties_dialog_base.rb +0 -420
- data/spec/alexandria/scanners/cuecat_spec.rb +0 -67
- data/spec/alexandria/ui/dialogs_spec.rb +0 -96
- data/spec/alexandria/ui/sidepane_spec.rb +0 -29
- data/spec/alexandria/ui/ui_utilities_spec.rb +0 -62
- data/spec/alexandria/utilities_spec.rb +0 -52
- data/tasks/dogtail.rake +0 -6
@@ -0,0 +1,242 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is part of Alexandria.
|
4
|
+
#
|
5
|
+
# See the file README.md for authorship and licensing information.
|
6
|
+
|
7
|
+
require "alexandria/ui/calendar_popup"
|
8
|
+
require "alexandria/ui/smart_library_rule_box"
|
9
|
+
|
10
|
+
module Alexandria
|
11
|
+
module UI
|
12
|
+
class SmartLibraryPropertiesDialogBase
|
13
|
+
include Logging
|
14
|
+
include CalendarPopup
|
15
|
+
include GetText
|
16
|
+
GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
|
17
|
+
|
18
|
+
attr_reader :predicate_operator_rule, :dialog
|
19
|
+
|
20
|
+
def initialize(parent)
|
21
|
+
@dialog = Gtk::Dialog.new(title: "",
|
22
|
+
parent: parent,
|
23
|
+
flags: :modal,
|
24
|
+
buttons: [[Gtk::Stock::HELP, :help]])
|
25
|
+
|
26
|
+
@dialog.window_position = :center
|
27
|
+
@dialog.resizable = true
|
28
|
+
@dialog.border_width = 4
|
29
|
+
@dialog.child.border_width = 12
|
30
|
+
|
31
|
+
main_box = Gtk::Box.new :vertical
|
32
|
+
main_box.border_width = 4
|
33
|
+
main_box.spacing = 8
|
34
|
+
|
35
|
+
@dialog.child << main_box
|
36
|
+
|
37
|
+
@smart_library_rules = []
|
38
|
+
|
39
|
+
@rules_header_box = Gtk::Box.new :horizontal
|
40
|
+
@rules_header_box.spacing = 2
|
41
|
+
|
42
|
+
@rules_box = Gtk::Box.new :vertical
|
43
|
+
@rules_box.spacing = 8
|
44
|
+
@rules_box.border_width = 8
|
45
|
+
|
46
|
+
scrollview = Gtk::ScrolledWindow.new
|
47
|
+
scrollview.hscrollbar_policy = :never
|
48
|
+
scrollview.vscrollbar_policy = :automatic
|
49
|
+
scrollview.set_size_request(-1, 125)
|
50
|
+
scrollview.add_with_viewport(@rules_box)
|
51
|
+
|
52
|
+
main_box.pack_start(@rules_header_box, expand: false, fill: false)
|
53
|
+
main_box << scrollview
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_date_icon_press(widget, primary, _icon)
|
57
|
+
display_calendar_popup(widget) if primary.nick == "primary"
|
58
|
+
end
|
59
|
+
|
60
|
+
def handle_add_rule_clicked
|
61
|
+
insert_new_rule
|
62
|
+
end
|
63
|
+
|
64
|
+
def handle_remove_rule_clicked(box_controller)
|
65
|
+
remove_rule_box(box_controller.rule_box)
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO: Move logic to SmartLibraryRuleBox
|
69
|
+
def apply_smart_rule_for_rule_box(rule_box, operand, operation)
|
70
|
+
idx = @rules_box.children.index(rule_box)
|
71
|
+
smart_library_rules[idx] ||= SmartLibrary::Rule.new(operand,
|
72
|
+
operation.first,
|
73
|
+
nil)
|
74
|
+
new_rule = smart_library_rules[idx]
|
75
|
+
new_rule.operand = operand
|
76
|
+
new_rule.operation = operation.first
|
77
|
+
new_rule.value = nil
|
78
|
+
end
|
79
|
+
|
80
|
+
protected
|
81
|
+
|
82
|
+
attr_reader :smart_library_rules
|
83
|
+
|
84
|
+
def has_weirdnesses?
|
85
|
+
fill_smart_library_rules_values
|
86
|
+
smart_library_rules.each do |rule|
|
87
|
+
return true if rule.value == ""
|
88
|
+
end
|
89
|
+
false
|
90
|
+
end
|
91
|
+
|
92
|
+
def user_confirms_possible_weirdnesses_before_saving?
|
93
|
+
return true unless has_weirdnesses?
|
94
|
+
|
95
|
+
dialog = AlertDialog.new(
|
96
|
+
@dialog,
|
97
|
+
_("Empty or conflictive condition"),
|
98
|
+
Gtk::Stock::DIALOG_QUESTION,
|
99
|
+
[[Gtk::Stock::CANCEL, Gtk::ResponseType::CANCEL],
|
100
|
+
[_("_Save However"), Gtk::ResponseType::YES]],
|
101
|
+
_("This smart library contains one or more conditions " \
|
102
|
+
"which are empty or conflict with each other. This is " \
|
103
|
+
"likely to result in never matching a book. Are you " \
|
104
|
+
"sure you want to save this library?"))
|
105
|
+
dialog.default_response = Gtk::ResponseType::CANCEL
|
106
|
+
dialog.show_all
|
107
|
+
confirmed = dialog.run == Gtk::ResponseType::YES
|
108
|
+
dialog.destroy
|
109
|
+
confirmed
|
110
|
+
end
|
111
|
+
|
112
|
+
def update_rules_header_box(predicate_operator_rule = SmartLibrary::ALL_RULES)
|
113
|
+
@rules_header_box.children.each { |x| @rules_header_box.remove(x) }
|
114
|
+
|
115
|
+
if @rules_box.children.length > 1
|
116
|
+
label1 = Gtk::Label.new
|
117
|
+
label1.set_alignment(0.0, 0.5)
|
118
|
+
label1.text = _("Match")
|
119
|
+
|
120
|
+
cb = Gtk::ComboBoxText.new
|
121
|
+
[_("all"), _("any")].each { |x| cb.append_text(x) }
|
122
|
+
cb.signal_connect("changed") do
|
123
|
+
@predicate_operator_rule =
|
124
|
+
cb.active.zero? ? SmartLibrary::ALL_RULES : SmartLibrary::ANY_RULE
|
125
|
+
end
|
126
|
+
cb.active =
|
127
|
+
predicate_operator_rule == SmartLibrary::ALL_RULES ? 0 : 1
|
128
|
+
|
129
|
+
label2 = Gtk::Label.new
|
130
|
+
label2.set_alignment(0.0, 0.5)
|
131
|
+
label2.text = _("of the following rules:")
|
132
|
+
|
133
|
+
@rules_header_box.pack_start(label1, expand: false, fill: false)
|
134
|
+
@rules_header_box.pack_start(cb, expand: false, fill: false)
|
135
|
+
@rules_header_box.pack_start(label2, expand: false, fill: false)
|
136
|
+
else
|
137
|
+
label = Gtk::Label.new
|
138
|
+
label.set_alignment(0.0, 0.5)
|
139
|
+
label.text = _("Match the following rule:")
|
140
|
+
@rules_header_box << label
|
141
|
+
@predicate_operator_rule = SmartLibrary::ALL_RULES
|
142
|
+
end
|
143
|
+
|
144
|
+
@rules_header_box.show_all
|
145
|
+
end
|
146
|
+
|
147
|
+
def make_rule_box(rule = nil)
|
148
|
+
box_controller = SmartLibraryRuleBox.new self
|
149
|
+
rule_box = box_controller.rule_box
|
150
|
+
rule_box.show_all
|
151
|
+
@rules_box.pack_start(rule_box, expand: false, fill: true)
|
152
|
+
|
153
|
+
if rule
|
154
|
+
operands = SmartLibrary::Rule::Operands::LEFT
|
155
|
+
operand_idx = operands.index(rule.operand)
|
156
|
+
operations =
|
157
|
+
SmartLibrary::Rule.operations_for_operand(rule.operand)
|
158
|
+
operation_idx = operations.map(&:first).index(rule.operation)
|
159
|
+
|
160
|
+
if !operand_idx.nil? && !operation_idx.nil?
|
161
|
+
box_controller.left_operand_combo.active = operand_idx
|
162
|
+
box_controller.operator_combo.active = operation_idx
|
163
|
+
unless rule.value.nil?
|
164
|
+
case rule.value
|
165
|
+
when String
|
166
|
+
box_controller.value_entry.text = rule.value
|
167
|
+
when Time
|
168
|
+
box_controller.date_entry.text = format_date(rule.value)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
else
|
173
|
+
box_controller.left_operand_combo.active = 0
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def insert_new_rule(rule = nil)
|
178
|
+
make_rule_box(rule)
|
179
|
+
@rules_box.check_resize # force a layout
|
180
|
+
update_rules_header_box
|
181
|
+
sensitize_remove_rule_buttons
|
182
|
+
end
|
183
|
+
|
184
|
+
def remove_rule_box(rule_box)
|
185
|
+
idx = @rules_box.children.index(rule_box)
|
186
|
+
raise if idx.nil?
|
187
|
+
|
188
|
+
smart_library_rules.delete_at(idx)
|
189
|
+
@rules_box.remove(rule_box)
|
190
|
+
sensitize_remove_rule_buttons
|
191
|
+
update_rules_header_box
|
192
|
+
end
|
193
|
+
|
194
|
+
def sensitize_remove_rule_buttons
|
195
|
+
boxes = @rules_box.children
|
196
|
+
state = boxes.length > 1
|
197
|
+
boxes.each do |box|
|
198
|
+
button = box.children[-1]
|
199
|
+
button.sensitive = state if button.is_a?(Gtk::Button)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def fill_smart_library_rules_values
|
204
|
+
@rules_box.children.each_with_index do |box, i|
|
205
|
+
entry, date = box.children[2..3]
|
206
|
+
value = nil
|
207
|
+
if entry.visible?
|
208
|
+
value = entry.text.strip
|
209
|
+
elsif date.visible?
|
210
|
+
begin
|
211
|
+
value = parse_date(date.text)
|
212
|
+
rescue StandardError => ex
|
213
|
+
trace = ex.backtrace.join("\n > ")
|
214
|
+
log.warn { "Possibly invalid date entered #{ex.message}" }
|
215
|
+
log.warn { "Date widget returned #{date.text} / #{trace}" }
|
216
|
+
# user entered some non-date...
|
217
|
+
# default to current time, for the moment
|
218
|
+
value = Time.now
|
219
|
+
end
|
220
|
+
end
|
221
|
+
smart_library_rules[i].value = value
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def parse_date(datestring)
|
226
|
+
# '%m/%d/%Y' for USA and Canada ; or '%Y-%m-%d' for most of Asia
|
227
|
+
# http://en.wikipedia.org/wiki/Calendar_date#Middle_endian_forms.2C_starting_with_the_month
|
228
|
+
date_format = "%d/%m/%Y"
|
229
|
+
begin
|
230
|
+
d = Date.strptime(datestring, date_format)
|
231
|
+
Time.gm(d.year, d.month, d.day)
|
232
|
+
rescue StandardError
|
233
|
+
nil
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def format_date(datetime)
|
238
|
+
datetime.strftime("%d/%m/%Y")
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is part of Alexandria.
|
4
|
+
#
|
5
|
+
# See the file README.md for authorship and licensing information.
|
6
|
+
|
7
|
+
module Alexandria
|
8
|
+
module UI
|
9
|
+
class SmartLibraryRuleBox
|
10
|
+
attr_accessor :rule_box,
|
11
|
+
:left_operand_combo, :operator_combo,
|
12
|
+
:value_entry, :date_entry, :entry_label,
|
13
|
+
:add_button, :remove_button
|
14
|
+
|
15
|
+
def initialize(parent)
|
16
|
+
@parent = parent
|
17
|
+
|
18
|
+
self.rule_box = Gtk::Box.new :horizontal
|
19
|
+
rule_box.spacing = 8
|
20
|
+
|
21
|
+
self.left_operand_combo = Gtk::ComboBoxText.new
|
22
|
+
self.operator_combo = Gtk::ComboBoxText.new
|
23
|
+
|
24
|
+
self.value_entry = Gtk::Entry.new
|
25
|
+
|
26
|
+
self.date_entry = Gtk::Entry.new.tap do |entry|
|
27
|
+
entry.primary_icon_name = Gtk::Stock::EDIT
|
28
|
+
|
29
|
+
entry.primary_icon_activatable = true
|
30
|
+
entry.signal_connect("icon-press") do |widget, primary, icon|
|
31
|
+
@parent.handle_date_icon_press(widget, primary, icon)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
self.entry_label = Gtk::Label.new("")
|
36
|
+
|
37
|
+
self.add_button = Gtk::Button.new(label: "").tap do |widget|
|
38
|
+
widget.remove(widget.children.first)
|
39
|
+
widget << Gtk::Image.new(stock: Gtk::Stock::ADD,
|
40
|
+
size: Gtk::IconSize::BUTTON)
|
41
|
+
|
42
|
+
widget.signal_connect("clicked") { @parent.handle_add_rule_clicked }
|
43
|
+
end
|
44
|
+
|
45
|
+
self.remove_button = Gtk::Button.new(label: "")
|
46
|
+
remove_button.remove(remove_button.children.first)
|
47
|
+
remove_button << Gtk::Image.new(stock: Gtk::Stock::REMOVE,
|
48
|
+
size: Gtk::IconSize::BUTTON)
|
49
|
+
|
50
|
+
remove_button.signal_connect("clicked") do |_button|
|
51
|
+
@parent.handle_remove_rule_clicked(self)
|
52
|
+
end
|
53
|
+
|
54
|
+
operands.each do |operand|
|
55
|
+
left_operand_combo.append_text(operand.name)
|
56
|
+
end
|
57
|
+
|
58
|
+
operator_combo.signal_connect("changed") do
|
59
|
+
handle_operator_changed
|
60
|
+
end
|
61
|
+
|
62
|
+
left_operand_combo.signal_connect("changed") do
|
63
|
+
handle_left_operand_changed
|
64
|
+
end
|
65
|
+
|
66
|
+
rule_box.pack_start(left_operand_combo, expand: false, fill: false)
|
67
|
+
rule_box.pack_start(operator_combo, expand: false, fill: false)
|
68
|
+
rule_box.pack_start(value_entry)
|
69
|
+
rule_box.pack_start(date_entry)
|
70
|
+
rule_box.pack_start(entry_label, expand: false, fill: false)
|
71
|
+
rule_box.pack_end(remove_button, expand: false, fill: false)
|
72
|
+
rule_box.pack_end(add_button, expand: false, fill: false)
|
73
|
+
|
74
|
+
value_entry.visible = date_entry.visible = entry_label.visible = false
|
75
|
+
end
|
76
|
+
|
77
|
+
def operands
|
78
|
+
SmartLibrary::Rule::Operands::LEFT
|
79
|
+
end
|
80
|
+
|
81
|
+
def handle_operator_changed
|
82
|
+
operand = operands[left_operand_combo.active]
|
83
|
+
operations = SmartLibrary::Rule.operations_for_operand(operand)
|
84
|
+
operation = operations[operator_combo.active]
|
85
|
+
|
86
|
+
value_entry.visible = date_entry.visible = entry_label.visible = false
|
87
|
+
right_operand = operation.last
|
88
|
+
unless right_operand.nil?
|
89
|
+
entry = case right_operand.klass.name
|
90
|
+
when "Time"
|
91
|
+
date_entry
|
92
|
+
else
|
93
|
+
value_entry
|
94
|
+
end
|
95
|
+
entry.visible = true
|
96
|
+
unless right_operand.name.nil?
|
97
|
+
entry_label.text = right_operand.name
|
98
|
+
entry_label.visible = true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
@parent.apply_smart_rule_for_rule_box(rule_box, operand, operation)
|
103
|
+
end
|
104
|
+
|
105
|
+
def handle_left_operand_changed
|
106
|
+
operand = operands[left_operand_combo.active]
|
107
|
+
operator_combo.freeze_notify do
|
108
|
+
operator_combo.remove_all
|
109
|
+
operations = SmartLibrary::Rule.operations_for_operand(operand)
|
110
|
+
operations.each do |operation|
|
111
|
+
operator = operation.first
|
112
|
+
operator_combo.append_text(operator.name)
|
113
|
+
end
|
114
|
+
operator_combo.active = 0
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/alexandria/ui/sound.rb
CHANGED
@@ -22,7 +22,7 @@
|
|
22
22
|
# Boston, MA 02110-1301 USA.
|
23
23
|
#++
|
24
24
|
|
25
|
-
require
|
25
|
+
require "gst"
|
26
26
|
|
27
27
|
module Alexandria
|
28
28
|
module UI
|
@@ -39,20 +39,20 @@ module Alexandria
|
|
39
39
|
def play(effect)
|
40
40
|
file = File.join(@sounds_dir, "#{effect}.ogg")
|
41
41
|
if @playing
|
42
|
-
|
42
|
+
log.debug { "Already playing #{effect}." }
|
43
43
|
else
|
44
|
-
|
44
|
+
log.debug { "Not playing. Starting #{effect}." }
|
45
45
|
@filesrc.location = file
|
46
46
|
start_playback
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
50
|
def set_up_pipeline
|
51
|
-
@filesrc = Gst::ElementFactory.make(
|
52
|
-
demuxer = Gst::ElementFactory.make(
|
53
|
-
decoder = Gst::ElementFactory.make(
|
54
|
-
converter = Gst::ElementFactory.make(
|
55
|
-
audiosink = Gst::ElementFactory.make(
|
51
|
+
@filesrc = Gst::ElementFactory.make("filesrc")
|
52
|
+
demuxer = Gst::ElementFactory.make("oggdemux")
|
53
|
+
decoder = Gst::ElementFactory.make("vorbisdec")
|
54
|
+
converter = Gst::ElementFactory.make("audioconvert") # #??
|
55
|
+
audiosink = Gst::ElementFactory.make("autoaudiosink")
|
56
56
|
|
57
57
|
@ogg_vorbis_pipeline.add(@filesrc, demuxer, decoder,
|
58
58
|
converter, audiosink)
|
@@ -61,7 +61,7 @@ module Alexandria
|
|
61
61
|
# this next must be a dynamic link, as demuxers potentially
|
62
62
|
# have multiple src pads (for audio/video muxed streams)
|
63
63
|
|
64
|
-
demuxer.signal_connect(
|
64
|
+
demuxer.signal_connect("pad-added") do |_parser, ogg_src_pad|
|
65
65
|
vorbis_sink_pad = decoder.sinkpads.first
|
66
66
|
ogg_src_pad.link(vorbis_sink_pad)
|
67
67
|
end
|
@@ -76,10 +76,8 @@ module Alexandria
|
|
76
76
|
when Gst::MessageType::EOS
|
77
77
|
stop_playback
|
78
78
|
when Gst::MessageType::ERROR
|
79
|
-
|
80
|
-
|
81
|
-
p message.parse
|
82
|
-
end
|
79
|
+
log.debug { "ERROR loop.quit" }
|
80
|
+
log.debug { message.parse.inspect }
|
83
81
|
stop_playback
|
84
82
|
end
|
85
83
|
true
|
@@ -1,38 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
|
-
# Copyright (C) 2008 Joseph Method
|
5
|
-
# Copyright (C) 2011, 2016 Matijs van Zuijlen
|
3
|
+
# This file is part of Alexandria.
|
6
4
|
#
|
7
|
-
#
|
8
|
-
# modify it under the terms of the GNU General Public License as
|
9
|
-
# published by the Free Software Foundation; either version 2 of the
|
10
|
-
# License, or (at your option) any later version.
|
11
|
-
#
|
12
|
-
# Alexandria is distributed in the hope that it will be useful,
|
13
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
-
# General Public License for more details.
|
16
|
-
#
|
17
|
-
# You should have received a copy of the GNU General Public
|
18
|
-
# License along with Alexandria; see the file COPYING. If not,
|
19
|
-
# write to the Free Software Foundation, Inc., 51 Franklin Street,
|
20
|
-
# Fifth Floor, Boston, MA 02110-1301 USA.
|
5
|
+
# See the file README.md for authorship and licensing information.
|
21
6
|
|
22
|
-
require
|
7
|
+
require "alexandria/ui/callbacks"
|
8
|
+
require "alexandria/ui/columns"
|
9
|
+
require "alexandria/ui/conflict_while_copying_dialog"
|
10
|
+
require "alexandria/library_sort_order"
|
23
11
|
|
24
12
|
module Alexandria
|
25
13
|
module UI
|
26
14
|
class UIManager < BuilderBase
|
27
|
-
attr_accessor :main_app, :actiongroup, :appbar, :prefs, :listview, :iconview,
|
28
|
-
:iconview_model, :filtered_model
|
15
|
+
attr_accessor :main_app, :actiongroup, :appbar, :prefs, :listview, :iconview,
|
16
|
+
:listview_model, :iconview_model, :filtered_model
|
29
17
|
attr_reader :model
|
18
|
+
|
30
19
|
include Logging
|
31
20
|
include GetText
|
32
|
-
GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset:
|
21
|
+
GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
|
33
22
|
|
34
23
|
def initialize(parent)
|
35
|
-
super(
|
24
|
+
super("main_app__builder.glade", widget_names)
|
36
25
|
@parent = parent
|
37
26
|
|
38
27
|
@library_separator_iter = nil
|
@@ -60,7 +49,7 @@ module Alexandria
|
|
60
49
|
log.debug { "UI Manager initialized: #{@iconview.model.inspect}" }
|
61
50
|
@clicking_on_sidepane = true
|
62
51
|
|
63
|
-
@library_listview.signal_connect(
|
52
|
+
@library_listview.signal_connect("cursor-changed") do
|
64
53
|
@clicking_on_sidepane = true
|
65
54
|
end
|
66
55
|
end
|
@@ -76,24 +65,23 @@ module Alexandria
|
|
76
65
|
end
|
77
66
|
|
78
67
|
def create_uimanager
|
79
|
-
log.debug {
|
68
|
+
log.debug { "Adding actiongroup to uimanager" }
|
80
69
|
@uimanager = Gtk::UIManager.new
|
81
70
|
@uimanager.insert_action_group(@actiongroup, 0)
|
82
71
|
end
|
83
72
|
|
84
73
|
def setup_dependents
|
85
|
-
@listview_model = Gtk::TreeModelSort.new(
|
86
|
-
@iconview_model = Gtk::TreeModelSort.new(
|
74
|
+
@listview_model = Gtk::TreeModelSort.new(@filtered_model)
|
75
|
+
@iconview_model = Gtk::TreeModelSort.new(@filtered_model)
|
87
76
|
@listview_manager = ListViewManager.new @listview, self
|
88
77
|
@iconview_manager = IconViewManager.new @iconview, self
|
89
|
-
@sidepane_manager =
|
78
|
+
@sidepane_manager = SidepaneManager.new @library_listview, self
|
90
79
|
@library_listview = @sidepane_manager.library_listview
|
91
80
|
@listview_manager.setup_listview_columns_visibility
|
92
81
|
@listview_manager.setup_listview_columns_width
|
93
82
|
end
|
94
83
|
|
95
84
|
def setup_callbacks
|
96
|
-
require 'alexandria/ui/callbacks'
|
97
85
|
self.class.send(:include, Callbacks)
|
98
86
|
connect_signals
|
99
87
|
end
|
@@ -103,10 +91,10 @@ module Alexandria
|
|
103
91
|
end
|
104
92
|
|
105
93
|
def setup_toolbar
|
106
|
-
log.debug {
|
94
|
+
log.debug { "setup_toolbar" }
|
107
95
|
setup_book_providers
|
108
96
|
add_main_toolbar_items
|
109
|
-
@toolbar = @uimanager.get_widget(
|
97
|
+
@toolbar = @uimanager.get_widget("/MainToolbar")
|
110
98
|
@toolbar.show_arrow = true
|
111
99
|
@toolbar.insert(Gtk::SeparatorToolItem.new, -1)
|
112
100
|
setup_toolbar_combobox
|
@@ -114,18 +102,18 @@ module Alexandria
|
|
114
102
|
@toolbar.insert(Gtk::SeparatorToolItem.new, -1)
|
115
103
|
setup_toolbar_viewas
|
116
104
|
@toolbar.show_all
|
117
|
-
@actiongroup[
|
105
|
+
@actiongroup["Undo"].sensitive = @actiongroup["Redo"].sensitive = false
|
118
106
|
UndoManager.instance.add_observer(self)
|
119
107
|
@vbox1.add(@toolbar, position: 1, expand: false, fill: false)
|
120
108
|
end
|
121
109
|
|
122
110
|
def add_main_toolbar_items
|
123
111
|
mid = @uimanager.new_merge_id
|
124
|
-
@uimanager.add_ui(mid,
|
112
|
+
@uimanager.add_ui(mid, "ui/", "MainToolbar", "MainToolbar",
|
125
113
|
:toolbar, false)
|
126
|
-
@uimanager.add_ui(mid,
|
114
|
+
@uimanager.add_ui(mid, "ui/MainToolbar/", "New", "New",
|
127
115
|
:toolitem, false)
|
128
|
-
@uimanager.add_ui(mid,
|
116
|
+
@uimanager.add_ui(mid, "ui/MainToolbar/", "AddBook", "AddBook",
|
129
117
|
:toolitem, false)
|
130
118
|
# @uimanager.add_ui(mid, "ui/MainToolbar/", "sep", "sep",
|
131
119
|
# :separator, false)
|
@@ -135,11 +123,11 @@ module Alexandria
|
|
135
123
|
|
136
124
|
def setup_toolbar_filter_entry
|
137
125
|
@filter_entry = Gtk::Entry.new
|
138
|
-
@filter_entry.signal_connect(
|
126
|
+
@filter_entry.signal_connect("changed", &method(:on_toolbar_filter_entry_changed))
|
139
127
|
@toolitem = Gtk::ToolItem.new
|
140
128
|
@toolitem.expand = true
|
141
129
|
@toolitem.border_width = 5
|
142
|
-
@filter_entry.set_tooltip_text _(
|
130
|
+
@filter_entry.set_tooltip_text _("Type here the search criterion")
|
143
131
|
@toolitem << @filter_entry
|
144
132
|
@toolbar.insert(@toolitem, -1)
|
145
133
|
end
|
@@ -148,20 +136,20 @@ module Alexandria
|
|
148
136
|
cb = Gtk::ComboBoxText.new
|
149
137
|
cb.set_row_separator_func do |model, iter|
|
150
138
|
# TODO: Replace with iter[0] if possible
|
151
|
-
model.get_value(iter, 0) ==
|
139
|
+
model.get_value(iter, 0) == "-"
|
152
140
|
end
|
153
|
-
[_(
|
154
|
-
|
155
|
-
_(
|
156
|
-
_(
|
157
|
-
_(
|
158
|
-
_(
|
159
|
-
_(
|
160
|
-
_(
|
141
|
+
[_("Match everything"),
|
142
|
+
"-",
|
143
|
+
_("Title contains"),
|
144
|
+
_("Authors contain"),
|
145
|
+
_("ISBN contains"),
|
146
|
+
_("Publisher contains"),
|
147
|
+
_("Notes contain"),
|
148
|
+
_("Tags contain")].each do |item|
|
161
149
|
cb.append_text(item)
|
162
150
|
end
|
163
151
|
cb.active = 0
|
164
|
-
cb.signal_connect(
|
152
|
+
cb.signal_connect("changed", &method(:on_criterion_combobox_changed))
|
165
153
|
|
166
154
|
# Put the combo box in a event box because it is not currently
|
167
155
|
# possible assign a tooltip to a combo box.
|
@@ -171,16 +159,16 @@ module Alexandria
|
|
171
159
|
@toolitem.border_width = 5
|
172
160
|
@toolitem << eb
|
173
161
|
@toolbar.insert(@toolitem, -1)
|
174
|
-
eb.set_tooltip_text _(
|
162
|
+
eb.set_tooltip_text _("Change the search type")
|
175
163
|
end
|
176
164
|
|
177
165
|
def setup_toolbar_viewas
|
178
166
|
@toolbar_view_as = Gtk::ComboBoxText.new
|
179
|
-
@toolbar_view_as.append_text(_(
|
180
|
-
@toolbar_view_as.append_text(_(
|
167
|
+
@toolbar_view_as.append_text(_("View as Icons"))
|
168
|
+
@toolbar_view_as.append_text(_("View as List"))
|
181
169
|
@toolbar_view_as.active = 0
|
182
170
|
@toolbar_view_as_signal_hid = \
|
183
|
-
@toolbar_view_as.signal_connect(
|
171
|
+
@toolbar_view_as.signal_connect("changed", &method(:on_toolbar_view_as_changed))
|
184
172
|
|
185
173
|
# Put the combo box in a event box because it is not currently
|
186
174
|
# possible assign a tooltip to a combo box.
|
@@ -190,59 +178,60 @@ module Alexandria
|
|
190
178
|
@toolitem.border_width = 5
|
191
179
|
@toolitem << eb
|
192
180
|
@toolbar.insert(@toolitem, -1)
|
193
|
-
eb.set_tooltip_text _(
|
181
|
+
eb.set_tooltip_text _("Choose how to show books")
|
194
182
|
end
|
195
183
|
|
196
184
|
def setup_book_providers
|
197
|
-
log.debug {
|
185
|
+
log.debug { "setup_book_providers" }
|
198
186
|
mid = @uimanager.new_merge_id
|
199
|
-
|
187
|
+
ui_paths = ["ui/MainMenubar/ViewMenu/OnlineInformation/",
|
188
|
+
"ui/BookPopup/OnlineInformation/",
|
189
|
+
"ui/NoBookPopup/OnlineInformation/"]
|
190
|
+
BookProviders.list.each do |provider|
|
200
191
|
name = provider.action_name
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
:menuitem, false)
|
207
|
-
end
|
192
|
+
ui_paths.each do |path|
|
193
|
+
log.debug { "Adding #{name} to #{path}" }
|
194
|
+
@uimanager.add_ui(mid, path, name, name,
|
195
|
+
:menuitem, false)
|
196
|
+
end
|
208
197
|
end
|
209
198
|
end
|
210
199
|
|
211
200
|
def add_menus_and_popups_from_xml
|
212
|
-
log.debug {
|
213
|
-
[
|
201
|
+
log.debug { "add_menus_and_popups_from_xml" }
|
202
|
+
["menus.xml", "popups.xml"].each do |ui_file|
|
214
203
|
@uimanager.add_ui(File.join(Alexandria::Config::DATA_DIR,
|
215
|
-
|
204
|
+
"ui", ui_file))
|
216
205
|
end
|
217
206
|
end
|
218
207
|
|
219
208
|
def setup_accel_group
|
220
|
-
log.debug {
|
209
|
+
log.debug { "setup_accel_group" }
|
221
210
|
@main_app.add_accel_group(@uimanager.accel_group)
|
222
211
|
end
|
223
212
|
|
224
213
|
def setup_menus
|
225
|
-
@menubar = @uimanager.get_widget(
|
214
|
+
@menubar = @uimanager.get_widget("/MainMenubar")
|
226
215
|
@vbox1.add(@menubar, position: 0, expand: false, fill: false)
|
227
216
|
end
|
228
217
|
|
229
218
|
def setup_popups
|
230
|
-
log.debug {
|
231
|
-
@library_popup = @uimanager.get_widget(
|
232
|
-
@smart_library_popup = @uimanager.get_widget(
|
233
|
-
@nolibrary_popup = @uimanager.get_widget(
|
234
|
-
@book_popup = @uimanager.get_widget(
|
235
|
-
@nobook_popup = @uimanager.get_widget(
|
219
|
+
log.debug { "setup_popups" }
|
220
|
+
@library_popup = @uimanager.get_widget("/LibraryPopup")
|
221
|
+
@smart_library_popup = @uimanager.get_widget("/SmartLibraryPopup")
|
222
|
+
@nolibrary_popup = @uimanager.get_widget("/NoLibraryPopup")
|
223
|
+
@book_popup = @uimanager.get_widget("/BookPopup")
|
224
|
+
@nobook_popup = @uimanager.get_widget("/NoBookPopup")
|
236
225
|
end
|
237
226
|
|
238
227
|
def setup_window_events
|
239
|
-
log.debug {
|
240
|
-
@main_app.signal_connect(
|
241
|
-
@main_app.signal_connect(
|
228
|
+
log.debug { "setup_window_events" }
|
229
|
+
@main_app.signal_connect("window-state-event", &method(:on_window_state_event))
|
230
|
+
@main_app.signal_connect("destroy", &method(:on_window_destroy))
|
242
231
|
end
|
243
232
|
|
244
233
|
def setup_active_model
|
245
|
-
log.debug {
|
234
|
+
log.debug { "setting up active model" }
|
246
235
|
# The active model.
|
247
236
|
|
248
237
|
list = [
|
@@ -277,13 +266,13 @@ module Alexandria
|
|
277
266
|
true
|
278
267
|
else
|
279
268
|
data = case @filter_books_mode
|
280
|
-
when 0
|
281
|
-
(iter[Columns::TITLE] ||
|
282
|
-
(iter[Columns::AUTHORS] ||
|
283
|
-
(iter[Columns::ISBN] ||
|
284
|
-
(iter[Columns::PUBLISHER] ||
|
285
|
-
(iter[Columns::NOTES] ||
|
286
|
-
(iter[Columns::TAGS] ||
|
269
|
+
when 0
|
270
|
+
(iter[Columns::TITLE] || "") +
|
271
|
+
(iter[Columns::AUTHORS] || "") +
|
272
|
+
(iter[Columns::ISBN] || "") +
|
273
|
+
(iter[Columns::PUBLISHER] || "") +
|
274
|
+
(iter[Columns::NOTES] || "") +
|
275
|
+
(iter[Columns::TAGS] || "")
|
287
276
|
when 2 then iter[Columns::TITLE]
|
288
277
|
when 3 then iter[Columns::AUTHORS]
|
289
278
|
when 4 then iter[Columns::ISBN]
|
@@ -297,19 +286,20 @@ module Alexandria
|
|
297
286
|
|
298
287
|
# Give filter entry the initial keyboard focus.
|
299
288
|
@filter_entry.grab_focus
|
300
|
-
log.debug {
|
289
|
+
log.debug { "done setting up active model" }
|
301
290
|
end
|
302
291
|
|
303
292
|
def on_library_button_press_event(widget, event)
|
304
|
-
log.debug {
|
293
|
+
log.debug { "library_button_press_event" }
|
305
294
|
|
306
295
|
# right click
|
307
296
|
if event_is_right_click event
|
308
|
-
log.debug {
|
297
|
+
log.debug { "library right click!" }
|
309
298
|
library_already_selected = true
|
310
299
|
if (path = widget.get_path_at_pos(event.x, event.y))
|
311
300
|
@clicking_on_sidepane = true
|
312
|
-
obj, path =
|
301
|
+
obj, path =
|
302
|
+
widget.is_a?(Gtk::TreeView) ? [widget.selection, path.first] : [widget, path]
|
313
303
|
widget.has_focus = true
|
314
304
|
|
315
305
|
unless obj.path_is_selected?(path)
|
@@ -350,34 +340,20 @@ module Alexandria
|
|
350
340
|
# seem to suffice).
|
351
341
|
#
|
352
342
|
# Then we wait a while and only *then* pop up the menu.
|
343
|
+
sensitize_library selected_library if library_already_selected
|
353
344
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
GLib::Idle.add do
|
358
|
-
menu.popup(nil, nil, event.button, event.time)
|
359
|
-
# @clicking_on_sidepane = false
|
360
|
-
false
|
361
|
-
end
|
362
|
-
else
|
363
|
-
GLib::Idle.add do
|
364
|
-
menu.popup(nil, nil, event.button, event.time)
|
365
|
-
# @clicking_on_sidepane = false
|
366
|
-
|
367
|
-
false
|
368
|
-
end
|
345
|
+
GLib::Idle.add do
|
346
|
+
menu.popup(nil, nil, event.button, event.time)
|
347
|
+
false
|
369
348
|
end
|
370
349
|
|
371
|
-
else
|
372
350
|
# not a right click
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
end
|
380
|
-
|
351
|
+
elsif (path = widget.get_path_at_pos(event.x, event.y))
|
352
|
+
@clicking_on_sidepane = true
|
353
|
+
obj, path =
|
354
|
+
widget.is_a?(Gtk::TreeView) ? [widget.selection, path.first] : [widget, path]
|
355
|
+
obj.select_path(path)
|
356
|
+
sensitize_library selected_library
|
381
357
|
end
|
382
358
|
end
|
383
359
|
|
@@ -396,25 +372,26 @@ module Alexandria
|
|
396
372
|
end
|
397
373
|
|
398
374
|
def on_books_button_press_event(widget, event)
|
399
|
-
log.debug {
|
400
|
-
|
401
|
-
widget.grab_focus
|
375
|
+
log.debug { "books_button_press_event" }
|
376
|
+
event_is_right_click event or return
|
402
377
|
|
403
|
-
|
404
|
-
obj, path = widget.is_a?(Gtk::TreeView) ? [widget.selection, path.first] : [widget, path]
|
378
|
+
widget.grab_focus
|
405
379
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
380
|
+
if (path = widget.get_path_at_pos(event.x.to_i, event.y.to_i))
|
381
|
+
obj, path =
|
382
|
+
widget.is_a?(Gtk::TreeView) ? [widget.selection, path.first] : [widget, path]
|
383
|
+
|
384
|
+
unless obj.path_is_selected?(path)
|
385
|
+
log.debug { "Select #{path}" }
|
412
386
|
widget.unselect_all
|
387
|
+
obj.select_path(path)
|
413
388
|
end
|
414
|
-
|
415
|
-
|
416
|
-
menu.popup(nil, nil, event.button, event.time)
|
389
|
+
else
|
390
|
+
widget.unselect_all
|
417
391
|
end
|
392
|
+
|
393
|
+
menu = selected_books.empty? ? @nobook_popup : @book_popup
|
394
|
+
menu.popup(nil, nil, event.button, event.time)
|
418
395
|
end
|
419
396
|
|
420
397
|
def get_library_selection_text(library)
|
@@ -434,9 +411,9 @@ module Alexandria
|
|
434
411
|
library.length), library.name, library.length)
|
435
412
|
else
|
436
413
|
format(n_("Library '%s' selected, %d book, " \
|
437
|
-
|
414
|
+
"%d unrated",
|
438
415
|
"Library '%s' selected, %d books, " \
|
439
|
-
|
416
|
+
"%d unrated",
|
440
417
|
library.length), library.name, library.length, n_unrated)
|
441
418
|
end
|
442
419
|
end
|
@@ -449,7 +426,7 @@ module Alexandria
|
|
449
426
|
when 1
|
450
427
|
_("'%s' selected") % books.first.title
|
451
428
|
else
|
452
|
-
n_(
|
429
|
+
n_("%d book selected", "%d books selected",
|
453
430
|
books.length) % books.length
|
454
431
|
end
|
455
432
|
end
|
@@ -469,28 +446,30 @@ module Alexandria
|
|
469
446
|
|
470
447
|
log.debug { "Currently focused widget: #{@main_app.focus.inspect}" }
|
471
448
|
log.debug { "#{@library_listview} : #{@library_popup} : #{@listview}" }
|
472
|
-
log.debug
|
449
|
+
log.debug do
|
473
450
|
"@library_listview: #{@library_listview.has_focus?} " \
|
474
451
|
"or @library_popup:#{@library_popup.has_focus?}"
|
475
|
-
|
476
|
-
log.debug {
|
452
|
+
end
|
453
|
+
log.debug { "@library_listview does *NOT* have focus" }
|
477
454
|
log.debug { "Books are empty: #{books.empty?}" }
|
478
|
-
@actiongroup[
|
479
|
-
@actiongroup[
|
455
|
+
@actiongroup["Properties"].sensitive = \
|
456
|
+
@actiongroup["OnlineInformation"].sensitive = \
|
480
457
|
books.length == 1
|
481
|
-
@actiongroup[
|
458
|
+
@actiongroup["SelectAll"].sensitive = \
|
482
459
|
books.length < library.length
|
483
460
|
|
484
|
-
@actiongroup[
|
485
|
-
@actiongroup[
|
486
|
-
@actiongroup[
|
487
|
-
@actiongroup[
|
461
|
+
@actiongroup["Delete"].sensitive = \
|
462
|
+
@actiongroup["DeselectAll"].sensitive = \
|
463
|
+
@actiongroup["Move"].sensitive =
|
464
|
+
@actiongroup["SetRating"].sensitive = !books.empty?
|
488
465
|
|
489
|
-
log.debug
|
466
|
+
log.debug do
|
467
|
+
"on_books_selection_changed Delete: #{@actiongroup['Delete'].sensitive?}"
|
468
|
+
end
|
490
469
|
|
491
470
|
if library.is_a?(SmartLibrary)
|
492
|
-
@actiongroup[
|
493
|
-
@actiongroup[
|
471
|
+
@actiongroup["Delete"].sensitive =
|
472
|
+
@actiongroup["Move"].sensitive = false
|
494
473
|
end
|
495
474
|
|
496
475
|
# Sensitize providers URL
|
@@ -498,37 +477,37 @@ module Alexandria
|
|
498
477
|
b = books.first
|
499
478
|
# FIXME: Clean up endless negation in this logic
|
500
479
|
no_urls = true
|
501
|
-
BookProviders.each do |provider|
|
480
|
+
BookProviders.list.each do |provider|
|
502
481
|
has_no_url = true
|
503
482
|
begin
|
504
483
|
has_no_url = (b.isbn.nil? || b.isbn.strip.empty? || provider.url(b).nil?)
|
505
|
-
rescue => ex
|
484
|
+
rescue StandardError => ex
|
506
485
|
log.warn { "Error determining URL from #{provider.name}; #{ex.message}" }
|
507
486
|
end
|
508
487
|
@actiongroup[provider.action_name].sensitive = !has_no_url
|
509
488
|
no_urls = false unless has_no_url
|
510
489
|
end
|
511
|
-
@actiongroup[
|
490
|
+
@actiongroup["OnlineInformation"].sensitive = false if no_urls
|
512
491
|
end
|
513
492
|
end
|
514
493
|
@clicking_on_sidepane = false
|
515
494
|
end
|
516
495
|
|
517
496
|
def on_switch_page(_notebook, _page, page_num)
|
518
|
-
log.debug {
|
519
|
-
@actiongroup[
|
497
|
+
log.debug { "on_switch_page" }
|
498
|
+
@actiongroup["ArrangeIcons"].sensitive = page_num.zero?
|
520
499
|
on_books_selection_changed
|
521
500
|
end
|
522
501
|
|
523
502
|
def on_focus(widget, _event_focus)
|
524
503
|
if @clicking_on_sidepane || (widget == @library_listview)
|
525
|
-
log.debug {
|
504
|
+
log.debug { "on_focus: @library_listview" }
|
526
505
|
GLib::Idle.add do
|
527
506
|
%w(OnlineInformation SelectAll DeselectAll).each do |action|
|
528
507
|
@actiongroup[action].sensitive = false
|
529
508
|
end
|
530
|
-
@actiongroup[
|
531
|
-
@actiongroup[
|
509
|
+
@actiongroup["Properties"].sensitive = selected_library.is_a?(SmartLibrary)
|
510
|
+
@actiongroup["Delete"].sensitive = determine_delete_option
|
532
511
|
false
|
533
512
|
end
|
534
513
|
else
|
@@ -537,50 +516,33 @@ module Alexandria
|
|
537
516
|
end
|
538
517
|
|
539
518
|
def determine_delete_option
|
540
|
-
|
541
|
-
sensitive
|
519
|
+
@libraries.all_regular_libraries.length > 1 || selected_library.is_a?(SmartLibrary)
|
542
520
|
end
|
543
521
|
|
544
522
|
def on_close_sidepane
|
545
|
-
log.debug {
|
546
|
-
@actiongroup[
|
523
|
+
log.debug { "on_close_sidepane" }
|
524
|
+
@actiongroup["Sidepane"].active = false
|
547
525
|
end
|
548
526
|
|
527
|
+
# TODO: Figure out why this frequently selects the wrong book!
|
549
528
|
def select_a_book(book)
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
path = iter.path
|
555
|
-
next unless view.model
|
556
|
-
path = view_path_to_model_path(view, path)
|
557
|
-
log.debug { "Path for #{bk.ident} is #{path}" }
|
558
|
-
selection = view.respond_to?(:selection) ? @listview.selection : @iconview
|
559
|
-
selection.unselect_all
|
560
|
-
selection.select_path(path)
|
561
|
-
end
|
562
|
-
begin
|
563
|
-
log.debug { 'select_a_book: listview' }
|
564
|
-
select_this_book.call(book, @listview)
|
565
|
-
log.debug { 'select_a_book: listview' }
|
566
|
-
select_this_book.call(book, @iconview)
|
567
|
-
rescue => ex
|
568
|
-
trace = ex.backtrace.join("\n> ")
|
569
|
-
log.warn { "Failed to automatically select book: #{ex.message} #{trace}" }
|
570
|
-
end
|
571
|
-
# TODO: Figure out why this frequently selects the wrong book!
|
529
|
+
log.debug { "select_a_book: listview" }
|
530
|
+
select_book_in_view(book, @listview)
|
531
|
+
log.debug { "select_a_book: iconview" }
|
532
|
+
select_book_in_view(book, @iconview)
|
572
533
|
end
|
573
534
|
|
574
535
|
def update(*ary)
|
575
536
|
log.debug { "on_update #{ary}" }
|
576
537
|
caller = ary.first
|
577
|
-
|
578
|
-
|
579
|
-
@actiongroup[
|
580
|
-
|
538
|
+
case caller
|
539
|
+
when UndoManager
|
540
|
+
@actiongroup["Undo"].sensitive = caller.can_undo?
|
541
|
+
@actiongroup["Redo"].sensitive = caller.can_redo?
|
542
|
+
when Library
|
581
543
|
handle_update_caller_library ary unless caller.updating?
|
582
544
|
else
|
583
|
-
raise
|
545
|
+
raise _("unrecognized update event")
|
584
546
|
end
|
585
547
|
end
|
586
548
|
|
@@ -606,18 +568,16 @@ module Alexandria
|
|
606
568
|
end
|
607
569
|
end
|
608
570
|
|
609
|
-
# private
|
610
|
-
|
611
571
|
def open_web_browser(url)
|
612
572
|
if url.nil?
|
613
|
-
log.warn(
|
573
|
+
log.warn("Attempt to open browser with nil url")
|
614
574
|
return
|
615
575
|
end
|
616
576
|
Gtk.show_uri url
|
617
577
|
end
|
618
578
|
|
619
579
|
def detach_old_libraries
|
620
|
-
log.debug {
|
580
|
+
log.debug { "Un-observing old libraries" }
|
621
581
|
@libraries.all_regular_libraries.each do |library|
|
622
582
|
if library.is_a?(Library)
|
623
583
|
library.delete_observer(self)
|
@@ -627,13 +587,13 @@ module Alexandria
|
|
627
587
|
end
|
628
588
|
|
629
589
|
def load_libraries
|
630
|
-
log.info {
|
590
|
+
log.info { _("Loading libraries...") }
|
631
591
|
@completion_models = CompletionModels.instance
|
632
592
|
if @libraries
|
633
593
|
detach_old_libraries
|
634
594
|
@libraries.reload
|
635
595
|
else
|
636
|
-
@libraries =
|
596
|
+
@libraries = LibraryCollection.instance
|
637
597
|
@libraries.reload
|
638
598
|
handle_ruined_books unless @libraries.ruined_books.empty?
|
639
599
|
end
|
@@ -645,19 +605,19 @@ module Alexandria
|
|
645
605
|
|
646
606
|
def handle_ruined_books
|
647
607
|
new_message = _(
|
648
|
-
|
608
|
+
"The data files for the following books are malformed or empty. Do you wish to" \
|
649
609
|
" attempt to download new information for them from the online book providers?\n")
|
650
610
|
|
651
|
-
@libraries.ruined_books.each
|
611
|
+
@libraries.ruined_books.each do |bi|
|
652
612
|
new_message += "\n#{bi[1] || bi[1].inspect}"
|
653
|
-
|
613
|
+
end
|
654
614
|
recovery_dialog = Gtk::MessageDialog.new(@main_app, Gtk::Dialog::MODAL,
|
655
615
|
Gtk::MessageDialog::WARNING,
|
656
616
|
Gtk::MessageDialog::BUTTONS_OK_CANCEL,
|
657
617
|
new_message).show
|
658
|
-
recovery_dialog.signal_connect(
|
618
|
+
recovery_dialog.signal_connect("response") do |_dialog, response_type|
|
659
619
|
recovery_dialog.destroy
|
660
|
-
if response_type ==
|
620
|
+
if response_type == Gtk::ResponseType::OK
|
661
621
|
# progress indicator...
|
662
622
|
@progressbar.fraction = 0
|
663
623
|
@appbar.children.first.visible = true # show the progress bar
|
@@ -684,17 +644,21 @@ module Alexandria
|
|
684
644
|
log.debug { "removing old file #{filename}" }
|
685
645
|
begin
|
686
646
|
File.delete(filename)
|
687
|
-
rescue => ex
|
647
|
+
rescue StandardError => ex
|
688
648
|
log.error { "Could not delete empty file #{filename}" }
|
689
649
|
end
|
690
650
|
end
|
691
651
|
|
692
|
-
log.debug
|
652
|
+
log.debug do
|
653
|
+
"Trying to add #{book.title}, #{cover_uri}" \
|
654
|
+
" in library ''#{library.name}'"
|
655
|
+
end
|
693
656
|
library.save_cover(book, cover_uri) unless cover_uri.nil?
|
694
657
|
library << book
|
695
658
|
library.save(book)
|
696
|
-
set_status_label(format(_("Added '%s' to library '%s'"),
|
697
|
-
|
659
|
+
set_status_label(format(_("Added '%s' to library '%s'"),
|
660
|
+
book.title, library.name))
|
661
|
+
rescue StandardError => ex
|
698
662
|
log.error { "Couldn't add book #{isbn}: #{ex}" }
|
699
663
|
log.error { ex.backtrace.join("\n") }
|
700
664
|
end
|
@@ -715,7 +679,7 @@ module Alexandria
|
|
715
679
|
## Hide the progress bar.
|
716
680
|
@appbar.children.first.visible = false
|
717
681
|
## Refresh the status bar.
|
718
|
-
set_status_label(
|
682
|
+
set_status_label("")
|
719
683
|
# on_books_selection_changed
|
720
684
|
false
|
721
685
|
end
|
@@ -740,22 +704,23 @@ module Alexandria
|
|
740
704
|
iter[Columns::TITLE] = book.title
|
741
705
|
title = book.title.sub(REDUCE_TITLE_REGEX, '\1...')
|
742
706
|
iter[Columns::TITLE_REDUCED] = title
|
743
|
-
iter[Columns::AUTHORS] = book.authors.join(
|
707
|
+
iter[Columns::AUTHORS] = book.authors.join(", ")
|
744
708
|
iter[Columns::ISBN] = book.isbn.to_s
|
745
709
|
iter[Columns::PUBLISHER] = book.publisher
|
746
710
|
iter[Columns::PUBLISH_DATE] = book.publishing_year.to_s
|
747
711
|
iter[Columns::EDITION] = book.edition
|
748
|
-
iter[Columns::NOTES] = (book.notes ||
|
749
|
-
iter[Columns::LOANED_TO] = (book.loaned_to ||
|
712
|
+
iter[Columns::NOTES] = (book.notes || "")
|
713
|
+
iter[Columns::LOANED_TO] = (book.loaned_to || "")
|
750
714
|
rating = (book.rating || Book::DEFAULT_RATING)
|
751
|
-
|
715
|
+
# ascending order is the default
|
716
|
+
iter[Columns::RATING] = Book::MAX_RATING_STARS - rating
|
752
717
|
iter[Columns::OWN] = book.own?
|
753
718
|
iter[Columns::REDD] = book.redd?
|
754
719
|
iter[Columns::WANT] = book.want?
|
755
720
|
iter[Columns::TAGS] = if book.tags
|
756
|
-
book.tags.join(
|
721
|
+
book.tags.join(",")
|
757
722
|
else
|
758
|
-
|
723
|
+
""
|
759
724
|
end
|
760
725
|
|
761
726
|
icon = Icons.cover(selected_library, book)
|
@@ -767,9 +732,9 @@ module Alexandria
|
|
767
732
|
new_height = [ICON_HEIGHT, icon.height].min
|
768
733
|
icon = cache_scaled_icon(icon, new_width, new_height)
|
769
734
|
end
|
770
|
-
icon =
|
735
|
+
icon = Icons.tag_icon(icon, Icons::FAVORITE_TAG) if rating == Book::MAX_RATING_STARS
|
771
736
|
iter[Columns::COVER_ICON] = icon
|
772
|
-
log.debug {
|
737
|
+
log.debug { "Full iter: " + (0..15).map { |num| iter[num].inspect }.join(", ") }
|
773
738
|
end
|
774
739
|
|
775
740
|
def append_book(book, _tail = nil)
|
@@ -779,7 +744,7 @@ module Alexandria
|
|
779
744
|
if iter
|
780
745
|
fill_iter_with_book(iter, book)
|
781
746
|
else
|
782
|
-
log.debug {
|
747
|
+
log.debug { "@model.append" }
|
783
748
|
iter = @model.append
|
784
749
|
fill_iter_with_book(iter, book)
|
785
750
|
log.debug { "no iter for book #{book}" }
|
@@ -816,13 +781,13 @@ module Alexandria
|
|
816
781
|
@library_listview.set_cursor(iter.path,
|
817
782
|
@library_listview.get_column(0),
|
818
783
|
true)
|
819
|
-
@actiongroup[
|
784
|
+
@actiongroup["Sidepane"].active = true
|
820
785
|
end
|
821
786
|
iter
|
822
787
|
end
|
823
788
|
|
824
789
|
def append_library_separator
|
825
|
-
log.debug {
|
790
|
+
log.debug { "append_library_separator" }
|
826
791
|
iter = @library_listview.model.append
|
827
792
|
iter[0] = nil
|
828
793
|
iter[1] = nil
|
@@ -832,12 +797,12 @@ module Alexandria
|
|
832
797
|
end
|
833
798
|
|
834
799
|
def refresh_books
|
835
|
-
log.debug {
|
800
|
+
log.debug { "refresh_books" }
|
836
801
|
@library_listview.set_sensitive(false)
|
837
802
|
library = selected_library
|
838
|
-
@model.clear
|
839
803
|
@iconview.freeze
|
840
|
-
@listview.freeze
|
804
|
+
@listview.freeze
|
805
|
+
@model.clear
|
841
806
|
@progressbar.fraction = 0
|
842
807
|
@appbar.children.first.visible = true # show the progress bar
|
843
808
|
set_status_label(_("Loading '%s'...") % library.name)
|
@@ -851,7 +816,7 @@ module Alexandria
|
|
851
816
|
if book
|
852
817
|
begin
|
853
818
|
append_book(book)
|
854
|
-
rescue => ex
|
819
|
+
rescue StandardError => ex
|
855
820
|
trace = ex.backtrace.join("\n > ")
|
856
821
|
log.error { "append_books failed #{ex.message} #{trace}" }
|
857
822
|
end
|
@@ -878,9 +843,10 @@ module Alexandria
|
|
878
843
|
end
|
879
844
|
|
880
845
|
def selected_library
|
881
|
-
log.debug {
|
846
|
+
log.debug { "selected_library" }
|
882
847
|
if (iter = @library_listview.selection.selected)
|
883
|
-
|
848
|
+
target_name = iter[1]
|
849
|
+
@libraries.all_libraries.find { |it| it.name == target_name }
|
884
850
|
else
|
885
851
|
@libraries.all_libraries.first
|
886
852
|
end
|
@@ -907,9 +873,10 @@ module Alexandria
|
|
907
873
|
def iter_from_ident(ident)
|
908
874
|
log.debug { ident.to_s }
|
909
875
|
iter = @model.iter_first
|
910
|
-
ok = true
|
876
|
+
ok = true if iter
|
911
877
|
while ok
|
912
878
|
return iter if iter[Columns::IDENT] == ident
|
879
|
+
|
913
880
|
ok = iter.next!
|
914
881
|
end
|
915
882
|
nil
|
@@ -948,11 +915,11 @@ module Alexandria
|
|
948
915
|
end
|
949
916
|
|
950
917
|
def refresh_libraries
|
951
|
-
log.debug {
|
918
|
+
log.debug { "refresh_libraries" }
|
952
919
|
library = selected_library
|
953
920
|
|
954
921
|
# Change the application's title.
|
955
|
-
@main_app.title = library.name +
|
922
|
+
@main_app.title = library.name + " - " + TITLE
|
956
923
|
|
957
924
|
# Disable the selected library in the move libraries actions.
|
958
925
|
@libraries.all_regular_libraries.each do |i_library|
|
@@ -966,11 +933,11 @@ module Alexandria
|
|
966
933
|
smart = library.is_a?(SmartLibrary)
|
967
934
|
log.debug { "sensitize_library: smartlibrary = #{smart}" }
|
968
935
|
GLib::Idle.add do
|
969
|
-
@actiongroup[
|
970
|
-
@actiongroup[
|
971
|
-
@actiongroup[
|
936
|
+
@actiongroup["AddBook"].sensitive = !smart
|
937
|
+
@actiongroup["AddBookManual"].sensitive = !smart
|
938
|
+
@actiongroup["Properties"].sensitive = smart
|
972
939
|
can_delete = smart || (@libraries.all_regular_libraries.length > 1)
|
973
|
-
@actiongroup[
|
940
|
+
@actiongroup["Delete"].sensitive = can_delete
|
974
941
|
log.debug { "sensitize_library delete: #{@actiongroup['Delete'].sensitive?}" }
|
975
942
|
false
|
976
943
|
end
|
@@ -979,14 +946,14 @@ module Alexandria
|
|
979
946
|
def get_view_actiongroup
|
980
947
|
case @prefs.view_as
|
981
948
|
when 0
|
982
|
-
@actiongroup[
|
949
|
+
@actiongroup["AsIcons"]
|
983
950
|
when 1
|
984
|
-
@actiongroup[
|
951
|
+
@actiongroup["AsList"]
|
985
952
|
end
|
986
953
|
end
|
987
954
|
|
988
955
|
def restore_preferences
|
989
|
-
log.debug {
|
956
|
+
log.debug { "Restoring preferences" }
|
990
957
|
if @prefs.maximized
|
991
958
|
@main_app.maximize
|
992
959
|
else
|
@@ -995,9 +962,9 @@ module Alexandria
|
|
995
962
|
@maximized = false
|
996
963
|
end
|
997
964
|
@paned.position = @prefs.sidepane_position
|
998
|
-
@actiongroup[
|
999
|
-
@actiongroup[
|
1000
|
-
@actiongroup[
|
965
|
+
@actiongroup["Sidepane"].active = @prefs.sidepane_visible
|
966
|
+
@actiongroup["Toolbar"].active = @prefs.toolbar_visible
|
967
|
+
@actiongroup["Statusbar"].active = @prefs.statusbar_visible
|
1001
968
|
@appbar.visible = @prefs.statusbar_visible
|
1002
969
|
action = get_view_actiongroup
|
1003
970
|
action.activate
|
@@ -1021,29 +988,29 @@ module Alexandria
|
|
1021
988
|
end
|
1022
989
|
|
1023
990
|
def save_preferences
|
1024
|
-
log.debug {
|
991
|
+
log.debug { "save_preferences" }
|
1025
992
|
@prefs.position = @main_app.position
|
1026
993
|
@prefs.size = @main_app.allocation.to_a[2..3]
|
1027
994
|
@prefs.maximized = @maximized
|
1028
995
|
@prefs.sidepane_position = @paned.position
|
1029
|
-
@prefs.sidepane_visible = @actiongroup[
|
1030
|
-
@prefs.toolbar_visible = @actiongroup[
|
1031
|
-
@prefs.statusbar_visible = @actiongroup[
|
996
|
+
@prefs.sidepane_visible = @actiongroup["Sidepane"].active?
|
997
|
+
@prefs.toolbar_visible = @actiongroup["Toolbar"].active?
|
998
|
+
@prefs.statusbar_visible = @actiongroup["Statusbar"].active?
|
1032
999
|
@prefs.view_as = @notebook.page
|
1033
1000
|
@prefs.selected_library = selected_library.name
|
1034
1001
|
cols_width = {}
|
1035
1002
|
@listview.columns.each do |c|
|
1036
1003
|
cols_width[c.title] = c.width
|
1037
1004
|
end
|
1038
|
-
@prefs.cols_width =
|
1005
|
+
@prefs.cols_width = "{" + cols_width.to_a.map do |t, v|
|
1039
1006
|
'"' + t + '": ' + v.to_s
|
1040
|
-
end.join(
|
1007
|
+
end.join(", ") + "}"
|
1041
1008
|
log.debug { "cols_width: #{@prefs.cols_width} " }
|
1042
1009
|
@prefs.save!
|
1043
1010
|
end
|
1044
1011
|
|
1045
1012
|
def undoable_move(source, dest, books)
|
1046
|
-
log.debug {
|
1013
|
+
log.debug { "undoable_move" }
|
1047
1014
|
Library.move(source, dest, *books)
|
1048
1015
|
UndoManager.instance.push { undoable_move(dest, source, books) }
|
1049
1016
|
end
|
@@ -1060,7 +1027,8 @@ module Alexandria
|
|
1060
1027
|
|
1061
1028
|
def setup_move_actions
|
1062
1029
|
@actiongroup.actions.each do |action|
|
1063
|
-
next unless
|
1030
|
+
next unless action.name.start_with?("MoveIn")
|
1031
|
+
|
1064
1032
|
@actiongroup.remove_action(action)
|
1065
1033
|
end
|
1066
1034
|
actions = []
|
@@ -1074,10 +1042,11 @@ module Alexandria
|
|
1074
1042
|
@actiongroup.add_actions(actions)
|
1075
1043
|
@uimanager.remove_ui(@move_mid) if @move_mid
|
1076
1044
|
@move_mid = @uimanager.new_merge_id
|
1045
|
+
ui_paths = ["ui/MainMenubar/EditMenu/Move/",
|
1046
|
+
"ui/BookPopup/Move/"]
|
1077
1047
|
@libraries.all_regular_libraries.each do |library|
|
1078
1048
|
name = library.action_name
|
1079
|
-
|
1080
|
-
'ui/BookPopup/Move/'].each do |path|
|
1049
|
+
ui_paths.each do |path|
|
1081
1050
|
@uimanager.add_ui(@move_mid, path, name, name,
|
1082
1051
|
:menuitem, false)
|
1083
1052
|
end
|
@@ -1096,22 +1065,22 @@ module Alexandria
|
|
1096
1065
|
# Gets the sort order of the current library, for use by export
|
1097
1066
|
def library_sort_order
|
1098
1067
|
# added by Cathal Mc Ginley, 23 Oct 2007
|
1099
|
-
log.debug
|
1068
|
+
log.debug do
|
1100
1069
|
"library_sort_order #{@notebook.page}: " \
|
1101
1070
|
"#{@iconview.model.inspect} #{@listview.model.inspect}"
|
1102
|
-
|
1071
|
+
end
|
1103
1072
|
result, sort_column, sort_order = current_view.model.sort_column_id
|
1104
1073
|
if result
|
1105
|
-
column_ids_to_attributes = { 2
|
1106
|
-
4
|
1107
|
-
5
|
1108
|
-
6
|
1109
|
-
7
|
1110
|
-
8
|
1074
|
+
column_ids_to_attributes = { 2 => :title,
|
1075
|
+
4 => :authors,
|
1076
|
+
5 => :isbn,
|
1077
|
+
6 => :publisher,
|
1078
|
+
7 => :publishing_year,
|
1079
|
+
8 => :edition, # binding
|
1111
1080
|
12 => :redd,
|
1112
1081
|
13 => :own,
|
1113
1082
|
14 => :want,
|
1114
|
-
9
|
1083
|
+
9 => :rating }
|
1115
1084
|
|
1116
1085
|
sort_attribute = column_ids_to_attributes.fetch sort_column
|
1117
1086
|
ascending = (sort_order == :ascending)
|
@@ -1161,10 +1130,10 @@ module Alexandria
|
|
1161
1130
|
end
|
1162
1131
|
|
1163
1132
|
def remove_library_separator
|
1164
|
-
if
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1133
|
+
return if @library_separator_iter.nil? || @libraries.all_smart_libraries.any?
|
1134
|
+
|
1135
|
+
@library_listview.model.remove(@library_separator_iter)
|
1136
|
+
@library_separator_iter = nil
|
1168
1137
|
end
|
1169
1138
|
|
1170
1139
|
def undoable_undelete(library, books = nil)
|
@@ -1185,13 +1154,14 @@ module Alexandria
|
|
1185
1154
|
|
1186
1155
|
def setup_window_icons
|
1187
1156
|
@main_app.icon = Icons::ALEXANDRIA_SMALL
|
1188
|
-
Gtk::Window.set_default_icon_name(
|
1189
|
-
@main_app.icon_name =
|
1157
|
+
Gtk::Window.set_default_icon_name("alexandria")
|
1158
|
+
@main_app.icon_name = "alexandria"
|
1190
1159
|
end
|
1191
1160
|
|
1192
1161
|
ICONS_SORTS = [
|
1193
1162
|
Columns::TITLE, Columns::AUTHORS, Columns::ISBN,
|
1194
|
-
Columns::PUBLISHER, Columns::EDITION, Columns::RATING,
|
1163
|
+
Columns::PUBLISHER, Columns::EDITION, Columns::RATING,
|
1164
|
+
Columns::REDD, Columns::OWN, Columns::WANT
|
1195
1165
|
].freeze
|
1196
1166
|
|
1197
1167
|
def setup_books_iconview_sorting
|
@@ -1203,6 +1173,21 @@ module Alexandria
|
|
1203
1173
|
|
1204
1174
|
private
|
1205
1175
|
|
1176
|
+
def select_book_in_view(book, view)
|
1177
|
+
@filtered_model.refilter
|
1178
|
+
iter = iter_from_book book
|
1179
|
+
return unless iter
|
1180
|
+
|
1181
|
+
path = iter.path
|
1182
|
+
return unless view.model
|
1183
|
+
|
1184
|
+
path = view_path_to_model_path(view, path)
|
1185
|
+
log.debug { "Path for #{book.ident} is #{path}" }
|
1186
|
+
selection = view.respond_to?(:selection) ? view.selection : view
|
1187
|
+
selection.unselect_all
|
1188
|
+
selection.select_path(path)
|
1189
|
+
end
|
1190
|
+
|
1206
1191
|
def view_path_to_model_path(view, path)
|
1207
1192
|
path = view.model.convert_path_to_child_path(path)
|
1208
1193
|
@filtered_model.convert_path_to_child_path(path) if path
|