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.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +9 -0
  3. data/.github/workflows/ruby.yml +77 -0
  4. data/.gitignore +5 -1
  5. data/.hound.yml +2 -0
  6. data/.rubocop.yml +87 -37
  7. data/.rubocop_todo.yml +62 -191
  8. data/.simplecov +5 -2
  9. data/CHANGELOG.md +63 -0
  10. data/Gemfile +4 -3
  11. data/INSTALL.md +26 -14
  12. data/README.md +52 -42
  13. data/Rakefile +93 -109
  14. data/TODO.md +9 -1
  15. data/alexandria-book-collection-manager.gemspec +50 -43
  16. data/bin/alexandria +30 -53
  17. data/doc/FAQ +2 -6
  18. data/doc/dependency_decisions.yml +27 -8
  19. data/lib/alexandria.rb +27 -37
  20. data/lib/alexandria/about.rb +50 -50
  21. data/lib/alexandria/book_providers.rb +90 -97
  22. data/lib/alexandria/book_providers/adlibris.rb +41 -76
  23. data/lib/alexandria/book_providers/amazon_aws.rb +96 -100
  24. data/lib/alexandria/book_providers/amazon_ecs_util.rb +295 -322
  25. data/lib/alexandria/book_providers/barnes_and_noble.rb +48 -45
  26. data/lib/alexandria/book_providers/douban.rb +26 -42
  27. data/lib/alexandria/book_providers/proxis.rb +44 -55
  28. data/lib/alexandria/book_providers/pseudomarc.rb +77 -85
  29. data/lib/alexandria/book_providers/siciliano.rb +64 -65
  30. data/lib/alexandria/book_providers/thalia.rb +42 -41
  31. data/lib/alexandria/book_providers/web.rb +15 -33
  32. data/lib/alexandria/book_providers/worldcat.rb +70 -97
  33. data/lib/alexandria/book_providers/z3950.rb +160 -173
  34. data/lib/alexandria/config.rb +1 -1
  35. data/lib/alexandria/console.rb +8 -21
  36. data/lib/alexandria/default_preferences.rb +37 -0
  37. data/lib/alexandria/execution_queue.rb +15 -13
  38. data/lib/alexandria/export_format.rb +47 -0
  39. data/lib/alexandria/export_library.rb +193 -300
  40. data/lib/alexandria/import_library.rb +108 -141
  41. data/lib/alexandria/import_library_csv.rb +43 -46
  42. data/lib/alexandria/library_collection.rb +79 -0
  43. data/lib/alexandria/library_sort_order.rb +45 -0
  44. data/lib/alexandria/library_store.rb +233 -0
  45. data/lib/alexandria/logging.rb +11 -13
  46. data/lib/alexandria/models/book.rb +13 -20
  47. data/lib/alexandria/models/library.rb +81 -353
  48. data/lib/alexandria/net.rb +5 -6
  49. data/lib/alexandria/preferences.rb +73 -87
  50. data/lib/alexandria/scanners.rb +2 -2
  51. data/lib/alexandria/scanners/{cuecat.rb → cue_cat.rb} +20 -18
  52. data/lib/alexandria/scanners/keyboard.rb +8 -8
  53. data/lib/alexandria/smart_library.rb +133 -170
  54. data/lib/alexandria/ui.rb +15 -15
  55. data/lib/alexandria/ui/about_dialog.rb +49 -0
  56. data/lib/alexandria/ui/{dialogs/acquire_dialog.rb → acquire_dialog.rb} +119 -136
  57. data/lib/alexandria/ui/alert_dialog.rb +64 -0
  58. data/lib/alexandria/ui/bad_isbns_dialog.rb +41 -0
  59. data/lib/alexandria/ui/{dialogs/barcode_animation.rb → barcode_animation.rb} +16 -15
  60. data/lib/alexandria/ui/{dialogs/book_properties_dialog.rb → book_properties_dialog.rb} +39 -52
  61. data/lib/alexandria/ui/book_properties_dialog_base.rb +318 -0
  62. data/lib/alexandria/ui/builder_base.rb +7 -27
  63. data/lib/alexandria/ui/calendar_popup.rb +58 -0
  64. data/lib/alexandria/ui/callbacks.rb +189 -183
  65. data/lib/alexandria/ui/completion_models.rb +10 -23
  66. data/lib/alexandria/ui/confirm_erase_dialog.rb +33 -0
  67. data/lib/alexandria/ui/conflict_while_copying_dialog.rb +34 -0
  68. data/lib/alexandria/ui/dndable.rb +7 -7
  69. data/lib/alexandria/ui/error_dialog.rb +25 -0
  70. data/lib/alexandria/ui/export_dialog.rb +142 -0
  71. data/lib/alexandria/ui/icons.rb +47 -63
  72. data/lib/alexandria/ui/iconview.rb +12 -10
  73. data/lib/alexandria/ui/iconview_tooltips.rb +41 -54
  74. data/lib/alexandria/ui/import_dialog.rb +157 -0
  75. data/lib/alexandria/ui/init.rb +21 -33
  76. data/lib/alexandria/ui/keep_bad_isbn_dialog.rb +36 -0
  77. data/lib/alexandria/ui/libraries_combo.rb +16 -14
  78. data/lib/alexandria/ui/listview.rb +73 -87
  79. data/lib/alexandria/ui/main_app.rb +24 -26
  80. data/lib/alexandria/ui/misc_dialogs.rb +10 -0
  81. data/lib/alexandria/ui/multi_drag_treeview.rb +28 -41
  82. data/lib/alexandria/ui/{dialogs/new_book_dialog.rb → new_book_dialog.rb} +156 -194
  83. data/lib/alexandria/ui/new_book_dialog_manual.rb +139 -0
  84. data/lib/alexandria/ui/new_provider_dialog.rb +100 -0
  85. data/lib/alexandria/ui/new_smart_library_dialog.rb +74 -0
  86. data/lib/alexandria/ui/preferences_dialog.rb +313 -0
  87. data/lib/alexandria/ui/provider_preferences_base_dialog.rb +95 -0
  88. data/lib/alexandria/ui/provider_preferences_dialog.rb +35 -0
  89. data/lib/alexandria/ui/really_delete_dialog.rb +53 -0
  90. data/lib/alexandria/ui/{sidepane.rb → sidepane_manager.rb} +56 -68
  91. data/lib/alexandria/ui/skip_entry_dialog.rb +33 -0
  92. data/lib/alexandria/ui/smart_library_properties_dialog.rb +60 -0
  93. data/lib/alexandria/ui/smart_library_properties_dialog_base.rb +242 -0
  94. data/lib/alexandria/ui/smart_library_rule_box.rb +119 -0
  95. data/lib/alexandria/ui/sound.rb +11 -13
  96. data/lib/alexandria/ui/ui_manager.rb +236 -251
  97. data/lib/alexandria/undo_manager.rb +1 -0
  98. data/lib/alexandria/version.rb +4 -19
  99. data/lib/alexandria/web_themes.rb +22 -21
  100. data/po/Makefile +2 -2
  101. data/po/cs.po +993 -880
  102. data/po/cy.po +957 -874
  103. data/po/de.po +990 -869
  104. data/po/el.po +989 -869
  105. data/po/es.po +985 -865
  106. data/po/fr.po +986 -870
  107. data/po/ga.po +907 -823
  108. data/po/gl.po +981 -865
  109. data/po/it.po +986 -868
  110. data/po/ja.po +969 -853
  111. data/po/mk.po +983 -863
  112. data/po/nb.po +979 -863
  113. data/po/nl.po +983 -864
  114. data/po/pl.po +1017 -974
  115. data/po/pt.po +988 -861
  116. data/po/pt_BR.po +984 -868
  117. data/po/ru.po +992 -873
  118. data/po/sk.po +987 -869
  119. data/po/sv.po +977 -861
  120. data/po/uk.po +975 -865
  121. data/po/zh_TW.po +976 -860
  122. data/schemas/alexandria.schemas +25 -3
  123. data/share/alexandria/glade/acquire_dialog__builder.glade +15 -12
  124. data/share/alexandria/glade/book_properties_dialog__builder.glade +171 -299
  125. data/share/alexandria/glade/main_app__builder.glade +24 -33
  126. data/share/alexandria/glade/new_book_dialog__builder.glade +27 -59
  127. data/share/alexandria/glade/preferences_dialog__builder.glade +250 -290
  128. data/share/gnome/help/alexandria/C/introduction.xml +0 -8
  129. data/share/gnome/help/alexandria/C/searching.xml +1 -1
  130. data/share/gnome/help/alexandria/C/smart-libraries.xml +2 -2
  131. data/share/gnome/help/alexandria/C/working-with-libraries.xml +1 -1
  132. data/share/gnome/help/alexandria/fr/alexandria.xml +1 -1
  133. data/share/gnome/help/alexandria/ja/introduction.xml +0 -8
  134. data/share/gnome/help/alexandria/ja/smart-libraries.xml +1 -1
  135. data/spec/alexandria/book_providers/world_cat_provider_spec.rb +160 -0
  136. data/spec/alexandria/book_providers_spec.rb +75 -171
  137. data/spec/alexandria/book_spec.rb +12 -10
  138. data/spec/alexandria/console_spec.rb +27 -0
  139. data/spec/alexandria/export_library_spec.rb +130 -0
  140. data/spec/alexandria/library_spec.rb +128 -172
  141. data/spec/alexandria/library_store_spec.rb +37 -0
  142. data/spec/alexandria/preferences_spec.rb +44 -17
  143. data/spec/alexandria/scanners/cue_cat_spec.rb +52 -0
  144. data/spec/alexandria/smart_library_spec.rb +30 -25
  145. data/spec/alexandria/ui/about_dialog_spec.rb +14 -0
  146. data/spec/alexandria/ui/acquire_dialog_spec.rb +14 -0
  147. data/spec/alexandria/ui/alert_dialog_spec.rb +16 -0
  148. data/spec/alexandria/ui/bad_isbns_dialog_spec.rb +14 -0
  149. data/spec/alexandria/ui/book_properties_dialog_spec.rb +17 -0
  150. data/spec/alexandria/ui/confirm_erase_dialog_spec.rb +14 -0
  151. data/spec/alexandria/ui/conflict_while_copying_dialog_spec.rb +16 -0
  152. data/spec/alexandria/ui/error_dialog_spec.rb +14 -0
  153. data/spec/alexandria/ui/export_dialog_spec.rb +36 -0
  154. data/spec/alexandria/ui/icons_spec.rb +26 -0
  155. data/spec/alexandria/ui/iconview_spec.rb +7 -21
  156. data/spec/alexandria/ui/import_dialog_spec.rb +46 -0
  157. data/spec/alexandria/ui/keep_bad_isbn_dialog_spec.rb +17 -0
  158. data/spec/alexandria/ui/main_app_spec.rb +7 -34
  159. data/spec/alexandria/ui/new_book_dialog_manual_spec.rb +15 -0
  160. data/spec/alexandria/ui/new_book_dialog_spec.rb +22 -0
  161. data/spec/alexandria/ui/new_provider_dialog_spec.rb +30 -0
  162. data/spec/alexandria/ui/new_smart_library_dialog_spec.rb +39 -0
  163. data/spec/alexandria/ui/preferences_dialog_spec.rb +14 -0
  164. data/spec/alexandria/ui/provider_preferences_dialog_spec.rb +34 -0
  165. data/spec/alexandria/ui/really_delete_dialog_spec.rb +16 -0
  166. data/spec/alexandria/ui/sidepane_manager_spec.rb +15 -0
  167. data/spec/alexandria/ui/skip_entry_dialog_spec.rb +14 -0
  168. data/spec/alexandria/ui/smart_library_properties_dialog_spec.rb +49 -0
  169. data/spec/alexandria/ui/sound_spec.rb +2 -2
  170. data/spec/alexandria/ui/ui_manager_spec.rb +43 -20
  171. data/spec/end_to_end/basic_run_spec.rb +52 -0
  172. data/spec/spec_helper.rb +65 -33
  173. data/tasks/setup.rb +2 -2
  174. data/tasks/spec.rake +16 -3
  175. data/util/rake/fileinstall.rb +39 -35
  176. data/util/rake/gettextgenerate.rb +7 -7
  177. data/util/rake/omfgenerate.rb +7 -7
  178. metadata +178 -45
  179. data/dogtail/basic_run_test.py +0 -9
  180. data/lib/alexandria/book_providers/deastore.rb +0 -265
  181. data/lib/alexandria/book_providers/mcu.rb +0 -182
  182. data/lib/alexandria/book_providers/renaud.rb +0 -149
  183. data/lib/alexandria/ui/dialogs/about_dialog.rb +0 -61
  184. data/lib/alexandria/ui/dialogs/alert_dialog.rb +0 -72
  185. data/lib/alexandria/ui/dialogs/bad_isbns_dialog.rb +0 -51
  186. data/lib/alexandria/ui/dialogs/book_properties_dialog_base.rb +0 -426
  187. data/lib/alexandria/ui/dialogs/export_dialog.rb +0 -171
  188. data/lib/alexandria/ui/dialogs/import_dialog.rb +0 -196
  189. data/lib/alexandria/ui/dialogs/misc_dialogs.rb +0 -87
  190. data/lib/alexandria/ui/dialogs/new_book_dialog_manual.rb +0 -154
  191. data/lib/alexandria/ui/dialogs/new_smart_library_dialog.rb +0 -74
  192. data/lib/alexandria/ui/dialogs/preferences_dialog.rb +0 -568
  193. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog.rb +0 -59
  194. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog_base.rb +0 -420
  195. data/spec/alexandria/scanners/cuecat_spec.rb +0 -67
  196. data/spec/alexandria/ui/dialogs_spec.rb +0 -96
  197. data/spec/alexandria/ui/sidepane_spec.rb +0 -29
  198. data/spec/alexandria/ui/ui_utilities_spec.rb +0 -62
  199. data/spec/alexandria/utilities_spec.rb +0 -52
  200. 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
@@ -22,7 +22,7 @@
22
22
  # Boston, MA 02110-1301 USA.
23
23
  #++
24
24
 
25
- require 'gst'
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
- puts "Already playing #{effect}." if $DEBUG
42
+ log.debug { "Already playing #{effect}." }
43
43
  else
44
- puts "Not playing. Starting #{effect}." if $DEBUG
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('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')
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('pad-added') do |_parser, ogg_src_pad|
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
- if $DEBUG
80
- puts 'ERROR loop.quit'
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
- # Copyright (C) 2004-2006 Laurent Sansonetti
4
- # Copyright (C) 2008 Joseph Method
5
- # Copyright (C) 2011, 2016 Matijs van Zuijlen
3
+ # This file is part of Alexandria.
6
4
  #
7
- # Alexandria is free software; you can redistribute it and/or
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 'alexandria/ui/columns'
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, :listview_model,
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: 'UTF-8')
21
+ GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
33
22
 
34
23
  def initialize(parent)
35
- super('main_app__builder.glade', widget_names)
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('cursor-changed') do
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 { 'Adding actiongroup to uimanager' }
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(model: @filtered_model)
86
- @iconview_model = Gtk::TreeModelSort.new(model: @filtered_model)
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 = SidePaneManager.new @library_listview, self
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 { 'setup_toolbar' }
94
+ log.debug { "setup_toolbar" }
107
95
  setup_book_providers
108
96
  add_main_toolbar_items
109
- @toolbar = @uimanager.get_widget('/MainToolbar')
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['Undo'].sensitive = @actiongroup['Redo'].sensitive = false
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, 'ui/', 'MainToolbar', 'MainToolbar',
112
+ @uimanager.add_ui(mid, "ui/", "MainToolbar", "MainToolbar",
125
113
  :toolbar, false)
126
- @uimanager.add_ui(mid, 'ui/MainToolbar/', 'New', 'New',
114
+ @uimanager.add_ui(mid, "ui/MainToolbar/", "New", "New",
127
115
  :toolitem, false)
128
- @uimanager.add_ui(mid, 'ui/MainToolbar/', 'AddBook', 'AddBook',
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('changed', &method(:on_toolbar_filter_entry_changed))
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 _('Type here the search criterion')
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
- [_('Match everything'),
154
- '-',
155
- _('Title contains'),
156
- _('Authors contain'),
157
- _('ISBN contains'),
158
- _('Publisher contains'),
159
- _('Notes contain'),
160
- _('Tags contain')].each do |item|
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('changed', &method(:on_criterion_combobox_changed))
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 _('Change the search type')
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(_('View as Icons'))
180
- @toolbar_view_as.append_text(_('View as List'))
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('changed', &method(:on_toolbar_view_as_changed))
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 _('Choose how to show books')
181
+ eb.set_tooltip_text _("Choose how to show books")
194
182
  end
195
183
 
196
184
  def setup_book_providers
197
- log.debug { 'setup_book_providers' }
185
+ log.debug { "setup_book_providers" }
198
186
  mid = @uimanager.new_merge_id
199
- BookProviders.each do |provider|
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
- ['ui/MainMenubar/ViewMenu/OnlineInformation/',
202
- 'ui/BookPopup/OnlineInformation/',
203
- 'ui/NoBookPopup/OnlineInformation/'].each do |path|
204
- log.debug { "Adding #{name} to #{path}" }
205
- @uimanager.add_ui(mid, path, name, name,
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 { 'add_menus_and_popups_from_xml' }
213
- ['menus.xml', 'popups.xml'].each do |ui_file|
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
- 'ui', ui_file))
204
+ "ui", ui_file))
216
205
  end
217
206
  end
218
207
 
219
208
  def setup_accel_group
220
- log.debug { 'setup_accel_group' }
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('/MainMenubar')
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 { 'setup_popups' }
231
- @library_popup = @uimanager.get_widget('/LibraryPopup')
232
- @smart_library_popup = @uimanager.get_widget('/SmartLibraryPopup')
233
- @nolibrary_popup = @uimanager.get_widget('/NoLibraryPopup')
234
- @book_popup = @uimanager.get_widget('/BookPopup')
235
- @nobook_popup = @uimanager.get_widget('/NoBookPopup')
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 { 'setup_window_events' }
240
- @main_app.signal_connect('window-state-event', &method(:on_window_state_event))
241
- @main_app.signal_connect('destroy', &method(:on_window_destroy))
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 { 'setting up active model' }
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 then
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 { 'done setting up active model' }
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 { 'library_button_press_event' }
293
+ log.debug { "library_button_press_event" }
305
294
 
306
295
  # right click
307
296
  if event_is_right_click event
308
- log.debug { 'library right click!' }
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 = widget.is_a?(Gtk::TreeView) ? [widget.selection, path.first] : [widget, 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
- if library_already_selected
355
- sensitize_library selected_library
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
- if (path = widget.get_path_at_pos(event.x, event.y))
374
- @clicking_on_sidepane = true
375
- obj, path = widget.is_a?(Gtk::TreeView) ? [widget.selection, path.first] : [widget, path]
376
- obj.select_path(path)
377
- sensitize_library selected_library
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 { 'books_button_press_event' }
400
- if event_is_right_click event
401
- widget.grab_focus
375
+ log.debug { "books_button_press_event" }
376
+ event_is_right_click event or return
402
377
 
403
- if (path = widget.get_path_at_pos(event.x.to_i, event.y.to_i))
404
- obj, path = widget.is_a?(Gtk::TreeView) ? [widget.selection, path.first] : [widget, path]
378
+ widget.grab_focus
405
379
 
406
- unless obj.path_is_selected?(path)
407
- log.debug { "Select #{path}" }
408
- widget.unselect_all
409
- obj.select_path(path)
410
- end
411
- else
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
- menu = selected_books.empty? ? @nobook_popup : @book_popup
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
- '%d unrated',
414
+ "%d unrated",
438
415
  "Library '%s' selected, %d books, " \
439
- '%d unrated',
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_('%d book selected', '%d books selected',
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 { '@library_listview does *NOT* have focus' }
452
+ end
453
+ log.debug { "@library_listview does *NOT* have focus" }
477
454
  log.debug { "Books are empty: #{books.empty?}" }
478
- @actiongroup['Properties'].sensitive = \
479
- @actiongroup['OnlineInformation'].sensitive = \
455
+ @actiongroup["Properties"].sensitive = \
456
+ @actiongroup["OnlineInformation"].sensitive = \
480
457
  books.length == 1
481
- @actiongroup['SelectAll'].sensitive = \
458
+ @actiongroup["SelectAll"].sensitive = \
482
459
  books.length < library.length
483
460
 
484
- @actiongroup['Delete'].sensitive = \
485
- @actiongroup['DeselectAll'].sensitive = \
486
- @actiongroup['Move'].sensitive =
487
- @actiongroup['SetRating'].sensitive = !books.empty?
461
+ @actiongroup["Delete"].sensitive = \
462
+ @actiongroup["DeselectAll"].sensitive = \
463
+ @actiongroup["Move"].sensitive =
464
+ @actiongroup["SetRating"].sensitive = !books.empty?
488
465
 
489
- log.debug { "on_books_selection_changed Delete: #{@actiongroup['Delete'].sensitive?}" }
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['Delete'].sensitive =
493
- @actiongroup['Move'].sensitive = false
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['OnlineInformation'].sensitive = false if no_urls
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 { 'on_switch_page' }
519
- @actiongroup['ArrangeIcons'].sensitive = page_num.zero?
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 { 'on_focus: @library_listview' }
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['Properties'].sensitive = selected_library.is_a?(SmartLibrary)
531
- @actiongroup['Delete'].sensitive = determine_delete_option
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
- sensitive = (@libraries.all_regular_libraries.length > 1 || selected_library.is_a?(SmartLibrary))
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 { 'on_close_sidepane' }
546
- @actiongroup['Sidepane'].active = false
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
- select_this_book = proc do |bk, view|
551
- @filtered_model.refilter
552
- iter = iter_from_book bk
553
- next unless iter
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
- if caller.is_a?(UndoManager)
578
- @actiongroup['Undo'].sensitive = caller.can_undo?
579
- @actiongroup['Redo'].sensitive = caller.can_redo?
580
- elsif caller.is_a?(Library)
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 'unrecognized update event'
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('Attempt to open browser with nil url')
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 { 'Un-observing old libraries' }
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 { 'Loading Libraries...' }
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 = Libraries.instance
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
- 'The data files for the following books are malformed or empty. Do you wish to' \
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 { |bi|
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('response') do |_dialog, response_type|
618
+ recovery_dialog.signal_connect("response") do |_dialog, response_type|
659
619
  recovery_dialog.destroy
660
- if response_type == :ok
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 { "Trying to add #{book.title}, #{cover_uri} in library ''#{library.name}'" }
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'"), book.title, library.name))
697
- rescue => ex
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
- iter[Columns::RATING] = Book::MAX_RATING_STARS - rating # ascending order is the default
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 = icon.tag(Icons::FAVORITE_TAG) if rating == Book::MAX_RATING_STARS
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 { 'Full iter: ' + (0..15).map { |num| iter[num].inspect }.join(', ') }
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 { '@model.append' }
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['Sidepane'].active = true
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 { 'append_library_separator' }
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 { 'refresh_books' }
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 # NEW / bdewey
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 { 'selected_library' }
846
+ log.debug { "selected_library" }
882
847
  if (iter = @library_listview.selection.selected)
883
- @libraries.all_libraries.find { |x| x.name == iter[1] }
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 { 'refresh_libraries' }
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 + ' - ' + TITLE
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['AddBook'].sensitive = !smart
970
- @actiongroup['AddBookManual'].sensitive = !smart
971
- @actiongroup['Properties'].sensitive = smart
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['Delete'].sensitive = can_delete
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['AsIcons']
949
+ @actiongroup["AsIcons"]
983
950
  when 1
984
- @actiongroup['AsList']
951
+ @actiongroup["AsList"]
985
952
  end
986
953
  end
987
954
 
988
955
  def restore_preferences
989
- log.debug { 'Restoring preferences...' }
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['Sidepane'].active = @prefs.sidepane_visible
999
- @actiongroup['Toolbar'].active = @prefs.toolbar_visible
1000
- @actiongroup['Statusbar'].active = @prefs.statusbar_visible
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 { 'save_preferences' }
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['Sidepane'].active?
1030
- @prefs.toolbar_visible = @actiongroup['Toolbar'].active?
1031
- @prefs.statusbar_visible = @actiongroup['Statusbar'].active?
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 = '{' + cols_width.to_a.map do |t, v|
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 { 'undoable_move' }
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 /^MoveIn/ =~ action.name
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
- ['ui/MainMenubar/EditMenu/Move/',
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 => :title,
1106
- 4 => :authors,
1107
- 5 => :isbn,
1108
- 6 => :publisher,
1109
- 7 => :publishing_year,
1110
- 8 => :edition, # binding
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 => :rating }
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 !@library_separator_iter.nil? && @libraries.all_smart_libraries.empty?
1165
- @library_listview.model.remove(@library_separator_iter)
1166
- @library_separator_iter = nil
1167
- end
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('alexandria')
1189
- @main_app.icon_name = 'alexandria'
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, Columns::REDD, Columns::OWN, Columns::WANT
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