alexandria-book-collection-manager 0.7.1 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (210) hide show
  1. checksums.yaml +5 -5
  2. data/.github/dependabot.yml +9 -0
  3. data/.gitignore +5 -2
  4. data/.hound.yml +2 -0
  5. data/.rubocop.yml +113 -45
  6. data/.rubocop_todo.yml +82 -170
  7. data/.simplecov +5 -1
  8. data/.travis.yml +45 -0
  9. data/.yardopts +1 -1
  10. data/CHANGELOG.md +60 -0
  11. data/ChangeLog.0 +33 -35
  12. data/Gemfile +6 -5
  13. data/INSTALL.md +164 -0
  14. data/README.md +52 -42
  15. data/Rakefile +95 -109
  16. data/TODO.md +9 -1
  17. data/alexandria-book-collection-manager.gemspec +52 -45
  18. data/bin/alexandria +31 -53
  19. data/doc/AUTHORS +61 -0
  20. data/doc/BUGS +31 -0
  21. data/doc/FAQ +365 -0
  22. data/doc/HACKING +19 -0
  23. data/doc/NEWS +341 -0
  24. data/doc/alexandria.1 +120 -0
  25. data/doc/cuecat_support.rdoc +67 -0
  26. data/doc/dependency_decisions.yml +80 -0
  27. data/lib/alexandria.rb +29 -37
  28. data/lib/alexandria/about.rb +52 -51
  29. data/lib/alexandria/book_providers.rb +94 -101
  30. data/lib/alexandria/book_providers/adlibris.rb +45 -85
  31. data/lib/alexandria/book_providers/amazon_aws.rb +105 -113
  32. data/lib/alexandria/book_providers/amazon_ecs_util.rb +293 -324
  33. data/lib/alexandria/book_providers/barnes_and_noble.rb +54 -53
  34. data/lib/alexandria/book_providers/douban.rb +29 -51
  35. data/lib/alexandria/book_providers/proxis.rb +42 -59
  36. data/lib/alexandria/book_providers/pseudomarc.rb +79 -99
  37. data/lib/alexandria/book_providers/siciliano.rb +68 -70
  38. data/lib/alexandria/book_providers/thalia.rb +46 -45
  39. data/lib/alexandria/book_providers/web.rb +17 -33
  40. data/lib/alexandria/book_providers/worldcat.rb +74 -102
  41. data/lib/alexandria/book_providers/z3950.rb +170 -174
  42. data/lib/alexandria/config.rb +5 -3
  43. data/lib/alexandria/console.rb +10 -21
  44. data/lib/alexandria/default_preferences.rb +37 -0
  45. data/lib/alexandria/execution_queue.rb +17 -15
  46. data/lib/alexandria/export_format.rb +47 -0
  47. data/lib/alexandria/export_library.rb +188 -302
  48. data/lib/alexandria/import_library.rb +114 -155
  49. data/lib/alexandria/import_library_csv.rb +46 -96
  50. data/lib/alexandria/library_collection.rb +79 -0
  51. data/lib/alexandria/library_sort_order.rb +45 -0
  52. data/lib/alexandria/library_store.rb +233 -0
  53. data/lib/alexandria/logging.rb +15 -19
  54. data/lib/alexandria/models/book.rb +15 -20
  55. data/lib/alexandria/models/library.rb +81 -363
  56. data/lib/alexandria/net.rb +7 -6
  57. data/lib/alexandria/preferences.rb +73 -91
  58. data/lib/alexandria/scanners.rb +4 -2
  59. data/lib/alexandria/scanners/{cuecat.rb → cue_cat.rb} +24 -20
  60. data/lib/alexandria/scanners/keyboard.rb +10 -8
  61. data/lib/alexandria/smart_library.rb +135 -171
  62. data/lib/alexandria/ui.rb +17 -15
  63. data/lib/alexandria/ui/about_dialog.rb +49 -0
  64. data/lib/alexandria/ui/{dialogs/acquire_dialog.rb → acquire_dialog.rb} +129 -152
  65. data/lib/alexandria/ui/alert_dialog.rb +64 -0
  66. data/lib/alexandria/ui/bad_isbns_dialog.rb +41 -0
  67. data/lib/alexandria/ui/{dialogs/barcode_animation.rb → barcode_animation.rb} +18 -15
  68. data/lib/alexandria/ui/{dialogs/book_properties_dialog.rb → book_properties_dialog.rb} +44 -61
  69. data/lib/alexandria/ui/{dialogs/book_properties_dialog_base.rb → book_properties_dialog_base.rb} +84 -89
  70. data/lib/alexandria/ui/builder_base.rb +9 -27
  71. data/lib/alexandria/ui/callbacks.rb +188 -186
  72. data/lib/alexandria/ui/columns.rb +2 -0
  73. data/lib/alexandria/ui/completion_models.rb +12 -23
  74. data/lib/alexandria/ui/confirm_erase_dialog.rb +33 -0
  75. data/lib/alexandria/ui/conflict_while_copying_dialog.rb +34 -0
  76. data/lib/alexandria/ui/dndable.rb +10 -8
  77. data/lib/alexandria/ui/error_dialog.rb +25 -0
  78. data/lib/alexandria/ui/export_dialog.rb +139 -0
  79. data/lib/alexandria/ui/icons.rb +49 -65
  80. data/lib/alexandria/ui/iconview.rb +15 -13
  81. data/lib/alexandria/ui/iconview_tooltips.rb +43 -58
  82. data/lib/alexandria/ui/import_dialog.rb +157 -0
  83. data/lib/alexandria/ui/init.rb +23 -33
  84. data/lib/alexandria/ui/keep_bad_isbn_dialog.rb +36 -0
  85. data/lib/alexandria/ui/libraries_combo.rb +18 -14
  86. data/lib/alexandria/ui/listview.rb +77 -88
  87. data/lib/alexandria/ui/main_app.rb +26 -26
  88. data/lib/alexandria/ui/misc_dialogs.rb +10 -0
  89. data/lib/alexandria/ui/multi_drag_treeview.rb +30 -41
  90. data/lib/alexandria/ui/{dialogs/new_book_dialog.rb → new_book_dialog.rb} +168 -215
  91. data/lib/alexandria/ui/new_book_dialog_manual.rb +139 -0
  92. data/lib/alexandria/ui/new_provider_dialog.rb +100 -0
  93. data/lib/alexandria/ui/new_smart_library_dialog.rb +74 -0
  94. data/lib/alexandria/ui/preferences_dialog.rb +313 -0
  95. data/lib/alexandria/ui/provider_preferences_base_dialog.rb +95 -0
  96. data/lib/alexandria/ui/provider_preferences_dialog.rb +35 -0
  97. data/lib/alexandria/ui/really_delete_dialog.rb +53 -0
  98. data/lib/alexandria/ui/{sidepane.rb → sidepane_manager.rb} +62 -72
  99. data/lib/alexandria/ui/skip_entry_dialog.rb +33 -0
  100. data/lib/alexandria/ui/smart_library_properties_dialog.rb +60 -0
  101. data/lib/alexandria/ui/{dialogs/smart_library_properties_dialog_base.rb → smart_library_properties_dialog_base.rb} +96 -172
  102. data/lib/alexandria/ui/smart_library_rule_box.rb +119 -0
  103. data/lib/alexandria/ui/sound.rb +13 -13
  104. data/lib/alexandria/ui/ui_manager.rb +262 -283
  105. data/lib/alexandria/undo_manager.rb +3 -0
  106. data/lib/alexandria/version.rb +6 -19
  107. data/lib/alexandria/web_themes.rb +24 -21
  108. data/po/Makefile +2 -2
  109. data/po/cs.po +993 -880
  110. data/po/cy.po +957 -874
  111. data/po/de.po +990 -869
  112. data/po/el.po +989 -869
  113. data/po/es.po +985 -865
  114. data/po/fr.po +986 -870
  115. data/po/ga.po +907 -823
  116. data/po/gl.po +981 -865
  117. data/po/it.po +986 -868
  118. data/po/ja.po +969 -853
  119. data/po/mk.po +983 -863
  120. data/po/nb.po +979 -863
  121. data/po/nl.po +983 -864
  122. data/po/pl.po +1020 -969
  123. data/po/pt.po +988 -861
  124. data/po/pt_BR.po +984 -868
  125. data/po/ru.po +992 -873
  126. data/po/sk.po +987 -869
  127. data/po/sv.po +977 -861
  128. data/po/uk.po +975 -865
  129. data/po/zh_TW.po +976 -860
  130. data/schemas/alexandria.schemas +25 -3
  131. data/share/alexandria/glade/acquire_dialog__builder.glade +15 -12
  132. data/share/alexandria/glade/book_properties_dialog__builder.glade +171 -299
  133. data/share/alexandria/glade/main_app__builder.glade +24 -33
  134. data/share/alexandria/glade/new_book_dialog__builder.glade +27 -59
  135. data/share/alexandria/glade/preferences_dialog__builder.glade +250 -290
  136. data/share/gnome/help/alexandria/C/introduction.xml +0 -8
  137. data/share/gnome/help/alexandria/C/searching.xml +1 -1
  138. data/share/gnome/help/alexandria/C/smart-libraries.xml +2 -2
  139. data/share/gnome/help/alexandria/C/working-with-libraries.xml +1 -1
  140. data/share/gnome/help/alexandria/fr/alexandria.xml +1 -1
  141. data/share/gnome/help/alexandria/ja/introduction.xml +0 -8
  142. data/share/gnome/help/alexandria/ja/smart-libraries.xml +1 -1
  143. data/spec/alexandria/book_providers/world_cat_provider_spec.rb +160 -0
  144. data/spec/alexandria/book_providers_spec.rb +77 -210
  145. data/spec/alexandria/book_spec.rb +16 -12
  146. data/spec/alexandria/console_spec.rb +27 -0
  147. data/spec/alexandria/export_library_spec.rb +130 -0
  148. data/spec/alexandria/library_spec.rb +130 -172
  149. data/spec/alexandria/library_store_spec.rb +37 -0
  150. data/spec/alexandria/preferences_spec.rb +46 -17
  151. data/spec/alexandria/scanners/cue_cat_spec.rb +52 -0
  152. data/spec/alexandria/smart_library_spec.rb +32 -25
  153. data/spec/alexandria/ui/about_dialog_spec.rb +14 -0
  154. data/spec/alexandria/ui/acquire_dialog_spec.rb +14 -0
  155. data/spec/alexandria/ui/alert_dialog_spec.rb +16 -0
  156. data/spec/alexandria/ui/bad_isbns_dialog_spec.rb +14 -0
  157. data/spec/alexandria/ui/book_properties_dialog_spec.rb +17 -0
  158. data/spec/alexandria/ui/confirm_erase_dialog_spec.rb +14 -0
  159. data/spec/alexandria/ui/conflict_while_copying_dialog_spec.rb +16 -0
  160. data/spec/alexandria/ui/error_dialog_spec.rb +14 -0
  161. data/spec/alexandria/ui/export_dialog_spec.rb +15 -0
  162. data/spec/alexandria/ui/icons_spec.rb +26 -0
  163. data/spec/alexandria/ui/iconview_spec.rb +9 -21
  164. data/spec/alexandria/ui/import_dialog_spec.rb +41 -0
  165. data/spec/alexandria/ui/keep_bad_isbn_dialog_spec.rb +17 -0
  166. data/spec/alexandria/ui/main_app_spec.rb +8 -33
  167. data/spec/alexandria/ui/new_book_dialog_manual_spec.rb +15 -0
  168. data/spec/alexandria/ui/new_book_dialog_spec.rb +22 -0
  169. data/spec/alexandria/ui/new_provider_dialog_spec.rb +30 -0
  170. data/spec/alexandria/ui/new_smart_library_dialog_spec.rb +39 -0
  171. data/spec/alexandria/ui/preferences_dialog_spec.rb +14 -0
  172. data/spec/alexandria/ui/provider_preferences_dialog_spec.rb +34 -0
  173. data/spec/alexandria/ui/really_delete_dialog_spec.rb +16 -0
  174. data/spec/alexandria/ui/sidepane_manager_spec.rb +15 -0
  175. data/spec/alexandria/ui/skip_entry_dialog_spec.rb +14 -0
  176. data/spec/alexandria/ui/smart_library_properties_dialog_spec.rb +32 -0
  177. data/spec/alexandria/ui/sound_spec.rb +4 -2
  178. data/spec/alexandria/ui/ui_manager_spec.rb +45 -20
  179. data/spec/end_to_end/basic_run_spec.rb +57 -0
  180. data/spec/spec_helper.rb +66 -33
  181. data/tasks/setup.rb +5 -3
  182. data/tasks/spec.rake +18 -3
  183. data/util/rake/fileinstall.rb +38 -40
  184. data/util/rake/gettextgenerate.rb +15 -70
  185. data/util/rake/omfgenerate.rb +10 -10
  186. metadata +176 -60
  187. data/INSTALL.rdoc +0 -148
  188. data/dogtail/basic_run_test.py +0 -9
  189. data/lib/alexandria/book_providers/bol_it.rb +0 -160
  190. data/lib/alexandria/book_providers/deastore.rb +0 -273
  191. data/lib/alexandria/book_providers/ibs_it.rb +0 -147
  192. data/lib/alexandria/book_providers/mcu.rb +0 -169
  193. data/lib/alexandria/book_providers/renaud.rb +0 -140
  194. data/lib/alexandria/book_providers/webster_it.rb +0 -167
  195. data/lib/alexandria/ui/dialogs/about_dialog.rb +0 -59
  196. data/lib/alexandria/ui/dialogs/alert_dialog.rb +0 -70
  197. data/lib/alexandria/ui/dialogs/bad_isbns_dialog.rb +0 -43
  198. data/lib/alexandria/ui/dialogs/export_dialog.rb +0 -171
  199. data/lib/alexandria/ui/dialogs/import_dialog.rb +0 -196
  200. data/lib/alexandria/ui/dialogs/misc_dialogs.rb +0 -85
  201. data/lib/alexandria/ui/dialogs/new_book_dialog_manual.rb +0 -154
  202. data/lib/alexandria/ui/dialogs/new_smart_library_dialog.rb +0 -74
  203. data/lib/alexandria/ui/dialogs/preferences_dialog.rb +0 -578
  204. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog.rb +0 -57
  205. data/spec/alexandria/scanners/cuecat_spec.rb +0 -65
  206. data/spec/alexandria/ui/dialogs_spec.rb +0 -94
  207. data/spec/alexandria/ui/sidepane_spec.rb +0 -27
  208. data/spec/alexandria/ui/ui_utilities_spec.rb +0 -60
  209. data/spec/alexandria/utilities_spec.rb +0 -50
  210. data/tasks/dogtail.rake +0 -4
@@ -0,0 +1,64 @@
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 AlertDialog
10
+ def initialize(parent, title, stock_icon, buttons, message = nil)
11
+ @dialog = Gtk::Dialog.new(title: "", parent: parent, flags: :destroy_with_parent,
12
+ buttons: buttons)
13
+ @dialog.border_width = 6
14
+ @dialog.resizable = false
15
+ @dialog.child.spacing = 12
16
+
17
+ hbox = Gtk::Box.new(:horizontal, 12)
18
+ hbox.homogeneous = false
19
+ hbox.border_width = 6
20
+
21
+ image = Gtk::Image.new(stock: stock_icon,
22
+ size: Gtk::IconSize::DIALOG)
23
+ image.set_alignment(0.5, 0)
24
+ hbox.pack_start(image)
25
+
26
+ vbox = Gtk::Box.new(:vertical, 6)
27
+ vbox.homogeneous = false
28
+ vbox.pack_start make_label("<b><big>#{title}</big></b>")
29
+ vbox.pack_start make_label(message.strip) unless message
30
+ hbox.pack_start(vbox)
31
+
32
+ @dialog.child.pack_start(hbox)
33
+ end
34
+
35
+ def show_all
36
+ dialog.show_all
37
+ end
38
+
39
+ def run
40
+ dialog.run
41
+ end
42
+
43
+ def destroy
44
+ dialog.destroy
45
+ end
46
+
47
+ def default_response=(response)
48
+ dialog.default_response = response
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :dialog
54
+
55
+ def make_label(markup)
56
+ label = Gtk::Label.new
57
+ label.set_alignment(0, 0)
58
+ label.wrap = label.selectable = true
59
+ label.markup = markup
60
+ label
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,41 @@
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
+ # Generalized Dialog for lists of bad isbns. Used for on_import. Can also
10
+ # be used for on_load library conversions.
11
+ class BadIsbnsDialog
12
+ def initialize(parent, message, list)
13
+ @dialog = Gtk::MessageDialog.new(parent: parent,
14
+ flags: :modal,
15
+ type: :warning,
16
+ buttons: :close,
17
+ message: message)
18
+ the_vbox = @dialog.children.first
19
+
20
+ isbn_container = Gtk::Box.new :horizontal
21
+ the_vbox.pack_start(isbn_container)
22
+ the_vbox.reorder_child(isbn_container, 3)
23
+ scrolley = Gtk::ScrolledWindow.new
24
+ isbn_container.pack_start(scrolley)
25
+ textview = Gtk::TextView.new(Gtk::TextBuffer.new)
26
+ textview.editable = false
27
+ textview.cursor_visible = false
28
+ scrolley.add(textview)
29
+ list.each do |li|
30
+ textview.buffer.insert_at_cursor("#{li}\n")
31
+ end
32
+
33
+ @dialog.signal_connect("response") { @dialog.destroy }
34
+ end
35
+
36
+ def show
37
+ @dialog.show_all
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # -*- ruby -*-
2
4
  #--
3
5
  # Copyright (C) 2011 Cathal Mc Ginley
@@ -21,7 +23,7 @@
21
23
  # Boston, MA 02110-1301 USA.
22
24
  #++
23
25
 
24
- require 'gobject-introspection'
26
+ require "gobject-introspection"
25
27
 
26
28
  module GooCanvas
27
29
  class << self
@@ -40,7 +42,7 @@ module GooCanvas
40
42
  remove_method(:const_missing)
41
43
  end
42
44
  loader = GObjectIntrospection::Loader.new(self)
43
- loader.load('GooCanvas')
45
+ loader.load("GooCanvas")
44
46
  end
45
47
  end
46
48
  end
@@ -89,16 +91,16 @@ module Alexandria
89
91
  end
90
92
 
91
93
  def set_active
92
- @canvas.set_property(:background_color, 'white')
93
- @barcode_bars.each { |rect| rect.set_property(:fill_color, 'white') }
94
+ @canvas.set_property(:background_color, "white")
95
+ @barcode_bars.each { |rect| rect.set_property(:fill_color, "white") }
94
96
  end
95
97
 
96
98
  def set_passive
97
- if @canvas
98
- passive_bg = '#F4F4F4'
99
- @canvas.set_property(:background_color, passive_bg)
100
- @barcode_bars.each { |rect| rect.set_property(:fill_color, passive_bg) }
101
- end
99
+ @canvas or return
100
+
101
+ passive_bg = "#F4F4F4"
102
+ @canvas.set_property(:background_color, passive_bg)
103
+ @barcode_bars.each { |rect| rect.set_property(:fill_color, passive_bg) }
102
104
  end
103
105
 
104
106
  def manual_input
@@ -114,7 +116,7 @@ module Alexandria
114
116
  private
115
117
 
116
118
  def create_ean_barcode_data
117
- d = '211113123121112331122131113211111123122211132321112311231111'
119
+ d = "211113123121112331122131113211111123122211132321112311231111"
118
120
  # ####911113... but that's too much padding on the left...
119
121
  until d.empty?
120
122
  space_width = d[0].chr.to_i
@@ -127,11 +129,12 @@ module Alexandria
127
129
  def draw_barcode_bars
128
130
  @barcode_data.each do |space_width, bar_width|
129
131
  @hpos += space_width
130
- rect_item = GooCanvas::CanvasRect.new(parent: @root,
131
- x: @bar_left_edge + @scale * @hpos, y: @bar_top,
132
- width: @scale * bar_width, height: @bar_height,
133
- line_width: 0,
134
- fill_color: 'white')
132
+ rect_item =
133
+ GooCanvas::CanvasRect.new(parent: @root,
134
+ x: @bar_left_edge + @scale * @hpos, y: @bar_top,
135
+ width: @scale * bar_width, height: @bar_height,
136
+ line_width: 0,
137
+ fill_color: "white")
135
138
  @hpos += bar_width
136
139
  @barcode_bars << rect_item
137
140
  end
@@ -1,20 +1,10 @@
1
- # Copyright (C) 2004-2006 Laurent Sansonetti
2
- # Copyright (C) 2016 Matijs van Zuijlen
3
- #
4
- # Alexandria is free software; you can redistribute it and/or
5
- # modify it under the terms of the GNU General Public License as
6
- # published by the Free Software Foundation; either version 2 of the
7
- # License, or (at your option) any later version.
8
- #
9
- # Alexandria is distributed in the hope that it will be useful,
10
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
- # General Public License for more details.
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Alexandria.
13
4
  #
14
- # You should have received a copy of the GNU General Public
15
- # License along with Alexandria; see the file COPYING. If not,
16
- # write to the Free Software Foundation, Inc., 51 Franklin Street,
17
- # Fifth Floor, Boston, MA 02110-1301 USA.
5
+ # See the file README.md for authorship and licensing information.
6
+
7
+ require "alexandria/ui/error_dialog"
18
8
 
19
9
  module Alexandria
20
10
  module UI
@@ -22,51 +12,51 @@ module Alexandria
22
12
  include Logging
23
13
  include GetText
24
14
  extend GetText
25
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
15
+ GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
26
16
 
27
17
  def initialize(parent, library, book)
28
18
  super(parent, library.cover(book))
29
- puts 'Initializing Book Properties Dialog...' if $DEBUG
19
+ log.debug { "Initializing Book Properties Dialog" }
30
20
 
31
21
  cancel_button = Gtk::Button.new(stock_id: Gtk::Stock::CANCEL)
32
- cancel_button.signal_connect('clicked') { on_cancel }
22
+ cancel_button.signal_connect("clicked") { on_cancel }
33
23
  cancel_button.show
34
24
  @button_box << cancel_button
35
25
 
36
26
  close_button = Gtk::Button.new(stock_id: Gtk::Stock::SAVE)
37
- close_button.signal_connect('clicked') { on_close }
27
+ close_button.signal_connect("clicked") { on_close }
38
28
  close_button.show
39
29
  @button_box << close_button
40
30
 
41
31
  help_button = Gtk::Button.new(stock_id: Gtk::Stock::HELP)
42
- help_button.signal_connect('clicked') { on_help }
32
+ help_button.signal_connect("clicked") { on_help }
43
33
  help_button.show
44
34
  @button_box << help_button
45
35
  @button_box.set_child_secondary(help_button, true)
46
36
 
47
37
  @entry_title.text = @book_properties_dialog.title = book.title
48
- @entry_isbn.text = (book.isbn || '')
38
+ @entry_isbn.text = (book.isbn || "")
49
39
  @entry_publisher.text = book.publisher
50
40
  @entry_publish_date.text = book.publishing_year.to_s
51
- @entry_publish_date.signal_connect('focus-out-event') do
41
+ @entry_publish_date.signal_connect("focus-out-event") do
52
42
  text = @entry_publish_date.text
53
43
  if text.empty?
54
44
  false
55
45
  else
56
46
  year = text.to_i
57
47
  if year.zero? || year > (Time.now.year + 10) || year < 10
58
- @entry_publish_date.text = ''
48
+ @entry_publish_date.text = ""
59
49
  @entry_publish_date.grab_focus
60
50
  true
61
51
  elsif year < 100
62
- @entry_publish_date.text = '19' + year.to_s
52
+ @entry_publish_date.text = "19" + year.to_s
63
53
  false
64
54
  end
65
55
  end
66
56
  end
67
57
  @entry_edition.text = book.edition
68
58
  if book.tags
69
- @entry_tags.text = book.tags.join(',') # tags are comma-separated
59
+ @entry_tags.text = book.tags.join(",") # tags are comma-separated
70
60
  end
71
61
 
72
62
  book.authors.each do |author|
@@ -76,7 +66,7 @@ module Alexandria
76
66
  end
77
67
 
78
68
  buffer = Gtk::TextBuffer.new
79
- buffer.text = (book.notes || '')
69
+ buffer.text = (book.notes || "")
80
70
  @textview_notes.buffer = buffer
81
71
 
82
72
  @library = library
@@ -85,7 +75,7 @@ module Alexandria
85
75
  self.rating = (book.rating || Book::DEFAULT_RATING)
86
76
 
87
77
  if (@checkbutton_loaned.active = book.loaned?)
88
- @entry_loaned_to.text = (book.loaned_to || '')
78
+ @entry_loaned_to.text = (book.loaned_to || "")
89
79
  self.loaned_since = book.loaned_since
90
80
  @date_loaned_since.sensitive = true
91
81
  else
@@ -96,7 +86,7 @@ module Alexandria
96
86
  if (@checkbutton_redd.active = book.redd?)
97
87
  @redd_date.sensitive = true
98
88
  if book.redd_when.nil?
99
- puts 'no redd_when'
89
+ log.debug { "no redd_when" }
100
90
  else
101
91
  @redd_date.text = format_date(book.redd_when)
102
92
  end
@@ -106,36 +96,36 @@ module Alexandria
106
96
  end
107
97
  @checkbutton_want.active = book.want?
108
98
 
109
- if (@checkbutton_own.active = book.own?)
110
- @checkbutton_want.inconsistent = true
111
- end
99
+ @checkbutton_want.inconsistent = true if (@checkbutton_own.active = book.own?)
112
100
  end
113
101
 
114
102
  private
115
103
 
116
104
  def on_close
117
- if @entry_isbn.text == ''
105
+ if @entry_isbn.text == ""
118
106
  # If set to nil .to_yaml in library.save causes crash
119
- @book.isbn = ''
107
+ @book.isbn = ""
120
108
  else
121
- ary = @library.select { |book| book.ident == @entry_isbn.text }
109
+ isbn = Library.canonicalise_ean(@entry_isbn.text)
110
+ unless isbn
111
+ ErrorDialog.new(@book_properties_dialog,
112
+ _("Couldn't modify the book"),
113
+ _("Couldn't validate the EAN/ISBN you " \
114
+ "provided. Make sure it is written " \
115
+ "correcty, and try again.")).display
116
+ return
117
+ end
118
+
119
+ ary = @library.select { |book| book.ident == isbn }
122
120
  unless ary.empty? || ((ary.length == 1) && (ary.first == @book))
123
- ErrorDialog.new(@parent,
121
+ ErrorDialog.new(@book_properties_dialog,
124
122
  _("Couldn't modify the book"),
125
- _('The EAN/ISBN you provided is already ' \
126
- 'used in this library.'))
123
+ _("The EAN/ISBN you provided is already " \
124
+ "used in this library.")).display
127
125
  return
128
126
  end
129
- @book.isbn = begin
130
- Library.canonicalise_ean(@entry_isbn.text)
131
- rescue Alexandria::Library::InvalidISBNError
132
- ErrorDialog.new(@parent,
133
- _("Couldn't modify the book"),
134
- _("Couldn't validate the EAN/ISBN you " \
135
- 'provided. Make sure it is written ' \
136
- 'correcty, and try again.'))
137
- return
138
- end
127
+
128
+ @book.isbn = isbn
139
129
  end
140
130
  @book.title = @entry_title.text
141
131
  @book.publisher = @entry_publisher.text
@@ -153,11 +143,8 @@ module Alexandria
153
143
  if loaned_since.strip.empty?
154
144
  @book.loaned_since = nil
155
145
  else
156
- begin
157
- t = parse_date(loaned_since)
158
- @book.loaned_since = t
159
- rescue
160
- end
146
+ t = parse_date(loaned_since)
147
+ @book.loaned_since = t
161
148
  end
162
149
 
163
150
  @book.redd = @checkbutton_redd.active?
@@ -174,13 +161,11 @@ module Alexandria
174
161
  end
175
162
  @book.own = @checkbutton_own.active?
176
163
  @book.want = @checkbutton_want.active?
177
- @book.tags = @entry_tags.text.split(',') # tags are comma separated
164
+ @book.tags = @entry_tags.text.split(",") # tags are comma separated
178
165
 
179
166
  FileUtils.rm_f(@cover_file) if @delete_cover_file
180
167
 
181
- if @original_cover_file
182
- FileUtils.rm_f(@original_cover_file)
183
- end
168
+ FileUtils.rm_f(@original_cover_file) if @original_cover_file
184
169
 
185
170
  @library.save(@book)
186
171
  # @on_close_cb.call(@book)
@@ -188,15 +173,13 @@ module Alexandria
188
173
  end
189
174
 
190
175
  def on_cancel
191
- if @original_cover_file
192
- FileUtils.mv(@original_cover_file, @cover_file)
193
- end
176
+ FileUtils.mv(@original_cover_file, @cover_file) if @original_cover_file
194
177
  @book_properties_dialog.destroy
195
178
  end
196
179
 
197
180
  def on_help
198
181
  Alexandria::UI.display_help(@preferences_dialog,
199
- 'editing-book-properties')
182
+ "editing-book-properties")
200
183
  end
201
184
  end
202
185
  end
@@ -1,36 +1,25 @@
1
- # Copyright (C) 2004-2006 Laurent Sansonetti
2
- # Copyright (C) 2011, 2014, 2016 Matijs van Zuijlen
3
- #
4
- # Alexandria is free software; you can redistribute it and/or
5
- # modify it under the terms of the GNU General Public License as
6
- # published by the Free Software Foundation; either version 2 of the
7
- # License, or (at your option) any later version.
8
- #
9
- # Alexandria is distributed in the hope that it will be useful,
10
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
- # General Public License for more details.
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Alexandria.
13
4
  #
14
- # You should have received a copy of the GNU General Public
15
- # License along with Alexandria; see the file COPYING. If not,
16
- # write to the Free Software Foundation, Inc., 51 Franklin Street,
17
- # Fifth Floor, Boston, MA 02110-1301 USA.
5
+ # See the file README.md for authorship and licensing information.
18
6
 
19
- # require 'alexandria/ui/glade_base'
7
+ require "alexandria/ui/builder_base"
8
+ require "alexandria/ui/error_dialog"
20
9
 
21
10
  module Alexandria
22
11
  module UI
23
12
  class BookPropertiesDialogBase < BuilderBase
24
13
  include GetText
25
14
  extend GetText
26
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
15
+ GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
27
16
 
28
17
  COVER_MAXWIDTH = 140 # pixels
29
18
 
30
19
  COVER_ABSOLUTE_MAXHEIGHT = 250 # pixels, above this we scale down...
31
20
 
32
21
  def initialize(parent, cover_file)
33
- super('book_properties_dialog__builder.glade', widget_names)
22
+ super("book_properties_dialog__builder.glade", widget_names)
34
23
  @setup_finished = false
35
24
  @book_properties_dialog.transient_for = parent
36
25
  @parent = parent
@@ -49,15 +38,15 @@ module Alexandria
49
38
  @treeview_authors.model = Gtk::ListStore.new(String, TrueClass)
50
39
  @treeview_authors.selection.mode = :single
51
40
  renderer = Gtk::CellRendererText.new
52
- renderer.signal_connect('edited') do |_cell, path_string, new_text|
41
+ renderer.signal_connect("edited") do |_cell, path_string, new_text|
53
42
  path = Gtk::TreePath.new(path_string)
54
43
  iter = @treeview_authors.model.get_iter(path)
55
44
  iter[0] = new_text
56
45
  end
57
- renderer.signal_connect('editing_started') do |_cell, entry, _path_string|
46
+ renderer.signal_connect("editing_started") do |_cell, entry, _path_string|
58
47
  entry.complete_authors
59
48
  end
60
- col = Gtk::TreeViewColumn.new('', renderer,
49
+ col = Gtk::TreeViewColumn.new("", renderer,
61
50
  text: 0,
62
51
  editable: 1)
63
52
  @treeview_authors.append_column(col)
@@ -70,6 +59,10 @@ module Alexandria
70
59
  end
71
60
  end
72
61
 
62
+ def show
63
+ @book_properties_dialog.show
64
+ end
65
+
73
66
  def setup_calendar_widgets
74
67
  @popup_displayed = false
75
68
  @calendar_popup = Gtk::Window.new # Gtk::Window::POPUP)
@@ -81,12 +74,12 @@ module Alexandria
81
74
 
82
75
  @calendar_popup.set_transient_for(@book_properties_dialog)
83
76
  @calendar_popup.set_type_hint :dialog
84
- @calendar_popup.name = 'calendar-popup'
77
+ @calendar_popup.name = "calendar-popup"
85
78
  @calendar_popup.resizable = false
86
79
  # @calendar_popup.border_width = 4
87
80
  # @calendar_popup.app_paintable = true
88
81
 
89
- @calendar_popup.signal_connect('focus-out-event') do |_popup, _event|
82
+ @calendar_popup.signal_connect("focus-out-event") do |_popup, _event|
90
83
  hide_calendar_popup
91
84
  false
92
85
  end
@@ -94,7 +87,7 @@ module Alexandria
94
87
  @calendar = Gtk::Calendar.new
95
88
  @calendar_popup.add(@calendar)
96
89
 
97
- @calendar.signal_connect('day-selected') do
90
+ @calendar.signal_connect("day-selected") do
98
91
  date_arr = @calendar.date
99
92
  year = date_arr[0]
100
93
  month = date_arr[1] # + 1 # gtk : months 0-indexed, Time.gm : 1-index
@@ -105,7 +98,7 @@ module Alexandria
105
98
  end
106
99
  end
107
100
 
108
- @calendar.signal_connect('day-selected-double-click') do
101
+ @calendar.signal_connect("day-selected-double-click") do
109
102
  date_arr = @calendar.date
110
103
  year = date_arr[0]
111
104
  month = date_arr[1] # + 1 # gtk : months 0-indexed, Time.gm : 1-index
@@ -117,26 +110,28 @@ module Alexandria
117
110
  hide_calendar_popup
118
111
  end
119
112
 
120
- @redd_date.signal_connect('icon-press') do |entry, primary, _icon|
121
- if primary.nick == 'primary'
113
+ @redd_date.signal_connect("icon-press") do |entry, primary, _icon|
114
+ case primary.nick
115
+ when "primary"
122
116
  display_calendar_popup(entry)
123
- elsif primary.nick == 'secondary'
117
+ when "secondary"
124
118
  clear_date_entry(entry)
125
119
  end
126
120
  end
127
121
 
128
- @date_loaned_since.signal_connect('icon-press') do |entry, primary, _icon|
129
- if primary.nick == 'primary'
122
+ @date_loaned_since.signal_connect("icon-press") do |entry, primary, _icon|
123
+ case primary.nick
124
+ when "primary"
130
125
  display_calendar_popup(entry)
131
- elsif primary.nick == 'secondary'
126
+ when "secondary"
132
127
  clear_date_entry(entry)
133
- @label_loaning_duration.label = ''
128
+ @label_loaning_duration.label = ""
134
129
  end
135
130
  end
136
131
  end
137
132
 
138
133
  def clear_date_entry(entry)
139
- entry.text = ''
134
+ entry.text = ""
140
135
  end
141
136
 
142
137
  def hide_calendar_popup
@@ -195,28 +190,20 @@ module Alexandria
195
190
  end
196
191
 
197
192
  def widget_names
198
- [:book_properties_dialog, :dialog_vbox1, :button_box,
199
- :notebook1, :hbox1, :table1, :label1, :label7, :entry_title,
200
- :entry_publisher, :label5, :entry_isbn, :hbox3,
201
- :scrolledwindow2, :treeview_authors, :vbox2, :button3,
202
- :image2, :button4, :image3, :label3, :label9, :entry_edition,
203
- :label16, :entry_publish_date, :label17, :entry_tags,
204
- :vseparator1, :vbox1, :label12, :button_cover, :image_cover,
205
- :vbox4, :vbox5, :checkbutton_own, :vbox6, :checkbutton_redd,
206
- :redd_date, :checkbutton_want, :eventbox8, :hbox2,
207
- :eventbox6, :image5, :eventbox1, :image_rating1, :eventbox5,
208
- :image_rating2, :eventbox4, :image_rating3, :eventbox3,
209
- :image_rating4, :eventbox2, :image_rating5, :eventbox7,
210
- :image4, :label11, :label9, :vbox3, :checkbutton_loaned,
211
- :table2, :entry_loaned_to, :label_loaning_duration, :label15,
212
- :label14, :date_loaned_since, :label13, :scrolledwindow1,
213
- :textview_notes, :label10]
193
+ [:book_properties_dialog, :button_box, :button_cover,
194
+ :checkbutton_loaned, :checkbutton_own, :checkbutton_redd,
195
+ :checkbutton_want, :date_loaned_since, :entry_edition,
196
+ :entry_loaned_to, :entry_publish_date, :entry_publisher, :entry_isbn,
197
+ :entry_tags, :entry_title, :image_cover, :image_rating1,
198
+ :image_rating2, :image_rating3, :image_rating4, :image_rating5,
199
+ :label_loaning_duration, :notebook, :redd_date, :textview_notes,
200
+ :treeview_authors]
214
201
  end
215
202
 
216
203
  def on_title_changed
217
204
  title = @entry_title.text.strip
218
205
  @book_properties_dialog.title = if title.empty?
219
- _('Properties')
206
+ _("Properties")
220
207
  else
221
208
  _("Properties for '%s'") % title
222
209
  end
@@ -224,7 +211,7 @@ module Alexandria
224
211
 
225
212
  def on_add_author
226
213
  iter = @treeview_authors.model.append
227
- iter[0] = _('Author')
214
+ iter[0] = _("Author")
228
215
  iter[1] = true
229
216
  @treeview_authors.set_cursor(iter.path,
230
217
  @treeview_authors.get_column(0),
@@ -270,47 +257,49 @@ module Alexandria
270
257
 
271
258
  def want_toggled; end
272
259
 
273
- @@latest_filechooser_directory = ENV['HOME']
260
+ @@latest_filechooser_directory = ENV["HOME"]
274
261
  def on_change_cover
275
- backend = `uname`.chomp == 'FreeBSD' ? 'neant' : 'gnome-vfs'
276
- dialog = Gtk::FileChooserDialog.new(_('Select a cover image'),
262
+ backend = `uname`.chomp == "FreeBSD" ? "neant" : "gnome-vfs"
263
+ dialog = Gtk::FileChooserDialog.new(_("Select a cover image"),
277
264
  @book_properties_dialog,
278
265
  Gtk::FileChooser::ACTION_OPEN,
279
266
  backend,
280
- [_('No Cover'), :reject],
281
- [Gtk::Stock::CANCEL, :cancel],
282
- [Gtk::Stock::OPEN, :accept])
267
+ [_("No Cover"), Gtk::ResponseType::REJECT],
268
+ [Gtk::Stock::CANCEL, Gtk::ResponseType::CANCEL],
269
+ [Gtk::Stock::OPEN, Gtk::ResponseType::ACCEPT])
283
270
  dialog.current_folder = @@latest_filechooser_directory
284
271
  response = dialog.run
285
- if response == :accept
272
+ case response
273
+ when Gtk::ResponseType::ACCEPT
286
274
  begin
287
275
  @delete_cover_file = false
288
276
  cover = GdkPixbuf::Pixbuf.new(file: dialog.filename)
289
277
  # At this stage the file format is recognized.
290
278
 
291
- if File.exist?(@cover_file)
292
- unless @original_cover_file
293
- # make a back up, but only of the original
294
- @original_cover_file = "#{@cover_file}~"
295
- FileUtils.cp(@cover_file, @original_cover_file)
296
- end
279
+ if File.exist?(@cover_file) && !@original_cover_file
280
+ # make a back up, but only of the original
281
+ @original_cover_file = "#{@cover_file}~"
282
+ FileUtils.cp(@cover_file, @original_cover_file)
297
283
  end
298
284
  if cover.height > COVER_ABSOLUTE_MAXHEIGHT
299
285
  FileUtils.cp(dialog.filename, "#{@cover_file}.orig")
300
286
  new_width = cover.width / (cover.height / COVER_ABSOLUTE_MAXHEIGHT.to_f)
301
- puts "Scaling large cover image to #{new_width.to_i} x #{COVER_ABSOLUTE_MAXHEIGHT}"
287
+ log.info do
288
+ "Scaling large cover image to" \
289
+ " #{new_width.to_i} x #{COVER_ABSOLUTE_MAXHEIGHT}"
290
+ end
302
291
  cover = cover.scale(new_width.to_i, COVER_ABSOLUTE_MAXHEIGHT)
303
- cover.save(@cover_file, 'jpeg')
292
+ cover.save(@cover_file, "jpeg")
304
293
  else
305
294
  FileUtils.cp(dialog.filename, @cover_file)
306
295
  end
307
296
 
308
297
  self.cover = cover
309
298
  @@latest_filechooser_directory = dialog.current_folder
310
- rescue RuntimeError => e
311
- ErrorDialog.new(@book_properties_dialog, e.message)
299
+ rescue RuntimeError => ex
300
+ ErrorDialog.new(@book_properties_dialog, ex.message).display
312
301
  end
313
- elsif response == :reject
302
+ when Gtk::ResponseType::REJECT
314
303
  ## FileUtils.rm_f(@cover_file) # fixing bug #16707
315
304
  @delete_cover_file = true
316
305
 
@@ -319,7 +308,11 @@ module Alexandria
319
308
  dialog.destroy
320
309
  end
321
310
 
322
- def on_destroy; end # no action by default
311
+ def on_destroy
312
+ @book_properties_dialog.hide
313
+ # Stop notebook trying to set tab labels at this time
314
+ @notebook.show_tabs = false
315
+ end
323
316
 
324
317
  def on_loaned
325
318
  loaned = @checkbutton_loaned.active?
@@ -329,7 +322,7 @@ module Alexandria
329
322
  end
330
323
 
331
324
  def on_loaned_date_changed
332
- date_regexes = [/[0123]?[0-9]\/[0123]?[0-9]\/[0-9]{4}/,
325
+ date_regexes = [%r{[0123]?[0-9]/[0123]?[0-9]/[0-9]{4}},
333
326
  /[0-9]{4}-[0123]?[0-9]-[0123]?[0-9]/]
334
327
  matches_regex = false
335
328
  date_regexes.each do |regex|
@@ -337,33 +330,33 @@ module Alexandria
337
330
  break if matches_regex
338
331
  end
339
332
  return unless matches_regex
333
+
340
334
  t = parse_date(@date_loaned_since.text)
341
335
  if t.nil?
342
- @label_loaning_duration.label = ''
336
+ @label_loaning_duration.label = ""
343
337
  return
344
338
  end
345
339
  loaned_time = Time.at(t)
346
340
  n_days = ((Time.now - loaned_time) / (3600 * 24)).to_i
347
341
  if n_days > 365_250 # 1,000 years
348
- @label_loaning_duration.label = ''
342
+ @label_loaning_duration.label = ""
349
343
  return
350
344
  end
351
345
  @label_loaning_duration.label = if n_days > 0
352
- n_('%d day', '%d days', n_days) % n_days
346
+ n_("%d day", "%d days", n_days) % n_days
353
347
  else
354
- ''
348
+ ""
355
349
  end
356
350
  end
357
351
 
358
352
  def redd_toggled
359
353
  redd_yes = @checkbutton_redd.active?
360
354
  @redd_date.sensitive = redd_yes
361
- if @setup_finished
362
- # don't do this when popping up the dialog for the first time
363
- if redd_yes && @redd_date.text.strip.empty?
364
- display_calendar_popup(@redd_date)
365
- end
366
- end
355
+
356
+ return unless redd_yes && @redd_date.text.strip.empty?
357
+
358
+ # don't do this when popping up the dialog for the first time
359
+ display_calendar_popup(@redd_date) if @setup_finished
367
360
  end
368
361
 
369
362
  private
@@ -376,7 +369,8 @@ module Alexandria
376
369
  @image_rating4,
377
370
  @image_rating5
378
371
  ]
379
- raise 'out of range' if rating < 0 || rating > images.length
372
+ raise _("out of range") if rating < 0 || rating > images.length
373
+
380
374
  images[0..rating - 1].each { |x| x.pixbuf = Icons::STAR_SET }
381
375
  images[rating..-1].each { |x| x.pixbuf = Icons::STAR_UNSET }
382
376
  @current_rating = rating
@@ -394,8 +388,8 @@ module Alexandria
394
388
 
395
389
  def loaned_since=(time)
396
390
  if time.nil?
397
- @date_loaned_since.text = ''
398
- @label_loaning_duration.label = ''
391
+ @date_loaned_since.text = ""
392
+ @label_loaning_duration.label = ""
399
393
  else
400
394
  @date_loaned_since.text = format_date(time)
401
395
  on_loaned_date_changed
@@ -408,18 +402,19 @@ module Alexandria
408
402
  end
409
403
 
410
404
  def parse_date(datestring)
411
- date_format = '%d/%m/%Y' # or '%m/%d/%Y' for USA and Canada ; or '%Y-%m-%d' for most of Asia
412
- ## http://en.wikipedia.org/wiki/Calendar_date#Middle_endian_forms.2C_starting_with_the_month
405
+ # '%m/%d/%Y' for USA and Canada ; or '%Y-%m-%d' for most of Asia
406
+ # http://en.wikipedia.org/wiki/Calendar_date#Middle_endian_forms.2C_starting_with_the_month
407
+ date_format = "%d/%m/%Y"
413
408
  begin
414
409
  d = Date.strptime(datestring, date_format)
415
410
  Time.gm(d.year, d.month, d.day)
416
- rescue
411
+ rescue StandardError
417
412
  nil
418
413
  end
419
414
  end
420
415
 
421
416
  def format_date(datetime)
422
- datetime.strftime('%d/%m/%Y')
417
+ datetime.strftime("%d/%m/%Y")
423
418
  end
424
419
  end
425
420
  end