alexandria-book-collection-manager 0.7.3 → 0.7.8
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 +4 -1
- data/.rubocop.yml +86 -36
- data/.rubocop_todo.yml +58 -161
- data/.simplecov +5 -2
- data/CHANGELOG.md +56 -2
- data/Gemfile +4 -3
- data/INSTALL.md +23 -11
- data/README.md +52 -41
- data/Rakefile +78 -75
- data/alexandria-book-collection-manager.gemspec +50 -44
- data/bin/alexandria +12 -22
- data/doc/FAQ +1 -2
- data/doc/dependency_decisions.yml +27 -8
- data/lib/alexandria.rb +25 -23
- data/lib/alexandria/about.rb +50 -50
- data/lib/alexandria/book_providers.rb +86 -91
- data/lib/alexandria/book_providers/adlibris.rb +37 -74
- data/lib/alexandria/book_providers/amazon_aws.rb +94 -100
- data/lib/alexandria/book_providers/amazon_ecs_util.rb +289 -324
- data/lib/alexandria/book_providers/barnes_and_noble.rb +42 -42
- data/lib/alexandria/book_providers/douban.rb +25 -41
- data/lib/alexandria/book_providers/proxis.rb +34 -29
- data/lib/alexandria/book_providers/pseudomarc.rb +77 -85
- data/lib/alexandria/book_providers/siciliano.rb +60 -64
- data/lib/alexandria/book_providers/thalia_provider.rb +161 -0
- data/lib/alexandria/book_providers/web.rb +5 -5
- data/lib/alexandria/book_providers/worldcat.rb +66 -95
- data/lib/alexandria/book_providers/z3950.rb +153 -169
- data/lib/alexandria/config.rb +1 -1
- data/lib/alexandria/console.rb +3 -3
- data/lib/alexandria/default_preferences.rb +37 -0
- data/lib/alexandria/execution_queue.rb +13 -12
- data/lib/alexandria/export_format.rb +8 -8
- data/lib/alexandria/export_library.rb +128 -127
- data/lib/alexandria/import_library.rb +102 -126
- data/lib/alexandria/import_library_csv.rb +41 -41
- data/lib/alexandria/library_collection.rb +6 -5
- data/lib/alexandria/library_sort_order.rb +4 -2
- data/lib/alexandria/library_store.rb +39 -28
- data/lib/alexandria/logging.rb +10 -14
- data/lib/alexandria/models/book.rb +5 -4
- data/lib/alexandria/models/library.rb +63 -53
- data/lib/alexandria/net.rb +5 -6
- data/lib/alexandria/preferences.rb +66 -63
- data/lib/alexandria/scanners.rb +2 -2
- data/lib/alexandria/scanners/{cuecat.rb → cue_cat.rb} +17 -17
- data/lib/alexandria/scanners/keyboard.rb +8 -8
- data/lib/alexandria/smart_library.rb +110 -112
- data/lib/alexandria/ui.rb +15 -15
- data/lib/alexandria/ui/{dialogs/about_dialog.rb → about_dialog.rb} +2 -2
- data/lib/alexandria/ui/{dialogs/acquire_dialog.rb → acquire_dialog.rb} +108 -109
- data/lib/alexandria/ui/alert_dialog.rb +66 -0
- data/lib/alexandria/ui/{dialogs/bad_isbns_dialog.rb → bad_isbns_dialog.rb} +13 -9
- 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} +25 -38
- data/lib/alexandria/ui/{dialogs/book_properties_dialog_base.rb → book_properties_dialog_base.rb} +64 -157
- data/lib/alexandria/ui/builder_base.rb +1 -1
- data/lib/alexandria/ui/calendar_popup.rb +58 -0
- data/lib/alexandria/ui/callbacks.rb +187 -155
- data/lib/alexandria/ui/completion_models.rb +8 -22
- 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/{dialogs/export_dialog.rb → export_dialog.rb} +37 -58
- data/lib/alexandria/ui/icons.rb +38 -43
- 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 +30 -41
- data/lib/alexandria/ui/{dialogs/keep_bad_isbn_dialog.rb → keep_bad_isbn_dialog.rb} +9 -6
- data/lib/alexandria/ui/libraries_combo.rb +15 -14
- data/lib/alexandria/ui/listview.rb +69 -67
- 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 +8 -9
- data/lib/alexandria/ui/{dialogs/new_book_dialog.rb → new_book_dialog.rb} +113 -114
- data/lib/alexandria/ui/{dialogs/new_book_dialog_manual.rb → new_book_dialog_manual.rb} +22 -19
- 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/{dialogs/misc_dialogs.rb → really_delete_dialog.rb} +7 -28
- data/lib/alexandria/ui/{sidepane.rb → sidepane_manager.rb} +53 -48
- 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 +216 -200
- data/lib/alexandria/version.rb +4 -19
- data/lib/alexandria/web_themes.rb +21 -21
- data/po/Makefile +2 -2
- data/po/cs.po +992 -875
- data/po/cy.po +961 -874
- data/po/de.po +990 -865
- data/po/el.po +989 -865
- data/po/es.po +985 -861
- data/po/fr.po +987 -867
- data/po/ga.po +908 -820
- data/po/gl.po +980 -860
- data/po/it.po +986 -864
- data/po/ja.po +969 -849
- data/po/mk.po +984 -860
- data/po/nb.po +979 -859
- data/po/nl.po +983 -860
- data/po/pl.po +1018 -971
- data/po/pt.po +988 -857
- data/po/pt_BR.po +983 -863
- data/po/ru.po +994 -871
- data/po/sk.po +989 -867
- data/po/sv.po +976 -856
- data/po/uk.po +972 -858
- data/po/zh_TW.po +974 -854
- data/schemas/alexandria.schemas +24 -2
- data/share/alexandria/glade/acquire_dialog__builder.glade +1 -1
- data/share/alexandria/glade/book_properties_dialog__builder.glade +1 -1
- data/share/alexandria/glade/main_app__builder.glade +6 -21
- data/share/alexandria/glade/new_book_dialog__builder.glade +1 -1
- data/share/alexandria/glade/preferences_dialog__builder.glade +1 -1
- data/share/gnome/help/alexandria/C/introduction.xml +0 -4
- 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 -4
- data/share/gnome/help/alexandria/ja/smart-libraries.xml +1 -1
- data/spec/alexandria/book_providers/thalia_provider_spec.rb +119 -0
- data/spec/alexandria/book_providers/world_cat_provider_spec.rb +160 -0
- data/spec/alexandria/book_providers_spec.rb +62 -156
- data/spec/alexandria/book_spec.rb +12 -10
- data/spec/alexandria/console_spec.rb +6 -11
- data/spec/alexandria/export_library_spec.rb +47 -58
- data/spec/alexandria/library_spec.rb +121 -109
- data/spec/alexandria/library_store_spec.rb +8 -8
- 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 +15 -15
- 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 +59 -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 +51 -0
- data/spec/alexandria/ui/{dialogs/new_book_dialog_spec.rb → new_book_dialog_spec.rb} +4 -4
- 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 +44 -20
- data/spec/end_to_end/basic_run_spec.rb +21 -38
- data/spec/fixtures/cover.jpg +0 -0
- data/spec/spec_helper.rb +54 -10
- data/tasks/setup.rb +2 -2
- data/tasks/spec.rake +11 -11
- data/util/rake/fileinstall.rb +38 -35
- data/util/rake/gettextgenerate.rb +7 -7
- data/util/rake/omfgenerate.rb +7 -7
- metadata +158 -45
- data/dogtail/basic_run_test.py +0 -9
- data/lib/alexandria/book_providers/renaud.rb +0 -155
- data/lib/alexandria/book_providers/thalia.rb +0 -198
- data/lib/alexandria/ui/dialogs/alert_dialog.rb +0 -63
- data/lib/alexandria/ui/dialogs/import_dialog.rb +0 -176
- data/lib/alexandria/ui/dialogs/new_smart_library_dialog.rb +0 -62
- data/lib/alexandria/ui/dialogs/preferences_dialog.rb +0 -563
- data/lib/alexandria/ui/dialogs/smart_library_properties_dialog.rb +0 -61
- data/lib/alexandria/ui/dialogs/smart_library_properties_dialog_base.rb +0 -423
- data/spec/alexandria/scanners/cuecat_spec.rb +0 -67
- data/spec/alexandria/ui/dialogs_spec.rb +0 -162
- 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
|
@@ -4,22 +4,24 @@
|
|
4
4
|
#
|
5
5
|
# See the file README.md for authorship and licensing information.
|
6
6
|
|
7
|
-
require
|
8
|
-
require
|
9
|
-
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"
|
10
11
|
|
11
12
|
module Alexandria
|
12
13
|
module UI
|
13
14
|
class UIManager < BuilderBase
|
14
|
-
attr_accessor :main_app, :actiongroup, :appbar, :prefs, :listview, :iconview,
|
15
|
-
:iconview_model, :filtered_model
|
15
|
+
attr_accessor :main_app, :actiongroup, :appbar, :prefs, :listview, :iconview,
|
16
|
+
:listview_model, :iconview_model, :filtered_model
|
16
17
|
attr_reader :model
|
18
|
+
|
17
19
|
include Logging
|
18
20
|
include GetText
|
19
|
-
GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset:
|
21
|
+
GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
|
20
22
|
|
21
23
|
def initialize(parent)
|
22
|
-
super(
|
24
|
+
super("main_app__builder.glade", widget_names)
|
23
25
|
@parent = parent
|
24
26
|
|
25
27
|
@library_separator_iter = nil
|
@@ -47,7 +49,7 @@ module Alexandria
|
|
47
49
|
log.debug { "UI Manager initialized: #{@iconview.model.inspect}" }
|
48
50
|
@clicking_on_sidepane = true
|
49
51
|
|
50
|
-
@library_listview.signal_connect(
|
52
|
+
@library_listview.signal_connect("cursor-changed") do
|
51
53
|
@clicking_on_sidepane = true
|
52
54
|
end
|
53
55
|
end
|
@@ -63,17 +65,17 @@ module Alexandria
|
|
63
65
|
end
|
64
66
|
|
65
67
|
def create_uimanager
|
66
|
-
log.debug {
|
68
|
+
log.debug { "Adding actiongroup to uimanager" }
|
67
69
|
@uimanager = Gtk::UIManager.new
|
68
70
|
@uimanager.insert_action_group(@actiongroup, 0)
|
69
71
|
end
|
70
72
|
|
71
73
|
def setup_dependents
|
72
|
-
@listview_model = Gtk::TreeModelSort.new(
|
73
|
-
@iconview_model = Gtk::TreeModelSort.new(
|
74
|
+
@listview_model = Gtk::TreeModelSort.new(@filtered_model)
|
75
|
+
@iconview_model = Gtk::TreeModelSort.new(@filtered_model)
|
74
76
|
@listview_manager = ListViewManager.new @listview, self
|
75
77
|
@iconview_manager = IconViewManager.new @iconview, self
|
76
|
-
@sidepane_manager =
|
78
|
+
@sidepane_manager = SidepaneManager.new @library_listview, self
|
77
79
|
@library_listview = @sidepane_manager.library_listview
|
78
80
|
@listview_manager.setup_listview_columns_visibility
|
79
81
|
@listview_manager.setup_listview_columns_width
|
@@ -89,10 +91,10 @@ module Alexandria
|
|
89
91
|
end
|
90
92
|
|
91
93
|
def setup_toolbar
|
92
|
-
log.debug {
|
94
|
+
log.debug { "setup_toolbar" }
|
93
95
|
setup_book_providers
|
94
96
|
add_main_toolbar_items
|
95
|
-
@toolbar = @uimanager.get_widget(
|
97
|
+
@toolbar = @uimanager.get_widget("/MainToolbar")
|
96
98
|
@toolbar.show_arrow = true
|
97
99
|
@toolbar.insert(Gtk::SeparatorToolItem.new, -1)
|
98
100
|
setup_toolbar_combobox
|
@@ -100,18 +102,18 @@ module Alexandria
|
|
100
102
|
@toolbar.insert(Gtk::SeparatorToolItem.new, -1)
|
101
103
|
setup_toolbar_viewas
|
102
104
|
@toolbar.show_all
|
103
|
-
@actiongroup[
|
105
|
+
@actiongroup["Undo"].sensitive = @actiongroup["Redo"].sensitive = false
|
104
106
|
UndoManager.instance.add_observer(self)
|
105
107
|
@vbox1.add(@toolbar, position: 1, expand: false, fill: false)
|
106
108
|
end
|
107
109
|
|
108
110
|
def add_main_toolbar_items
|
109
111
|
mid = @uimanager.new_merge_id
|
110
|
-
@uimanager.add_ui(mid,
|
112
|
+
@uimanager.add_ui(mid, "ui/", "MainToolbar", "MainToolbar",
|
111
113
|
:toolbar, false)
|
112
|
-
@uimanager.add_ui(mid,
|
114
|
+
@uimanager.add_ui(mid, "ui/MainToolbar/", "New", "New",
|
113
115
|
:toolitem, false)
|
114
|
-
@uimanager.add_ui(mid,
|
116
|
+
@uimanager.add_ui(mid, "ui/MainToolbar/", "AddBook", "AddBook",
|
115
117
|
:toolitem, false)
|
116
118
|
# @uimanager.add_ui(mid, "ui/MainToolbar/", "sep", "sep",
|
117
119
|
# :separator, false)
|
@@ -121,11 +123,13 @@ module Alexandria
|
|
121
123
|
|
122
124
|
def setup_toolbar_filter_entry
|
123
125
|
@filter_entry = Gtk::Entry.new
|
124
|
-
@filter_entry.signal_connect(
|
126
|
+
@filter_entry.signal_connect("changed") do |entry|
|
127
|
+
on_toolbar_filter_entry_changed(entry)
|
128
|
+
end
|
125
129
|
@toolitem = Gtk::ToolItem.new
|
126
130
|
@toolitem.expand = true
|
127
131
|
@toolitem.border_width = 5
|
128
|
-
@filter_entry.set_tooltip_text _(
|
132
|
+
@filter_entry.set_tooltip_text _("Type here the search criterion")
|
129
133
|
@toolitem << @filter_entry
|
130
134
|
@toolbar.insert(@toolitem, -1)
|
131
135
|
end
|
@@ -134,20 +138,20 @@ module Alexandria
|
|
134
138
|
cb = Gtk::ComboBoxText.new
|
135
139
|
cb.set_row_separator_func do |model, iter|
|
136
140
|
# TODO: Replace with iter[0] if possible
|
137
|
-
model.get_value(iter, 0) ==
|
141
|
+
model.get_value(iter, 0) == "-"
|
138
142
|
end
|
139
|
-
[_(
|
140
|
-
|
141
|
-
_(
|
142
|
-
_(
|
143
|
-
_(
|
144
|
-
_(
|
145
|
-
_(
|
146
|
-
_(
|
143
|
+
[_("Match everything"),
|
144
|
+
"-",
|
145
|
+
_("Title contains"),
|
146
|
+
_("Authors contain"),
|
147
|
+
_("ISBN contains"),
|
148
|
+
_("Publisher contains"),
|
149
|
+
_("Notes contain"),
|
150
|
+
_("Tags contain")].each do |item|
|
147
151
|
cb.append_text(item)
|
148
152
|
end
|
149
153
|
cb.active = 0
|
150
|
-
cb.signal_connect(
|
154
|
+
cb.signal_connect("changed") { |combo| on_criterion_combobox_changed(combo) }
|
151
155
|
|
152
156
|
# Put the combo box in a event box because it is not currently
|
153
157
|
# possible assign a tooltip to a combo box.
|
@@ -157,16 +161,17 @@ module Alexandria
|
|
157
161
|
@toolitem.border_width = 5
|
158
162
|
@toolitem << eb
|
159
163
|
@toolbar.insert(@toolitem, -1)
|
160
|
-
eb.set_tooltip_text _(
|
164
|
+
eb.set_tooltip_text _("Change the search type")
|
161
165
|
end
|
162
166
|
|
163
167
|
def setup_toolbar_viewas
|
164
168
|
@toolbar_view_as = Gtk::ComboBoxText.new
|
165
|
-
@toolbar_view_as.append_text(_(
|
166
|
-
@toolbar_view_as.append_text(_(
|
169
|
+
@toolbar_view_as.append_text(_("View as Icons"))
|
170
|
+
@toolbar_view_as.append_text(_("View as List"))
|
167
171
|
@toolbar_view_as.active = 0
|
168
|
-
@toolbar_view_as_signal_hid =
|
169
|
-
|
172
|
+
@toolbar_view_as_signal_hid = @toolbar_view_as.signal_connect("changed") do |combo|
|
173
|
+
on_toolbar_view_as_changed(combo)
|
174
|
+
end
|
170
175
|
|
171
176
|
# Put the combo box in a event box because it is not currently
|
172
177
|
# possible assign a tooltip to a combo box.
|
@@ -176,59 +181,64 @@ module Alexandria
|
|
176
181
|
@toolitem.border_width = 5
|
177
182
|
@toolitem << eb
|
178
183
|
@toolbar.insert(@toolitem, -1)
|
179
|
-
eb.set_tooltip_text _(
|
184
|
+
eb.set_tooltip_text _("Choose how to show books")
|
180
185
|
end
|
181
186
|
|
182
187
|
def setup_book_providers
|
183
|
-
log.debug {
|
188
|
+
log.debug { "setup_book_providers" }
|
184
189
|
mid = @uimanager.new_merge_id
|
185
|
-
|
190
|
+
ui_paths = ["ui/MainMenubar/ViewMenu/OnlineInformation/",
|
191
|
+
"ui/BookPopup/OnlineInformation/",
|
192
|
+
"ui/NoBookPopup/OnlineInformation/"]
|
193
|
+
BookProviders.list.each do |provider|
|
186
194
|
name = provider.action_name
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
:menuitem, false)
|
193
|
-
end
|
195
|
+
ui_paths.each do |path|
|
196
|
+
log.debug { "Adding #{name} to #{path}" }
|
197
|
+
@uimanager.add_ui(mid, path, name, name,
|
198
|
+
:menuitem, false)
|
199
|
+
end
|
194
200
|
end
|
195
201
|
end
|
196
202
|
|
197
203
|
def add_menus_and_popups_from_xml
|
198
|
-
log.debug {
|
199
|
-
[
|
204
|
+
log.debug { "add_menus_and_popups_from_xml" }
|
205
|
+
["menus.xml", "popups.xml"].each do |ui_file|
|
200
206
|
@uimanager.add_ui(File.join(Alexandria::Config::DATA_DIR,
|
201
|
-
|
207
|
+
"ui", ui_file))
|
202
208
|
end
|
203
209
|
end
|
204
210
|
|
205
211
|
def setup_accel_group
|
206
|
-
log.debug {
|
212
|
+
log.debug { "setup_accel_group" }
|
207
213
|
@main_app.add_accel_group(@uimanager.accel_group)
|
208
214
|
end
|
209
215
|
|
210
216
|
def setup_menus
|
211
|
-
@menubar = @uimanager.get_widget(
|
217
|
+
@menubar = @uimanager.get_widget("/MainMenubar")
|
212
218
|
@vbox1.add(@menubar, position: 0, expand: false, fill: false)
|
213
219
|
end
|
214
220
|
|
215
221
|
def setup_popups
|
216
|
-
log.debug {
|
217
|
-
@library_popup = @uimanager.get_widget(
|
218
|
-
@smart_library_popup = @uimanager.get_widget(
|
219
|
-
@nolibrary_popup = @uimanager.get_widget(
|
220
|
-
@book_popup = @uimanager.get_widget(
|
221
|
-
@nobook_popup = @uimanager.get_widget(
|
222
|
+
log.debug { "setup_popups" }
|
223
|
+
@library_popup = @uimanager.get_widget("/LibraryPopup")
|
224
|
+
@smart_library_popup = @uimanager.get_widget("/SmartLibraryPopup")
|
225
|
+
@nolibrary_popup = @uimanager.get_widget("/NoLibraryPopup")
|
226
|
+
@book_popup = @uimanager.get_widget("/BookPopup")
|
227
|
+
@nobook_popup = @uimanager.get_widget("/NoBookPopup")
|
222
228
|
end
|
223
229
|
|
224
230
|
def setup_window_events
|
225
|
-
log.debug {
|
226
|
-
@main_app.signal_connect(
|
227
|
-
|
231
|
+
log.debug { "setup_window_events" }
|
232
|
+
@main_app.signal_connect("window-state-event") do |window, event|
|
233
|
+
on_window_state_event(window, event)
|
234
|
+
end
|
235
|
+
@main_app.signal_connect("destroy") do |window|
|
236
|
+
on_window_destroy(window)
|
237
|
+
end
|
228
238
|
end
|
229
239
|
|
230
240
|
def setup_active_model
|
231
|
-
log.debug {
|
241
|
+
log.debug { "setting up active model" }
|
232
242
|
# The active model.
|
233
243
|
|
234
244
|
list = [
|
@@ -263,13 +273,13 @@ module Alexandria
|
|
263
273
|
true
|
264
274
|
else
|
265
275
|
data = case @filter_books_mode
|
266
|
-
when 0
|
267
|
-
(iter[Columns::TITLE] ||
|
268
|
-
(iter[Columns::AUTHORS] ||
|
269
|
-
(iter[Columns::ISBN] ||
|
270
|
-
(iter[Columns::PUBLISHER] ||
|
271
|
-
(iter[Columns::NOTES] ||
|
272
|
-
(iter[Columns::TAGS] ||
|
276
|
+
when 0
|
277
|
+
(iter[Columns::TITLE] || "") +
|
278
|
+
(iter[Columns::AUTHORS] || "") +
|
279
|
+
(iter[Columns::ISBN] || "") +
|
280
|
+
(iter[Columns::PUBLISHER] || "") +
|
281
|
+
(iter[Columns::NOTES] || "") +
|
282
|
+
(iter[Columns::TAGS] || "")
|
273
283
|
when 2 then iter[Columns::TITLE]
|
274
284
|
when 3 then iter[Columns::AUTHORS]
|
275
285
|
when 4 then iter[Columns::ISBN]
|
@@ -283,19 +293,20 @@ module Alexandria
|
|
283
293
|
|
284
294
|
# Give filter entry the initial keyboard focus.
|
285
295
|
@filter_entry.grab_focus
|
286
|
-
log.debug {
|
296
|
+
log.debug { "done setting up active model" }
|
287
297
|
end
|
288
298
|
|
289
299
|
def on_library_button_press_event(widget, event)
|
290
|
-
log.debug {
|
300
|
+
log.debug { "library_button_press_event" }
|
291
301
|
|
292
302
|
# right click
|
293
303
|
if event_is_right_click event
|
294
|
-
log.debug {
|
304
|
+
log.debug { "library right click!" }
|
295
305
|
library_already_selected = true
|
296
306
|
if (path = widget.get_path_at_pos(event.x, event.y))
|
297
307
|
@clicking_on_sidepane = true
|
298
|
-
obj, path =
|
308
|
+
obj, path =
|
309
|
+
widget.is_a?(Gtk::TreeView) ? [widget.selection, path.first] : [widget, path]
|
299
310
|
widget.has_focus = true
|
300
311
|
|
301
312
|
unless obj.path_is_selected?(path)
|
@@ -339,14 +350,15 @@ module Alexandria
|
|
339
350
|
sensitize_library selected_library if library_already_selected
|
340
351
|
|
341
352
|
GLib::Idle.add do
|
342
|
-
menu.
|
353
|
+
menu.popup_at_pointer(event)
|
343
354
|
false
|
344
355
|
end
|
345
356
|
|
346
357
|
# not a right click
|
347
358
|
elsif (path = widget.get_path_at_pos(event.x, event.y))
|
348
359
|
@clicking_on_sidepane = true
|
349
|
-
obj, path =
|
360
|
+
obj, path =
|
361
|
+
widget.is_a?(Gtk::TreeView) ? [widget.selection, path.first] : [widget, path]
|
350
362
|
obj.select_path(path)
|
351
363
|
sensitize_library selected_library
|
352
364
|
end
|
@@ -367,25 +379,26 @@ module Alexandria
|
|
367
379
|
end
|
368
380
|
|
369
381
|
def on_books_button_press_event(widget, event)
|
370
|
-
log.debug {
|
371
|
-
|
372
|
-
widget.grab_focus
|
382
|
+
log.debug { "books_button_press_event" }
|
383
|
+
event_is_right_click event or return
|
373
384
|
|
374
|
-
|
375
|
-
obj, path = widget.is_a?(Gtk::TreeView) ? [widget.selection, path.first] : [widget, path]
|
385
|
+
widget.grab_focus
|
376
386
|
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
387
|
+
if (path = widget.get_path_at_pos(event.x.to_i, event.y.to_i))
|
388
|
+
obj, path =
|
389
|
+
widget.is_a?(Gtk::TreeView) ? [widget.selection, path.first] : [widget, path]
|
390
|
+
|
391
|
+
unless obj.path_is_selected?(path)
|
392
|
+
log.debug { "Select #{path}" }
|
383
393
|
widget.unselect_all
|
394
|
+
obj.select_path(path)
|
384
395
|
end
|
385
|
-
|
386
|
-
|
387
|
-
menu.popup(nil, nil, event.button, event.time)
|
396
|
+
else
|
397
|
+
widget.unselect_all
|
388
398
|
end
|
399
|
+
|
400
|
+
menu = selected_books.empty? ? @nobook_popup : @book_popup
|
401
|
+
menu.popup_at_pointer(event)
|
389
402
|
end
|
390
403
|
|
391
404
|
def get_library_selection_text(library)
|
@@ -405,9 +418,9 @@ module Alexandria
|
|
405
418
|
library.length), library.name, library.length)
|
406
419
|
else
|
407
420
|
format(n_("Library '%s' selected, %d book, " \
|
408
|
-
|
421
|
+
"%d unrated",
|
409
422
|
"Library '%s' selected, %d books, " \
|
410
|
-
|
423
|
+
"%d unrated",
|
411
424
|
library.length), library.name, library.length, n_unrated)
|
412
425
|
end
|
413
426
|
end
|
@@ -420,7 +433,7 @@ module Alexandria
|
|
420
433
|
when 1
|
421
434
|
_("'%s' selected") % books.first.title
|
422
435
|
else
|
423
|
-
n_(
|
436
|
+
n_("%d book selected", "%d books selected",
|
424
437
|
books.length) % books.length
|
425
438
|
end
|
426
439
|
end
|
@@ -440,28 +453,30 @@ module Alexandria
|
|
440
453
|
|
441
454
|
log.debug { "Currently focused widget: #{@main_app.focus.inspect}" }
|
442
455
|
log.debug { "#{@library_listview} : #{@library_popup} : #{@listview}" }
|
443
|
-
log.debug
|
456
|
+
log.debug do
|
444
457
|
"@library_listview: #{@library_listview.has_focus?} " \
|
445
458
|
"or @library_popup:#{@library_popup.has_focus?}"
|
446
|
-
|
447
|
-
log.debug {
|
459
|
+
end
|
460
|
+
log.debug { "@library_listview does *NOT* have focus" }
|
448
461
|
log.debug { "Books are empty: #{books.empty?}" }
|
449
|
-
@actiongroup[
|
450
|
-
@actiongroup[
|
462
|
+
@actiongroup["Properties"].sensitive = \
|
463
|
+
@actiongroup["OnlineInformation"].sensitive = \
|
451
464
|
books.length == 1
|
452
|
-
@actiongroup[
|
465
|
+
@actiongroup["SelectAll"].sensitive = \
|
453
466
|
books.length < library.length
|
454
467
|
|
455
|
-
@actiongroup[
|
456
|
-
@actiongroup[
|
457
|
-
@actiongroup[
|
458
|
-
@actiongroup[
|
468
|
+
@actiongroup["Delete"].sensitive = \
|
469
|
+
@actiongroup["DeselectAll"].sensitive = \
|
470
|
+
@actiongroup["Move"].sensitive =
|
471
|
+
@actiongroup["SetRating"].sensitive = !books.empty?
|
459
472
|
|
460
|
-
log.debug
|
473
|
+
log.debug do
|
474
|
+
"on_books_selection_changed Delete: #{@actiongroup['Delete'].sensitive?}"
|
475
|
+
end
|
461
476
|
|
462
477
|
if library.is_a?(SmartLibrary)
|
463
|
-
@actiongroup[
|
464
|
-
@actiongroup[
|
478
|
+
@actiongroup["Delete"].sensitive =
|
479
|
+
@actiongroup["Move"].sensitive = false
|
465
480
|
end
|
466
481
|
|
467
482
|
# Sensitize providers URL
|
@@ -469,7 +484,7 @@ module Alexandria
|
|
469
484
|
b = books.first
|
470
485
|
# FIXME: Clean up endless negation in this logic
|
471
486
|
no_urls = true
|
472
|
-
BookProviders.each do |provider|
|
487
|
+
BookProviders.list.each do |provider|
|
473
488
|
has_no_url = true
|
474
489
|
begin
|
475
490
|
has_no_url = (b.isbn.nil? || b.isbn.strip.empty? || provider.url(b).nil?)
|
@@ -479,27 +494,27 @@ module Alexandria
|
|
479
494
|
@actiongroup[provider.action_name].sensitive = !has_no_url
|
480
495
|
no_urls = false unless has_no_url
|
481
496
|
end
|
482
|
-
@actiongroup[
|
497
|
+
@actiongroup["OnlineInformation"].sensitive = false if no_urls
|
483
498
|
end
|
484
499
|
end
|
485
500
|
@clicking_on_sidepane = false
|
486
501
|
end
|
487
502
|
|
488
503
|
def on_switch_page(_notebook, _page, page_num)
|
489
|
-
log.debug {
|
490
|
-
@actiongroup[
|
504
|
+
log.debug { "on_switch_page" }
|
505
|
+
@actiongroup["ArrangeIcons"].sensitive = page_num.zero?
|
491
506
|
on_books_selection_changed
|
492
507
|
end
|
493
508
|
|
494
509
|
def on_focus(widget, _event_focus)
|
495
510
|
if @clicking_on_sidepane || (widget == @library_listview)
|
496
|
-
log.debug {
|
511
|
+
log.debug { "on_focus: @library_listview" }
|
497
512
|
GLib::Idle.add do
|
498
513
|
%w(OnlineInformation SelectAll DeselectAll).each do |action|
|
499
514
|
@actiongroup[action].sensitive = false
|
500
515
|
end
|
501
|
-
@actiongroup[
|
502
|
-
@actiongroup[
|
516
|
+
@actiongroup["Properties"].sensitive = selected_library.is_a?(SmartLibrary)
|
517
|
+
@actiongroup["Delete"].sensitive = determine_delete_option
|
503
518
|
false
|
504
519
|
end
|
505
520
|
else
|
@@ -508,52 +523,33 @@ module Alexandria
|
|
508
523
|
end
|
509
524
|
|
510
525
|
def determine_delete_option
|
511
|
-
|
512
|
-
sensitive
|
526
|
+
@libraries.all_regular_libraries.length > 1 || selected_library.is_a?(SmartLibrary)
|
513
527
|
end
|
514
528
|
|
515
529
|
def on_close_sidepane
|
516
|
-
log.debug {
|
517
|
-
@actiongroup[
|
530
|
+
log.debug { "on_close_sidepane" }
|
531
|
+
@actiongroup["Sidepane"].active = false
|
518
532
|
end
|
519
533
|
|
534
|
+
# TODO: Figure out why this frequently selects the wrong book!
|
520
535
|
def select_a_book(book)
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
path = iter.path
|
527
|
-
next unless view.model
|
528
|
-
|
529
|
-
path = view_path_to_model_path(view, path)
|
530
|
-
log.debug { "Path for #{bk.ident} is #{path}" }
|
531
|
-
selection = view.respond_to?(:selection) ? @listview.selection : @iconview
|
532
|
-
selection.unselect_all
|
533
|
-
selection.select_path(path)
|
534
|
-
end
|
535
|
-
begin
|
536
|
-
log.debug { 'select_a_book: listview' }
|
537
|
-
select_this_book.call(book, @listview)
|
538
|
-
log.debug { 'select_a_book: listview' }
|
539
|
-
select_this_book.call(book, @iconview)
|
540
|
-
rescue StandardError => ex
|
541
|
-
trace = ex.backtrace.join("\n> ")
|
542
|
-
log.warn { "Failed to automatically select book: #{ex.message} #{trace}" }
|
543
|
-
end
|
544
|
-
# TODO: Figure out why this frequently selects the wrong book!
|
536
|
+
log.debug { "select_a_book: listview" }
|
537
|
+
select_book_in_view(book, @listview)
|
538
|
+
log.debug { "select_a_book: iconview" }
|
539
|
+
select_book_in_view(book, @iconview)
|
545
540
|
end
|
546
541
|
|
547
542
|
def update(*ary)
|
548
543
|
log.debug { "on_update #{ary}" }
|
549
544
|
caller = ary.first
|
550
|
-
|
551
|
-
|
552
|
-
@actiongroup[
|
553
|
-
|
545
|
+
case caller
|
546
|
+
when UndoManager
|
547
|
+
@actiongroup["Undo"].sensitive = caller.can_undo?
|
548
|
+
@actiongroup["Redo"].sensitive = caller.can_redo?
|
549
|
+
when Library
|
554
550
|
handle_update_caller_library ary unless caller.updating?
|
555
551
|
else
|
556
|
-
raise
|
552
|
+
raise _("unrecognized update event")
|
557
553
|
end
|
558
554
|
end
|
559
555
|
|
@@ -579,18 +575,16 @@ module Alexandria
|
|
579
575
|
end
|
580
576
|
end
|
581
577
|
|
582
|
-
# private
|
583
|
-
|
584
578
|
def open_web_browser(url)
|
585
579
|
if url.nil?
|
586
|
-
log.warn(
|
580
|
+
log.warn("Attempt to open browser with nil url")
|
587
581
|
return
|
588
582
|
end
|
589
583
|
Gtk.show_uri url
|
590
584
|
end
|
591
585
|
|
592
586
|
def detach_old_libraries
|
593
|
-
log.debug {
|
587
|
+
log.debug { "Un-observing old libraries" }
|
594
588
|
@libraries.all_regular_libraries.each do |library|
|
595
589
|
if library.is_a?(Library)
|
596
590
|
library.delete_observer(self)
|
@@ -600,7 +594,7 @@ module Alexandria
|
|
600
594
|
end
|
601
595
|
|
602
596
|
def load_libraries
|
603
|
-
log.info {
|
597
|
+
log.info { _("Loading libraries...") }
|
604
598
|
@completion_models = CompletionModels.instance
|
605
599
|
if @libraries
|
606
600
|
detach_old_libraries
|
@@ -618,17 +612,17 @@ module Alexandria
|
|
618
612
|
|
619
613
|
def handle_ruined_books
|
620
614
|
new_message = _(
|
621
|
-
|
615
|
+
"The data files for the following books are malformed or empty. Do you wish to" \
|
622
616
|
" attempt to download new information for them from the online book providers?\n")
|
623
617
|
|
624
|
-
@libraries.ruined_books.each
|
618
|
+
@libraries.ruined_books.each do |bi|
|
625
619
|
new_message += "\n#{bi[1] || bi[1].inspect}"
|
626
|
-
|
620
|
+
end
|
627
621
|
recovery_dialog = Gtk::MessageDialog.new(@main_app, Gtk::Dialog::MODAL,
|
628
622
|
Gtk::MessageDialog::WARNING,
|
629
623
|
Gtk::MessageDialog::BUTTONS_OK_CANCEL,
|
630
624
|
new_message).show
|
631
|
-
recovery_dialog.signal_connect(
|
625
|
+
recovery_dialog.signal_connect("response") do |_dialog, response_type|
|
632
626
|
recovery_dialog.destroy
|
633
627
|
if response_type == Gtk::ResponseType::OK
|
634
628
|
# progress indicator...
|
@@ -662,11 +656,15 @@ module Alexandria
|
|
662
656
|
end
|
663
657
|
end
|
664
658
|
|
665
|
-
log.debug
|
659
|
+
log.debug do
|
660
|
+
"Trying to add #{book.title}, #{cover_uri}" \
|
661
|
+
" in library ''#{library.name}'"
|
662
|
+
end
|
666
663
|
library.save_cover(book, cover_uri) unless cover_uri.nil?
|
667
664
|
library << book
|
668
665
|
library.save(book)
|
669
|
-
set_status_label(format(_("Added '%s' to library '%s'"),
|
666
|
+
set_status_label(format(_("Added '%s' to library '%s'"),
|
667
|
+
book.title, library.name))
|
670
668
|
rescue StandardError => ex
|
671
669
|
log.error { "Couldn't add book #{isbn}: #{ex}" }
|
672
670
|
log.error { ex.backtrace.join("\n") }
|
@@ -688,7 +686,7 @@ module Alexandria
|
|
688
686
|
## Hide the progress bar.
|
689
687
|
@appbar.children.first.visible = false
|
690
688
|
## Refresh the status bar.
|
691
|
-
set_status_label(
|
689
|
+
set_status_label("")
|
692
690
|
# on_books_selection_changed
|
693
691
|
false
|
694
692
|
end
|
@@ -713,22 +711,23 @@ module Alexandria
|
|
713
711
|
iter[Columns::TITLE] = book.title
|
714
712
|
title = book.title.sub(REDUCE_TITLE_REGEX, '\1...')
|
715
713
|
iter[Columns::TITLE_REDUCED] = title
|
716
|
-
iter[Columns::AUTHORS] = book.authors.join(
|
714
|
+
iter[Columns::AUTHORS] = book.authors.join(", ")
|
717
715
|
iter[Columns::ISBN] = book.isbn.to_s
|
718
716
|
iter[Columns::PUBLISHER] = book.publisher
|
719
717
|
iter[Columns::PUBLISH_DATE] = book.publishing_year.to_s
|
720
718
|
iter[Columns::EDITION] = book.edition
|
721
|
-
iter[Columns::NOTES] = (book.notes ||
|
722
|
-
iter[Columns::LOANED_TO] = (book.loaned_to ||
|
719
|
+
iter[Columns::NOTES] = (book.notes || "")
|
720
|
+
iter[Columns::LOANED_TO] = (book.loaned_to || "")
|
723
721
|
rating = (book.rating || Book::DEFAULT_RATING)
|
724
|
-
|
722
|
+
# ascending order is the default
|
723
|
+
iter[Columns::RATING] = Book::MAX_RATING_STARS - rating
|
725
724
|
iter[Columns::OWN] = book.own?
|
726
725
|
iter[Columns::REDD] = book.redd?
|
727
726
|
iter[Columns::WANT] = book.want?
|
728
727
|
iter[Columns::TAGS] = if book.tags
|
729
|
-
book.tags.join(
|
728
|
+
book.tags.join(",")
|
730
729
|
else
|
731
|
-
|
730
|
+
""
|
732
731
|
end
|
733
732
|
|
734
733
|
icon = Icons.cover(selected_library, book)
|
@@ -740,9 +739,9 @@ module Alexandria
|
|
740
739
|
new_height = [ICON_HEIGHT, icon.height].min
|
741
740
|
icon = cache_scaled_icon(icon, new_width, new_height)
|
742
741
|
end
|
743
|
-
icon =
|
742
|
+
icon = Icons.tag_icon(icon, Icons::FAVORITE_TAG) if rating == Book::MAX_RATING_STARS
|
744
743
|
iter[Columns::COVER_ICON] = icon
|
745
|
-
log.debug {
|
744
|
+
log.debug { "Full iter: " + (0..15).map { |num| iter[num].inspect }.join(", ") }
|
746
745
|
end
|
747
746
|
|
748
747
|
def append_book(book, _tail = nil)
|
@@ -752,7 +751,7 @@ module Alexandria
|
|
752
751
|
if iter
|
753
752
|
fill_iter_with_book(iter, book)
|
754
753
|
else
|
755
|
-
log.debug {
|
754
|
+
log.debug { "@model.append" }
|
756
755
|
iter = @model.append
|
757
756
|
fill_iter_with_book(iter, book)
|
758
757
|
log.debug { "no iter for book #{book}" }
|
@@ -789,13 +788,13 @@ module Alexandria
|
|
789
788
|
@library_listview.set_cursor(iter.path,
|
790
789
|
@library_listview.get_column(0),
|
791
790
|
true)
|
792
|
-
@actiongroup[
|
791
|
+
@actiongroup["Sidepane"].active = true
|
793
792
|
end
|
794
793
|
iter
|
795
794
|
end
|
796
795
|
|
797
796
|
def append_library_separator
|
798
|
-
log.debug {
|
797
|
+
log.debug { "append_library_separator" }
|
799
798
|
iter = @library_listview.model.append
|
800
799
|
iter[0] = nil
|
801
800
|
iter[1] = nil
|
@@ -805,7 +804,7 @@ module Alexandria
|
|
805
804
|
end
|
806
805
|
|
807
806
|
def refresh_books
|
808
|
-
log.debug {
|
807
|
+
log.debug { "refresh_books" }
|
809
808
|
@library_listview.set_sensitive(false)
|
810
809
|
library = selected_library
|
811
810
|
@iconview.freeze
|
@@ -851,7 +850,7 @@ module Alexandria
|
|
851
850
|
end
|
852
851
|
|
853
852
|
def selected_library
|
854
|
-
log.debug {
|
853
|
+
log.debug { "selected_library" }
|
855
854
|
if (iter = @library_listview.selection.selected)
|
856
855
|
target_name = iter[1]
|
857
856
|
@libraries.all_libraries.find { |it| it.name == target_name }
|
@@ -881,7 +880,7 @@ module Alexandria
|
|
881
880
|
def iter_from_ident(ident)
|
882
881
|
log.debug { ident.to_s }
|
883
882
|
iter = @model.iter_first
|
884
|
-
ok = true
|
883
|
+
ok = true if iter
|
885
884
|
while ok
|
886
885
|
return iter if iter[Columns::IDENT] == ident
|
887
886
|
|
@@ -923,11 +922,11 @@ module Alexandria
|
|
923
922
|
end
|
924
923
|
|
925
924
|
def refresh_libraries
|
926
|
-
log.debug {
|
925
|
+
log.debug { "refresh_libraries" }
|
927
926
|
library = selected_library
|
928
927
|
|
929
928
|
# Change the application's title.
|
930
|
-
@main_app.title = library.name +
|
929
|
+
@main_app.title = library.name + " - " + TITLE
|
931
930
|
|
932
931
|
# Disable the selected library in the move libraries actions.
|
933
932
|
@libraries.all_regular_libraries.each do |i_library|
|
@@ -941,11 +940,11 @@ module Alexandria
|
|
941
940
|
smart = library.is_a?(SmartLibrary)
|
942
941
|
log.debug { "sensitize_library: smartlibrary = #{smart}" }
|
943
942
|
GLib::Idle.add do
|
944
|
-
@actiongroup[
|
945
|
-
@actiongroup[
|
946
|
-
@actiongroup[
|
943
|
+
@actiongroup["AddBook"].sensitive = !smart
|
944
|
+
@actiongroup["AddBookManual"].sensitive = !smart
|
945
|
+
@actiongroup["Properties"].sensitive = smart
|
947
946
|
can_delete = smart || (@libraries.all_regular_libraries.length > 1)
|
948
|
-
@actiongroup[
|
947
|
+
@actiongroup["Delete"].sensitive = can_delete
|
949
948
|
log.debug { "sensitize_library delete: #{@actiongroup['Delete'].sensitive?}" }
|
950
949
|
false
|
951
950
|
end
|
@@ -954,14 +953,14 @@ module Alexandria
|
|
954
953
|
def get_view_actiongroup
|
955
954
|
case @prefs.view_as
|
956
955
|
when 0
|
957
|
-
@actiongroup[
|
956
|
+
@actiongroup["AsIcons"]
|
958
957
|
when 1
|
959
|
-
@actiongroup[
|
958
|
+
@actiongroup["AsList"]
|
960
959
|
end
|
961
960
|
end
|
962
961
|
|
963
962
|
def restore_preferences
|
964
|
-
log.debug {
|
963
|
+
log.debug { "Restoring preferences" }
|
965
964
|
if @prefs.maximized
|
966
965
|
@main_app.maximize
|
967
966
|
else
|
@@ -970,9 +969,9 @@ module Alexandria
|
|
970
969
|
@maximized = false
|
971
970
|
end
|
972
971
|
@paned.position = @prefs.sidepane_position
|
973
|
-
@actiongroup[
|
974
|
-
@actiongroup[
|
975
|
-
@actiongroup[
|
972
|
+
@actiongroup["Sidepane"].active = @prefs.sidepane_visible
|
973
|
+
@actiongroup["Toolbar"].active = @prefs.toolbar_visible
|
974
|
+
@actiongroup["Statusbar"].active = @prefs.statusbar_visible
|
976
975
|
@appbar.visible = @prefs.statusbar_visible
|
977
976
|
action = get_view_actiongroup
|
978
977
|
action.activate
|
@@ -996,29 +995,29 @@ module Alexandria
|
|
996
995
|
end
|
997
996
|
|
998
997
|
def save_preferences
|
999
|
-
log.debug {
|
998
|
+
log.debug { "save_preferences" }
|
1000
999
|
@prefs.position = @main_app.position
|
1001
1000
|
@prefs.size = @main_app.allocation.to_a[2..3]
|
1002
1001
|
@prefs.maximized = @maximized
|
1003
1002
|
@prefs.sidepane_position = @paned.position
|
1004
|
-
@prefs.sidepane_visible = @actiongroup[
|
1005
|
-
@prefs.toolbar_visible = @actiongroup[
|
1006
|
-
@prefs.statusbar_visible = @actiongroup[
|
1003
|
+
@prefs.sidepane_visible = @actiongroup["Sidepane"].active?
|
1004
|
+
@prefs.toolbar_visible = @actiongroup["Toolbar"].active?
|
1005
|
+
@prefs.statusbar_visible = @actiongroup["Statusbar"].active?
|
1007
1006
|
@prefs.view_as = @notebook.page
|
1008
1007
|
@prefs.selected_library = selected_library.name
|
1009
1008
|
cols_width = {}
|
1010
1009
|
@listview.columns.each do |c|
|
1011
1010
|
cols_width[c.title] = c.width
|
1012
1011
|
end
|
1013
|
-
@prefs.cols_width =
|
1012
|
+
@prefs.cols_width = "{" + cols_width.to_a.map do |t, v|
|
1014
1013
|
'"' + t + '": ' + v.to_s
|
1015
|
-
end.join(
|
1014
|
+
end.join(", ") + "}"
|
1016
1015
|
log.debug { "cols_width: #{@prefs.cols_width} " }
|
1017
1016
|
@prefs.save!
|
1018
1017
|
end
|
1019
1018
|
|
1020
1019
|
def undoable_move(source, dest, books)
|
1021
|
-
log.debug {
|
1020
|
+
log.debug { "undoable_move" }
|
1022
1021
|
Library.move(source, dest, *books)
|
1023
1022
|
UndoManager.instance.push { undoable_move(dest, source, books) }
|
1024
1023
|
end
|
@@ -1035,7 +1034,7 @@ module Alexandria
|
|
1035
1034
|
|
1036
1035
|
def setup_move_actions
|
1037
1036
|
@actiongroup.actions.each do |action|
|
1038
|
-
next unless
|
1037
|
+
next unless action.name.start_with?("MoveIn")
|
1039
1038
|
|
1040
1039
|
@actiongroup.remove_action(action)
|
1041
1040
|
end
|
@@ -1050,10 +1049,11 @@ module Alexandria
|
|
1050
1049
|
@actiongroup.add_actions(actions)
|
1051
1050
|
@uimanager.remove_ui(@move_mid) if @move_mid
|
1052
1051
|
@move_mid = @uimanager.new_merge_id
|
1052
|
+
ui_paths = ["ui/MainMenubar/EditMenu/Move/",
|
1053
|
+
"ui/BookPopup/Move/"]
|
1053
1054
|
@libraries.all_regular_libraries.each do |library|
|
1054
1055
|
name = library.action_name
|
1055
|
-
|
1056
|
-
'ui/BookPopup/Move/'].each do |path|
|
1056
|
+
ui_paths.each do |path|
|
1057
1057
|
@uimanager.add_ui(@move_mid, path, name, name,
|
1058
1058
|
:menuitem, false)
|
1059
1059
|
end
|
@@ -1072,10 +1072,10 @@ module Alexandria
|
|
1072
1072
|
# Gets the sort order of the current library, for use by export
|
1073
1073
|
def library_sort_order
|
1074
1074
|
# added by Cathal Mc Ginley, 23 Oct 2007
|
1075
|
-
log.debug
|
1075
|
+
log.debug do
|
1076
1076
|
"library_sort_order #{@notebook.page}: " \
|
1077
1077
|
"#{@iconview.model.inspect} #{@listview.model.inspect}"
|
1078
|
-
|
1078
|
+
end
|
1079
1079
|
result, sort_column, sort_order = current_view.model.sort_column_id
|
1080
1080
|
if result
|
1081
1081
|
column_ids_to_attributes = { 2 => :title,
|
@@ -1137,10 +1137,10 @@ module Alexandria
|
|
1137
1137
|
end
|
1138
1138
|
|
1139
1139
|
def remove_library_separator
|
1140
|
-
if
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1140
|
+
return if @library_separator_iter.nil? || @libraries.all_smart_libraries.any?
|
1141
|
+
|
1142
|
+
@library_listview.model.remove(@library_separator_iter)
|
1143
|
+
@library_separator_iter = nil
|
1144
1144
|
end
|
1145
1145
|
|
1146
1146
|
def undoable_undelete(library, books = nil)
|
@@ -1161,13 +1161,14 @@ module Alexandria
|
|
1161
1161
|
|
1162
1162
|
def setup_window_icons
|
1163
1163
|
@main_app.icon = Icons::ALEXANDRIA_SMALL
|
1164
|
-
Gtk::Window.set_default_icon_name(
|
1165
|
-
@main_app.icon_name =
|
1164
|
+
Gtk::Window.set_default_icon_name("alexandria")
|
1165
|
+
@main_app.icon_name = "alexandria"
|
1166
1166
|
end
|
1167
1167
|
|
1168
1168
|
ICONS_SORTS = [
|
1169
1169
|
Columns::TITLE, Columns::AUTHORS, Columns::ISBN,
|
1170
|
-
Columns::PUBLISHER, Columns::EDITION, Columns::RATING,
|
1170
|
+
Columns::PUBLISHER, Columns::EDITION, Columns::RATING,
|
1171
|
+
Columns::REDD, Columns::OWN, Columns::WANT
|
1171
1172
|
].freeze
|
1172
1173
|
|
1173
1174
|
def setup_books_iconview_sorting
|
@@ -1179,6 +1180,21 @@ module Alexandria
|
|
1179
1180
|
|
1180
1181
|
private
|
1181
1182
|
|
1183
|
+
def select_book_in_view(book, view)
|
1184
|
+
@filtered_model.refilter
|
1185
|
+
iter = iter_from_book book
|
1186
|
+
return unless iter
|
1187
|
+
|
1188
|
+
path = iter.path
|
1189
|
+
return unless view.model
|
1190
|
+
|
1191
|
+
path = view_path_to_model_path(view, path)
|
1192
|
+
log.debug { "Path for #{book.ident} is #{path}" }
|
1193
|
+
selection = view.respond_to?(:selection) ? view.selection : view
|
1194
|
+
selection.unselect_all
|
1195
|
+
selection.select_path(path)
|
1196
|
+
end
|
1197
|
+
|
1182
1198
|
def view_path_to_model_path(view, path)
|
1183
1199
|
path = view.model.convert_path_to_child_path(path)
|
1184
1200
|
@filtered_model.convert_path_to_child_path(path) if path
|