alexandria-book-collection-manager 0.7.10 → 0.7.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +5 -1
  3. data/.github/workflows/ruby.yml +33 -19
  4. data/.rubocop.yml +12 -3
  5. data/.rubocop_todo.yml +30 -29
  6. data/CHANGELOG.md +64 -25
  7. data/Gemfile +0 -6
  8. data/Rakefile +2 -2
  9. data/alexandria-book-collection-manager.gemspec +20 -17
  10. data/bin/rake +28 -0
  11. data/bin/rspec +28 -0
  12. data/doc/dependency_decisions.yml +32 -26
  13. data/{bin → exe}/alexandria +1 -3
  14. data/lib/alexandria/about.rb +1 -0
  15. data/lib/alexandria/book_providers/bl_provider.rb +4 -6
  16. data/lib/alexandria/book_providers/{douban.rb → douban_provider.rb} +1 -1
  17. data/lib/alexandria/book_providers/loc_provider.rb +2 -6
  18. data/lib/alexandria/book_providers/sbn_provider.rb +2 -12
  19. data/lib/alexandria/book_providers/thalia_provider.rb +5 -6
  20. data/lib/alexandria/book_providers/{web.rb → website_based_provider.rb} +20 -1
  21. data/lib/alexandria/book_providers/{worldcat.rb → world_cat_provider.rb} +3 -4
  22. data/lib/alexandria/book_providers/z3950_provider.rb +24 -26
  23. data/lib/alexandria/book_providers.rb +14 -10
  24. data/lib/alexandria/config.rb +2 -2
  25. data/lib/alexandria/console.rb +12 -10
  26. data/lib/alexandria/export_format.rb +3 -2
  27. data/lib/alexandria/export_library.rb +31 -36
  28. data/lib/alexandria/import_library.rb +3 -4
  29. data/lib/alexandria/import_library_csv.rb +2 -2
  30. data/lib/alexandria/library_collection.rb +1 -1
  31. data/lib/alexandria/library_store.rb +19 -14
  32. data/lib/alexandria/logging.rb +22 -21
  33. data/lib/alexandria/models/book.rb +1 -2
  34. data/lib/alexandria/models/library.rb +5 -6
  35. data/lib/alexandria/preferences.rb +7 -19
  36. data/lib/alexandria/pseudo_marc_parser.rb +1 -1
  37. data/lib/alexandria/scanners/cue_cat.rb +5 -9
  38. data/lib/alexandria/scanners/{keyboard.rb → keyboard_wedge.rb} +3 -3
  39. data/lib/alexandria/scanners.rb +2 -2
  40. data/lib/alexandria/smart_library.rb +7 -3
  41. data/lib/alexandria/ui/acquire_dialog.rb +42 -45
  42. data/lib/alexandria/ui/alert_dialog.rb +1 -1
  43. data/lib/alexandria/ui/barcode_animation.rb +3 -3
  44. data/lib/alexandria/ui/book_properties_dialog.rb +9 -9
  45. data/lib/alexandria/ui/book_properties_dialog_base.rb +10 -11
  46. data/lib/alexandria/ui/builder_base.rb +1 -1
  47. data/lib/alexandria/ui/callbacks.rb +8 -7
  48. data/lib/alexandria/ui/confirm_erase_dialog.rb +1 -0
  49. data/lib/alexandria/ui/conflict_while_copying_dialog.rb +1 -0
  50. data/lib/alexandria/ui/export_dialog.rb +1 -0
  51. data/lib/alexandria/ui/{iconview.rb → icon_view_manager.rb} +1 -0
  52. data/lib/alexandria/ui/iconview_tooltips.rb +1 -1
  53. data/lib/alexandria/ui/keep_bad_isbn_dialog.rb +1 -0
  54. data/lib/alexandria/ui/libraries_combo.rb +1 -0
  55. data/lib/alexandria/ui/listview.rb +2 -0
  56. data/lib/alexandria/ui/main_app.rb +3 -1
  57. data/lib/alexandria/ui/multi_drag_treeview.rb +0 -2
  58. data/lib/alexandria/ui/new_book_dialog.rb +15 -20
  59. data/lib/alexandria/ui/new_book_dialog_manual.rb +7 -7
  60. data/lib/alexandria/ui/new_provider_dialog.rb +1 -0
  61. data/lib/alexandria/ui/new_smart_library_dialog.rb +2 -1
  62. data/lib/alexandria/ui/preferences_dialog.rb +4 -4
  63. data/lib/alexandria/ui/provider_preferences_dialog.rb +1 -0
  64. data/lib/alexandria/ui/really_delete_dialog.rb +1 -0
  65. data/lib/alexandria/ui/sidepane_manager.rb +49 -48
  66. data/lib/alexandria/ui/skip_entry_dialog.rb +1 -0
  67. data/lib/alexandria/ui/smart_library_properties_dialog.rb +1 -0
  68. data/lib/alexandria/ui/smart_library_properties_dialog_base.rb +2 -1
  69. data/lib/alexandria/ui/{sound.rb → sound_effects_player.rb} +3 -0
  70. data/lib/alexandria/ui/ui_manager.rb +192 -141
  71. data/lib/alexandria/ui.rb +1 -0
  72. data/lib/alexandria/version.rb +1 -1
  73. data/po/Makefile +1 -1
  74. data/po/it.po +64 -82
  75. data/spec/alexandria/book_providers/bl_provider_spec.rb +10 -1
  76. data/spec/alexandria/book_providers/douban_provider_spec.rb +17 -0
  77. data/spec/alexandria/book_providers/loc_provider_spec.rb +8 -0
  78. data/spec/alexandria/book_providers/sbn_provider_spec.rb +9 -1
  79. data/spec/alexandria/book_providers/thalia_provider_spec.rb +8 -0
  80. data/spec/alexandria/book_providers/world_cat_provider_spec.rb +8 -0
  81. data/spec/alexandria/book_providers/z3950_provider_spec.rb +22 -0
  82. data/spec/alexandria/console_spec.rb +1 -1
  83. data/spec/alexandria/export_library_spec.rb +57 -11
  84. data/spec/alexandria/library_collection_spec.rb +24 -0
  85. data/spec/alexandria/library_spec.rb +2 -1
  86. data/spec/alexandria/library_store_spec.rb +32 -0
  87. data/spec/alexandria/scanners/keyboard_wedge_spec.rb +47 -0
  88. data/spec/alexandria/ui/about_dialog_spec.rb +1 -1
  89. data/spec/alexandria/ui/acquire_dialog_spec.rb +8 -3
  90. data/spec/alexandria/ui/alert_dialog_spec.rb +1 -1
  91. data/spec/alexandria/ui/bad_isbns_dialog_spec.rb +1 -1
  92. data/spec/alexandria/ui/book_properties_dialog_spec.rb +1 -1
  93. data/spec/alexandria/ui/confirm_erase_dialog_spec.rb +1 -1
  94. data/spec/alexandria/ui/conflict_while_copying_dialog_spec.rb +1 -1
  95. data/spec/alexandria/ui/error_dialog_spec.rb +1 -1
  96. data/spec/alexandria/ui/export_dialog_spec.rb +3 -3
  97. data/spec/alexandria/ui/{iconview_spec.rb → icon_view_manager_spec.rb} +1 -1
  98. data/spec/alexandria/ui/keep_bad_isbn_dialog_spec.rb +1 -1
  99. data/spec/alexandria/ui/main_app_spec.rb +0 -2
  100. data/spec/alexandria/ui/new_book_dialog_manual_spec.rb +1 -1
  101. data/spec/alexandria/ui/new_book_dialog_spec.rb +6 -3
  102. data/spec/alexandria/ui/preferences_dialog_spec.rb +1 -1
  103. data/spec/alexandria/ui/provider_preferences_dialog_spec.rb +22 -7
  104. data/spec/alexandria/ui/really_delete_dialog_spec.rb +1 -1
  105. data/spec/alexandria/ui/sidepane_manager_spec.rb +1 -1
  106. data/spec/alexandria/ui/skip_entry_dialog_spec.rb +1 -1
  107. data/spec/alexandria/ui/ui_manager_spec.rb +2 -2
  108. data/spec/end_to_end/basic_run_spec.rb +2 -1
  109. data/spec/spec_helper.rb +4 -2
  110. data/tasks/setup.rb +1 -1
  111. data/util/rake/fileinstall.rb +5 -6
  112. data/util/rake/omfgenerate.rb +1 -1
  113. metadata +93 -61
  114. /data/spec/alexandria/ui/{sound_spec.rb → sound_effects_player_spec.rb} +0 -0
@@ -10,6 +10,7 @@ module Alexandria
10
10
  module UI
11
11
  class ReallyDeleteDialog < AlertDialog
12
12
  include GetText
13
+
13
14
  GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
14
15
 
15
16
  def initialize(parent, library, books = nil)
@@ -11,6 +11,7 @@ module Alexandria
11
11
  class SidepaneManager
12
12
  include Logging
13
13
  include GetText
14
+
14
15
  attr_accessor :library_listview
15
16
 
16
17
  def initialize(library_listview, parent)
@@ -50,7 +51,7 @@ module Alexandria
50
51
 
51
52
  if (match = contains_illegal_character(new_text))
52
53
  if match.instance_of? MatchData
53
- chars = match[1].gsub(/&/, "&amp;")
54
+ chars = match[1].gsub("&", "&amp;")
54
55
  ErrorDialog.new(@main_app, _("Invalid library name '%s'") % new_text,
55
56
  _("The name provided contains the " \
56
57
  "disallowed character <b>%s</b>") % chars).display
@@ -128,71 +129,71 @@ module Alexandria
128
129
 
129
130
  @library_listview
130
131
  .signal_connect("drag-motion") do |_widget, drag_context, x, y, time, _data|
131
- log.debug { "drag-motion" }
132
-
133
- path, column, =
134
- @library_listview.get_path_at_pos(x, y)
135
-
136
- if path
137
- # Refuse drags from/to smart libraries.
138
- if @parent.selected_library.is_a?(SmartLibrary)
139
- path = nil
140
- else
141
- iter = @library_listview.model.get_iter(path)
142
- if iter[3] # separator?
132
+ log.debug { "drag-motion" }
133
+
134
+ path, column, =
135
+ @library_listview.get_path_at_pos(x, y)
136
+
137
+ if path
138
+ # Refuse drags from/to smart libraries.
139
+ if @parent.selected_library.is_a?(SmartLibrary)
143
140
  path = nil
144
141
  else
145
- library = @libraries.all_libraries.find do |lib|
146
- lib.name == iter[1]
142
+ iter = @library_listview.model.get_iter(path)
143
+ if iter[3] # separator?
144
+ path = nil
145
+ else
146
+ library = @libraries.all_libraries.find do |lib|
147
+ lib.name == iter[1]
148
+ end
149
+ path = nil if library.is_a?(SmartLibrary)
147
150
  end
148
- path = nil if library.is_a?(SmartLibrary)
149
151
  end
150
152
  end
151
- end
152
153
 
153
- @library_listview.set_drag_dest_row(path, :into_or_after)
154
+ @library_listview.set_drag_dest_row(path, :into_or_after)
154
155
 
155
- Gdk.drag_status(drag_context,
156
- path ? drag_context.suggested_action : 0,
157
- time)
156
+ Gdk.drag_status(drag_context,
157
+ path ? drag_context.suggested_action : 0,
158
+ time)
158
159
  end
159
160
 
160
161
  @library_listview
161
162
  .signal_connect("drag-drop") do |widget, drag_context, _x, _y, time, _data|
162
- log.debug { "drag-drop" }
163
+ log.debug { "drag-drop" }
163
164
 
164
- widget.drag_get_data(drag_context,
165
- drag_context.targets.first,
166
- time)
167
- true
165
+ widget.drag_get_data(drag_context,
166
+ drag_context.targets.first,
167
+ time)
168
+ true
168
169
  end
169
170
 
170
171
  @library_listview
171
172
  .signal_connect("drag-data-received") do |_, drag_context, x, y, data, _, _|
172
- log.debug { "drag-data-received" }
173
-
174
- success = false
175
- # FIXME: Ruby-GNOME2 should make comparison work without needing to
176
- # call #name.
177
- if data.data_type.name == Gdk::Selection::TYPE_STRING.name
178
- success, path =
179
- @library_listview.get_dest_row_at_pos(x, y)
180
-
181
- if success
182
- iter = @library_listview.model.get_iter(path)
183
- library = @libraries.all_libraries.find do |lib|
184
- lib.name == iter[1]
173
+ log.debug { "drag-data-received" }
174
+
175
+ success = false
176
+ # FIXME: Ruby-GNOME2 should make comparison work without needing to
177
+ # call #name.
178
+ if data.data_type.name == Gdk::Selection::TYPE_STRING.name
179
+ success, path =
180
+ @library_listview.get_dest_row_at_pos(x, y)
181
+
182
+ if success
183
+ iter = @library_listview.model.get_iter(path)
184
+ library = @libraries.all_libraries.find do |lib|
185
+ lib.name == iter[1]
186
+ end
187
+ @parent.move_selected_books_to_library(library)
188
+ success = true
185
189
  end
186
- @parent.move_selected_books_to_library(library)
187
- success = true
188
190
  end
189
- end
190
- begin
191
- drag_context.finish(success: success, delete: false)
192
- rescue StandardError => ex
193
- log.error { "drag_context.finish failed: #{ex}" }
194
- raise
195
- end
191
+ begin
192
+ drag_context.finish(success: success, delete: false)
193
+ rescue StandardError => ex
194
+ log.error { "drag_context.finish failed: #{ex}" }
195
+ raise
196
+ end
196
197
  end
197
198
  end
198
199
  end
@@ -11,6 +11,7 @@ module Alexandria
11
11
  class SkipEntryDialog < AlertDialog
12
12
  include GetText
13
13
  include Logging
14
+
14
15
  GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
15
16
 
16
17
  def initialize(parent, message)
@@ -8,6 +8,7 @@ module Alexandria
8
8
  module UI
9
9
  class SmartLibraryPropertiesDialog < SmartLibraryPropertiesDialogBase
10
10
  include GetText
11
+
11
12
  GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
12
13
 
13
14
  def initialize(parent, smart_library)
@@ -13,6 +13,7 @@ module Alexandria
13
13
  include Logging
14
14
  include CalendarPopup
15
15
  include GetText
16
+
16
17
  GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
17
18
 
18
19
  attr_reader :predicate_operator_rule, :dialog
@@ -77,7 +78,7 @@ module Alexandria
77
78
  new_rule.value = nil
78
79
  end
79
80
 
80
- protected
81
+ private
81
82
 
82
83
  attr_reader :smart_library_rules
83
84
 
@@ -28,7 +28,10 @@ module Alexandria
28
28
  module UI
29
29
  ## Uses Ruby/GStreamer to play Ogg/Vorbis sound effects
30
30
  class SoundEffectsPlayer
31
+ include Logging
32
+
31
33
  def initialize
34
+ log.info { "Setting up SoundEffectsPlayer" }
32
35
  @sounds_dir = Alexandria::Config::SOUNDS_DIR
33
36
  @ogg_vorbis_pipeline = Gst::Pipeline.new
34
37
  set_up_pipeline
@@ -11,6 +11,7 @@ require "alexandria/library_sort_order"
11
11
 
12
12
  module Alexandria
13
13
  module UI
14
+ # rubocop:disable Metrics/ClassLength
14
15
  class UIManager < BuilderBase
15
16
  attr_accessor :main_app, :actiongroup, :appbar, :prefs, :listview, :iconview,
16
17
  :listview_model, :iconview_model, :filtered_model
@@ -18,6 +19,7 @@ module Alexandria
18
19
 
19
20
  include Logging
20
21
  include GetText
22
+
21
23
  GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
22
24
 
23
25
  def initialize(parent)
@@ -237,58 +239,36 @@ module Alexandria
237
239
  end
238
240
  end
239
241
 
242
+ LIST_STORE_COLUMNS = [
243
+ GdkPixbuf::Pixbuf, # COVER_LIST
244
+ GdkPixbuf::Pixbuf, # COVER_ICON
245
+ String, # TITLE
246
+ String, # TITLE_REDUCED
247
+ String, # AUTHORS
248
+ String, # ISBN
249
+ String, # PUBLISHER
250
+ String, # PUBLISH_DATE
251
+ String, # EDITION
252
+ Integer, # RATING
253
+ String, # IDENT
254
+ String, # NOTES
255
+ TrueClass, # REDD
256
+ TrueClass, # OWN
257
+ TrueClass, # WANT
258
+ String, # TAGS
259
+ String # LOANED TO
260
+ ].freeze
261
+
240
262
  def setup_active_model
241
263
  log.debug { "setting up active model" }
242
264
  # The active model.
243
265
 
244
- list = [
245
- GdkPixbuf::Pixbuf, # COVER_LIST
246
- GdkPixbuf::Pixbuf, # COVER_ICON
247
- String, # TITLE
248
- String, # TITLE_REDUCED
249
- String, # AUTHORS
250
- String, # ISBN
251
- String, # PUBLISHER
252
- String, # PUBLISH_DATE
253
- String, # EDITION
254
- Integer, # RATING
255
- String, # IDENT
256
- String, # NOTES
257
- TrueClass, # REDD
258
- TrueClass, # OWN
259
- TrueClass, # WANT
260
- String, # TAGS
261
- String # LOANED TO
262
- ]
263
-
264
- @model = Gtk::ListStore.new(*list)
266
+ @model = Gtk::ListStore.new(*LIST_STORE_COLUMNS)
265
267
 
266
268
  # Filter books according to the search toolbar widgets.
267
269
  @filtered_model = Gtk::TreeModelFilter.new(@model)
268
270
  @filtered_model.set_visible_func do |_model, iter|
269
- # log.debug { "visible_func" }
270
- @filter_books_mode ||= 0
271
- filter = @filter_entry.text
272
- if filter.empty?
273
- true
274
- else
275
- data = case @filter_books_mode
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] || "")
283
- when 2 then iter[Columns::TITLE]
284
- when 3 then iter[Columns::AUTHORS]
285
- when 4 then iter[Columns::ISBN]
286
- when 5 then iter[Columns::PUBLISHER]
287
- when 6 then iter[Columns::NOTES]
288
- when 7 then iter[Columns::TAGS]
289
- end
290
- !data.nil? && data.downcase.include?(filter.downcase)
291
- end
271
+ filter_iter(iter, @filter_entry.text)
292
272
  end
293
273
 
294
274
  # Give filter entry the initial keyboard focus.
@@ -296,6 +276,30 @@ module Alexandria
296
276
  log.debug { "done setting up active model" }
297
277
  end
298
278
 
279
+ def filter_iter(iter, filter)
280
+ if filter.empty?
281
+ true
282
+ else
283
+ @filter_books_mode ||= 0
284
+ data = case @filter_books_mode
285
+ when 0
286
+ (iter[Columns::TITLE] || "") +
287
+ (iter[Columns::AUTHORS] || "") +
288
+ (iter[Columns::ISBN] || "") +
289
+ (iter[Columns::PUBLISHER] || "") +
290
+ (iter[Columns::NOTES] || "") +
291
+ (iter[Columns::TAGS] || "")
292
+ when 2 then iter[Columns::TITLE]
293
+ when 3 then iter[Columns::AUTHORS]
294
+ when 4 then iter[Columns::ISBN]
295
+ when 5 then iter[Columns::PUBLISHER]
296
+ when 6 then iter[Columns::NOTES]
297
+ when 7 then iter[Columns::TAGS]
298
+ end
299
+ !data.nil? && data.downcase.include?(filter.downcase)
300
+ end
301
+ end
302
+
299
303
  def on_library_button_press_event(widget, event)
300
304
  log.debug { "library_button_press_event" }
301
305
 
@@ -449,55 +453,56 @@ module Alexandria
449
453
 
450
454
  # Focus is the wrong idiom here.
451
455
  unless @clicking_on_sidepane || (@main_app.focus == @library_listview)
452
- # unless @main_app.focus == @library_listview
456
+ sensizite_action_group_items(books, library)
457
+ sensitize_provider_actions(books)
458
+ end
459
+ @clicking_on_sidepane = false
460
+ end
453
461
 
454
- log.debug { "Currently focused widget: #{@main_app.focus.inspect}" }
455
- log.debug { "#{@library_listview} : #{@library_popup} : #{@listview}" }
456
- log.debug do
457
- "@library_listview: #{@library_listview.has_focus?} " \
458
- "or @library_popup:#{@library_popup.has_focus?}"
459
- end
460
- log.debug { "@library_listview does *NOT* have focus" }
461
- log.debug { "Books are empty: #{books.empty?}" }
462
- @actiongroup["Properties"].sensitive = \
463
- @actiongroup["OnlineInformation"].sensitive = \
464
- books.length == 1
465
- @actiongroup["SelectAll"].sensitive = \
466
- books.length < library.length
467
-
468
- @actiongroup["Delete"].sensitive = \
469
- @actiongroup["DeselectAll"].sensitive = \
470
- @actiongroup["Move"].sensitive =
471
- @actiongroup["SetRating"].sensitive = !books.empty?
472
-
473
- log.debug do
474
- "on_books_selection_changed Delete: #{@actiongroup['Delete'].sensitive?}"
475
- end
462
+ def sensizite_action_group_items(books, library)
463
+ log.debug { "Currently focused widget: #{@main_app.focus.inspect}" }
464
+ log.debug { "#{@library_listview} : #{@library_popup} : #{@listview}" }
465
+ log.debug do
466
+ "@library_listview: #{@library_listview.has_focus?} " \
467
+ "or @library_popup:#{@library_popup.has_focus?}"
468
+ end
469
+ log.debug { "@library_listview does *NOT* have focus" }
470
+ log.debug { "Books are empty: #{books.empty?}" }
476
471
 
472
+ @actiongroup["Properties"].sensitive = @actiongroup["OnlineInformation"].sensitive =
473
+ books.length == 1
474
+ @actiongroup["SelectAll"].sensitive = books.length < library.length
475
+ @actiongroup["DeselectAll"].sensitive = @actiongroup["SetRating"].sensitive =
476
+ !books.empty?
477
+
478
+ can_delete_or_move =
477
479
  if library.is_a?(SmartLibrary)
478
- @actiongroup["Delete"].sensitive =
479
- @actiongroup["Move"].sensitive = false
480
+ false
481
+ else
482
+ !books.empty?
480
483
  end
484
+ @actiongroup["Delete"].sensitive = @actiongroup["Move"].sensitive =
485
+ can_delete_or_move
486
+ end
481
487
 
482
- # Sensitize providers URL
483
- if books.length == 1
484
- b = books.first
485
- # FIXME: Clean up endless negation in this logic
486
- no_urls = true
487
- BookProviders.list.each do |provider|
488
- has_no_url = true
489
- begin
490
- has_no_url = (b.isbn.nil? || b.isbn.strip.empty? || provider.url(b).nil?)
491
- rescue StandardError => ex
492
- log.warn { "Error determining URL from #{provider.name}; #{ex.message}" }
493
- end
494
- @actiongroup[provider.action_name].sensitive = !has_no_url
495
- no_urls = false unless has_no_url
496
- end
497
- @actiongroup["OnlineInformation"].sensitive = false if no_urls
488
+ # Sensitize providers URL
489
+ def sensitize_provider_actions(books)
490
+ return if books.length != 1
491
+
492
+ b = books.first
493
+ # FIXME: Clean up endless negation in this logic
494
+ no_urls = true
495
+ BookProviders.list.each do |provider|
496
+ has_no_url = true
497
+ begin
498
+ has_no_url = b.isbn.nil? || b.isbn.strip.empty? || provider.url(b).nil?
499
+ rescue StandardError => ex
500
+ log.warn { "Error determining URL from #{provider.name}; #{ex.message}" }
498
501
  end
502
+ @actiongroup[provider.action_name].sensitive = !has_no_url
503
+ no_urls = false unless has_no_url
499
504
  end
500
- @clicking_on_sidepane = false
505
+ @actiongroup["OnlineInformation"].sensitive = false if no_urls
501
506
  end
502
507
 
503
508
  def on_switch_page(_notebook, _page, page_num)
@@ -624,7 +629,7 @@ module Alexandria
624
629
  if response_type == Gtk::ResponseType::OK
625
630
  # progress indicator...
626
631
  @progressbar.fraction = 0
627
- @appbar.children.first.visible = true # show the progress bar
632
+ show_progress_bar
628
633
 
629
634
  total_book_count = @libraries.ruined_books.size
630
635
  fraction_per_book = 1.0 / total_book_count
@@ -634,38 +639,8 @@ module Alexandria
634
639
  GLib::Idle.add do
635
640
  ruined_book = @libraries.ruined_books.pop
636
641
  if ruined_book
637
- book, isbn, library = ruined_book
638
- begin
639
- book_rslt = Alexandria::BookProviders.isbn_search(isbn.to_s)
640
- book = book_rslt[0]
641
- cover_uri = book_rslt[1]
642
-
643
- # TODO: if the book was saved okay, make sure the old
644
- # empty yaml file doesn't stick around esp if doing
645
- # isbn-10 --> isbn-13 conversion...
646
- if isbn.size == 10
647
- filename = library.yaml(isbn)
648
- log.debug { "removing old file #{filename}" }
649
- begin
650
- File.delete(filename)
651
- rescue StandardError => ex
652
- log.error { "Could not delete empty file #{filename}" }
653
- end
654
- end
655
-
656
- log.debug do
657
- "Trying to add #{book.title}, #{cover_uri} " \
658
- "in library ''#{library.name}'"
659
- end
660
- library.save_cover(book, cover_uri) unless cover_uri.nil?
661
- library << book
662
- library.save(book)
663
- set_status_label(format(_("Added '%s' to library '%s'"),
664
- book.title, library.name))
665
- rescue StandardError => ex
666
- log.error { "Couldn't add book #{isbn}: #{ex}" }
667
- log.error { ex.backtrace.join("\n") }
668
- end
642
+ _book, isbn, library = ruined_book
643
+ repair_ruined_book(isbn, library)
669
644
 
670
645
  prog_percentage += fraction_per_book
671
646
  @progressbar.fraction = prog_percentage
@@ -680,8 +655,7 @@ module Alexandria
680
655
  # @listview.columns_autosize
681
656
 
682
657
  @progressbar.fraction = 1
683
- ## Hide the progress bar.
684
- @appbar.children.first.visible = false
658
+ hide_progress_bar
685
659
  ## Refresh the status bar.
686
660
  set_status_label("")
687
661
  # on_books_selection_changed
@@ -692,6 +666,75 @@ module Alexandria
692
666
  end
693
667
  end
694
668
 
669
+ def repair_ruined_book(isbn, library)
670
+ book_rslt = Alexandria::BookProviders.isbn_search(isbn.to_s)
671
+ book = book_rslt[0]
672
+ cover_uri = book_rslt[1]
673
+
674
+ # TODO: if the book was saved okay, make sure the old
675
+ # empty yaml file doesn't stick around esp if doing
676
+ # isbn-10 --> isbn-13 conversion...
677
+ if isbn.size == 10
678
+ filename = library.yaml(isbn)
679
+ log.debug { "removing old file #{filename}" }
680
+ begin
681
+ File.delete(filename)
682
+ rescue StandardError => ex
683
+ log.error { "Could not delete empty file #{filename}" }
684
+ end
685
+ end
686
+
687
+ log.debug do
688
+ "Trying to add #{book.title}, #{cover_uri} " \
689
+ "in library ''#{library.name}'"
690
+ end
691
+ library.save_cover(book, cover_uri) unless cover_uri.nil?
692
+ library << book
693
+ library.save(book)
694
+ set_status_label(format(_("Added '%s' to library '%s'"),
695
+ book.title, library.name))
696
+ rescue StandardError => ex
697
+ log.error { "Couldn't add book #{isbn}: #{ex}" }
698
+ log.error { ex.backtrace.join("\n") }
699
+ end
700
+
701
+ def progress_bar
702
+ @progress_bar ||= @appbar.children.first
703
+ end
704
+
705
+ def show_progress_bar
706
+ progress_bar.visible = true
707
+ end
708
+
709
+ def hide_progress_bar
710
+ progress_bar.visible = false
711
+ end
712
+
713
+ def start_progress_bar_pulsing(dialog)
714
+ GLib::Idle.add do
715
+ show_progress_bar
716
+ @progress_pulsing = GLib::Timeout.add(100) do
717
+ if dialog.destroyed?
718
+ @progress_pulsing = nil
719
+ hide_progress_bar
720
+ false
721
+ else
722
+ progress_bar.pulse
723
+ true
724
+ end
725
+ end
726
+ false
727
+ end
728
+ end
729
+
730
+ def stop_progress_bar_pulsing
731
+ GLib::Idle.add do
732
+ hide_progress_bar
733
+ GLib::Source.remove(@progress_pulsing) if @progress_pulsing
734
+ false
735
+ end
736
+ end
737
+
695
738
  def cache_scaled_icon(icon, width, height)
696
739
  log.debug { "cache_scaled_icon #{icon}, #{width}, #{height}" }
697
740
  @cache ||= {}
@@ -703,11 +746,9 @@ module Alexandria
703
746
  REDUCE_TITLE_REGEX = Regexp.new("^(.{#{ICON_TITLE_MAXLEN}}).*$")
704
747
 
705
748
  def fill_iter_with_book(iter, book)
706
- log.debug { "fill iter #{iter} with book #{book}" }
707
749
  iter[Columns::IDENT] = book.ident.to_s
708
750
  iter[Columns::TITLE] = book.title
709
- title = book.title.sub(REDUCE_TITLE_REGEX, '\1...')
710
- iter[Columns::TITLE_REDUCED] = title
751
+ iter[Columns::TITLE_REDUCED] = reduced_title(book.title)
711
752
  iter[Columns::AUTHORS] = book.authors.join(", ")
712
753
  iter[Columns::ISBN] = book.isbn.to_s
713
754
  iter[Columns::PUBLISHER] = book.publisher
@@ -715,30 +756,37 @@ module Alexandria
715
756
  iter[Columns::EDITION] = book.edition
716
757
  iter[Columns::NOTES] = (book.notes || "")
717
758
  iter[Columns::LOANED_TO] = (book.loaned_to || "")
718
- rating = (book.rating || Book::DEFAULT_RATING)
759
+
760
+ rating = book.rating || Book::DEFAULT_RATING
719
761
  # ascending order is the default
720
762
  iter[Columns::RATING] = Book::MAX_RATING_STARS - rating
763
+
721
764
  iter[Columns::OWN] = book.own?
722
765
  iter[Columns::REDD] = book.redd?
723
766
  iter[Columns::WANT] = book.want?
724
- iter[Columns::TAGS] = if book.tags
725
- book.tags.join(",")
726
- else
727
- ""
728
- end
767
+ iter[Columns::TAGS] = book.tags&.join(",") || ""
729
768
 
730
769
  icon = Icons.cover(selected_library, book)
731
- log.debug { "Setting icon #{icon} for book #{book.title}" }
732
- iter[Columns::COVER_LIST] = cache_scaled_icon(icon, 20, 25)
770
+ iter[Columns::COVER_LIST] = cover_list_icon(icon)
771
+ iter[Columns::COVER_ICON] = cover_icon(icon, rating)
772
+ end
773
+
774
+ def reduced_title(title)
775
+ title.sub(REDUCE_TITLE_REGEX, '\1...')
776
+ end
777
+
778
+ def cover_list_icon(icon)
779
+ cache_scaled_icon(icon, 20, 25)
780
+ end
733
781
 
782
+ def cover_icon(icon, rating)
734
783
  if icon.height > ICON_HEIGHT
735
784
  new_width = icon.width / (icon.height / ICON_HEIGHT.to_f)
736
785
  new_height = [ICON_HEIGHT, icon.height].min
737
786
  icon = cache_scaled_icon(icon, new_width, new_height)
738
787
  end
739
788
  icon = Icons.tag_icon(icon, Icons::FAVORITE_TAG) if rating == Book::MAX_RATING_STARS
740
- iter[Columns::COVER_ICON] = icon
741
- log.debug { "Full iter: " + (0..15).map { |num| iter[num].inspect }.join(", ") }
789
+ icon
742
790
  end
743
791
 
744
792
  def append_book(book, _tail = nil)
@@ -807,8 +855,10 @@ module Alexandria
807
855
  @iconview.freeze
808
856
  @listview.freeze
809
857
  @model.clear
858
+
810
859
  @progressbar.fraction = 0
811
- @appbar.children.first.visible = true # show the progress bar
860
+ show_progress_bar
861
+
812
862
  set_status_label(_("Loading '%s'...") % library.name)
813
863
  total = library.length
814
864
  log.debug { "library #{library.name} length #{library.length}" }
@@ -833,9 +883,10 @@ module Alexandria
833
883
  @listview.unfreeze # NEW / bdewey
834
884
  @filtered_model.refilter
835
885
  @listview.columns_autosize
886
+
836
887
  @progressbar.fraction = 1
837
- # Hide the progress bar.
838
- @appbar.children.first.visible = false
888
+ hide_progress_bar
889
+
839
890
  # Refresh the status bar.
840
891
  on_books_selection_changed
841
892
  @library_listview.set_sensitive(true)
@@ -850,7 +901,7 @@ module Alexandria
850
901
  log.debug { "selected_library" }
851
902
  if (iter = @library_listview.selection.selected)
852
903
  target_name = iter[1]
853
- @libraries.all_libraries.find { |it| it.name == target_name }
904
+ @libraries.all_libraries.find { _1.name == target_name }
854
905
  else
855
906
  @libraries.all_libraries.first
856
907
  end
@@ -1033,9 +1084,8 @@ module Alexandria
1033
1084
 
1034
1085
  @actiongroup.remove_action(action)
1035
1086
  end
1036
- actions = []
1037
- @libraries.all_regular_libraries.each do |library|
1038
- actions << [
1087
+ actions = @libraries.all_regular_libraries.map do |library|
1088
+ [
1039
1089
  library.action_name, nil,
1040
1090
  _("In '_%s'") % library.name,
1041
1091
  nil, nil, proc { move_selected_books_to_library(library) }
@@ -1192,5 +1242,6 @@ module Alexandria
1192
1242
  @filtered_model.convert_path_to_child_path(filter_path) if filter_path
1193
1243
  end
1194
1244
  end
1245
+ # rubocop:enable Metrics/ClassLength
1195
1246
  end
1196
1247
  end
data/lib/alexandria/ui.rb CHANGED
@@ -30,6 +30,7 @@ require "alexandria/ui/main_app"
30
30
  module Alexandria
31
31
  module UI
32
32
  include Logging
33
+
33
34
  def self.init_icons
34
35
  log.info { "Initializing Icons" }
35
36
  Icons.init
@@ -5,7 +5,7 @@
5
5
  # See the file README.md for authorship and licensing information.
6
6
 
7
7
  module Alexandria
8
- VERSION = "0.7.10"
8
+ VERSION = "0.7.11"
9
9
  DATA_VERSION = "0.6.3"
10
10
  DISPLAY_VERSION = VERSION
11
11
  end