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
@@ -1,154 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright (C) 2004-2006 Laurent Sansonetti
4
- # Copyright (C) 2011, 2014 Matijs van Zuijlen
5
- #
6
- # Alexandria is free software; you can redistribute it and/or
7
- # modify it under the terms of the GNU General Public License as
8
- # published by the Free Software Foundation; either version 2 of the
9
- # License, or (at your option) any later version.
10
- #
11
- # Alexandria is distributed in the hope that it will be useful,
12
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
- # General Public License for more details.
15
- #
16
- # You should have received a copy of the GNU General Public
17
- # License along with Alexandria; see the file COPYING. If not,
18
- # write to the Free Software Foundation, Inc., 51 Franklin Street,
19
- # Fifth Floor, Boston, MA 02110-1301 USA.
20
-
21
- module Alexandria
22
- module UI
23
- class NewBookDialogManual < BookPropertiesDialogBase
24
- include GetText
25
- extend GetText
26
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
27
-
28
- TMP_COVER_FILE = File.join(Dir.tmpdir, 'tmp_cover')
29
- def initialize(parent, library, &on_add_cb)
30
- super(parent, TMP_COVER_FILE)
31
-
32
- @library = library
33
- @on_add_cb = on_add_cb
34
- FileUtils.rm_f(TMP_COVER_FILE)
35
-
36
- cancel_button = Gtk::Button.new(Gtk::Stock::CANCEL)
37
- cancel_button.signal_connect('clicked') { on_cancel }
38
- cancel_button.show
39
- @button_box << cancel_button
40
-
41
- add_button = Gtk::Button.new(Gtk::Stock::ADD)
42
- add_button.signal_connect('clicked') { on_add }
43
- add_button.show
44
- @button_box << add_button
45
-
46
- help_button = Gtk::Button.new(Gtk::Stock::HELP)
47
- help_button.signal_connect('clicked') { on_help }
48
- help_button.show
49
- @button_box << help_button
50
- @button_box.set_child_secondary(help_button, true)
51
-
52
- self.rating = Book::DEFAULT_RATING
53
- self.cover = Icons::BOOK_ICON
54
-
55
- on_title_changed
56
- end
57
-
58
- def on_title_changed
59
- title = @entry_title.text.strip
60
- begin
61
- @book_properties_dialog.title = if title.empty?
62
- _('Adding a Book')
63
- else
64
- _("Adding '%s'") % title
65
- end
66
- rescue
67
- raise "There's a problem with a book somewhere"
68
- end
69
- end
70
-
71
- private
72
-
73
- def on_cancel
74
- @book_properties_dialog.destroy
75
- end
76
-
77
- class AddError < StandardError
78
- end
79
-
80
- def on_add
81
- if (title = @entry_title.text.strip).empty?
82
- raise AddError, _('A title must be provided.')
83
- end
84
- isbn = nil
85
- if @entry_isbn.text != ''
86
- ary = @library.select { |book|
87
- book.ident == @entry_isbn.text
88
- }
89
- raise AddError, _('The EAN/ISBN you provided is already used in this library.') unless ary.empty?
90
- isbn = begin
91
- Library.canonicalise_isbn(@entry_isbn.text)
92
- rescue Alexandria::Library::InvalidISBNError
93
- raise AddError, _("Couldn't validate the " \
94
- 'EAN/ISBN you provided. Make ' \
95
- 'sure it is written correcty, ' \
96
- 'and try again.')
97
- end
98
- end
99
- if (publisher = @entry_publisher.text.strip).empty?
100
- raise AddError, _('A publisher must be provided.')
101
- end
102
- publishing_year = @entry_publish_date.text.to_i
103
- # TODO: Get rid of this silly requirement
104
- if (edition = @entry_edition.text.strip).empty?
105
- raise AddError, _('A binding must be provided.')
106
- end
107
- authors = []
108
- @treeview_authors.model.each { |_m, _p, i| authors << i[0] }
109
- if authors.empty?
110
- raise AddError, _('At least one author must be ' \
111
- 'provided.')
112
- end
113
- book = Book.new(title, authors, isbn, publisher,
114
- publishing_year.zero? ? nil : publishing_year,
115
- edition)
116
- book.rating = @current_rating
117
- book.notes = @textview_notes.buffer.text
118
- book.loaned = @checkbutton_loaned.active?
119
- book.loaned_to = @entry_loaned_to.text
120
- # book.loaned_since = Time.at(@date_loaned_since.time)
121
- book.loaned_since = parse_date(@date_loaned_since.text)
122
- book.redd = @checkbutton_redd.active?
123
- book.own = @checkbutton_own.active?
124
- book.want = @checkbutton_want.active?
125
- book.tags = @entry_tags.text.split
126
- @library << book
127
- @library.save(book)
128
- if File.exist?(TMP_COVER_FILE) && !@delete_cover_file
129
- FileUtils.cp(TMP_COVER_FILE, @library.cover(book))
130
- end
131
- @on_add_cb.call(book)
132
- @book_properties_dialog.destroy
133
- rescue AddError => e
134
- ErrorDialog.new(@parent, _("Couldn't add the book"),
135
- e.message)
136
- end
137
-
138
- # COPIED from book_properties_dialog_base
139
- def parse_date(datestring)
140
- date_format = '%d/%m/%Y'
141
- begin
142
- d = Date.strptime(datestring, date_format)
143
- Time.gm(d.year, d.month, d.day)
144
- rescue
145
- nil
146
- end
147
- end
148
-
149
- def on_help
150
- Alexandria::UI.display_help(@preferences_dialog, 'add-book-manually')
151
- end
152
- end
153
- end
154
- end
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright (C) 2004-2006 Laurent Sansonetti
4
- # Copyright (C) 2015, 2016 Matijs van Zuijlen
5
- #
6
- # Alexandria is free software; you can redistribute it and/or
7
- # modify it under the terms of the GNU General Public License as
8
- # published by the Free Software Foundation; either version 2 of the
9
- # License, or (at your option) any later version.
10
- #
11
- # Alexandria is distributed in the hope that it will be useful,
12
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
- # General Public License for more details.
15
- #
16
- # You should have received a copy of the GNU General Public
17
- # License along with Alexandria; see the file COPYING. If not,
18
- # write to the Free Software Foundation, Inc., 51 Franklin Street,
19
- # Fifth Floor, Boston, MA 02110-1301 USA.
20
-
21
- module Alexandria
22
- module UI
23
- class NewSmartLibraryDialog < SmartLibraryPropertiesDialogBase
24
- include GetText
25
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
26
-
27
- def initialize(parent)
28
- super(parent)
29
-
30
- add_buttons([Gtk::Stock::CANCEL, :cancel],
31
- [Gtk::Stock::NEW, :ok])
32
-
33
- self.title = _('New Smart Library')
34
- # FIXME: Should accept just :cancel
35
- self.default_response = Gtk::ResponseType::CANCEL
36
-
37
- show_all
38
- insert_new_rule
39
-
40
- while ((response = run) != :cancel) &&
41
- (response != :delete_event)
42
-
43
- if response == :help
44
- Alexandria::UI.display_help(self, 'new-smart-library')
45
- elsif response == :ok
46
- if user_confirms_possible_weirdnesses_before_saving?
47
- rules = smart_library_rules
48
- basename = smart_library_base_name(rules) || _('Smart Library')
49
- name = Library.generate_new_name(
50
- Libraries.instance.all_libraries,
51
- basename)
52
- library = SmartLibrary.new(name,
53
- rules,
54
- predicate_operator_rule)
55
- yield(library)
56
- break
57
- end
58
- end
59
- end
60
-
61
- destroy
62
- end
63
-
64
- private
65
-
66
- def smart_library_base_name(rules)
67
- if rules.length == 1
68
- value = rules.first.value
69
- return value if value.is_a?(String) && !value.strip.empty?
70
- end
71
- end
72
- end
73
- end
74
- end
@@ -1,568 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright (C) 2004-2006 Laurent Sansonetti
4
- # Copyright (C) 2011, 2016 Matijs van Zuijlen
5
- #
6
- # Alexandria is free software; you can redistribute it and/or
7
- # modify it under the terms of the GNU General Public License as
8
- # published by the Free Software Foundation; either version 2 of the
9
- # License, or (at your option) any later version.
10
- #
11
- # Alexandria is distributed in the hope that it will be useful,
12
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
- # General Public License for more details.
15
- #
16
- # You should have received a copy of the GNU General Public
17
- # License along with Alexandria; see the file COPYING. If not,
18
- # write to the Free Software Foundation, Inc., 51 Franklin Street,
19
- # Fifth Floor, Boston, MA 02110-1301 USA.
20
-
21
- require 'alexandria/scanners/cuecat'
22
- require 'alexandria/scanners/keyboard'
23
-
24
- class Gtk::Entry
25
- attr_writer :mandatory
26
- def mandatory?
27
- @mandatory
28
- end
29
- end
30
-
31
- module Alexandria
32
- module UI
33
- class ProviderPreferencesBaseDialog < Gtk::Dialog
34
- def initialize(*args)
35
- super(*args)
36
-
37
- self.resizable = false
38
- child.border_width = 12
39
-
40
- @controls = []
41
- end
42
-
43
- def fill_table(table, provider)
44
- i = table.n_rows
45
- table.resize(table.n_rows + provider.prefs.length,
46
- table.n_columns)
47
- table.border_width = 12
48
- table.row_spacings = 6
49
- table.column_spacings = 12
50
-
51
- @controls.clear
52
-
53
- provider.prefs.read.each do |variable|
54
- if variable.name == 'piggyback'
55
- next
56
- # ULTRA-HACK!! for bug #13302
57
- # not displaying the visual choice, as its usually unnecessary
58
- # Either way, this is confusing to the user: FIX
59
- # - Cathal Mc Ginley 2008-02-18
60
- end
61
-
62
- if variable.name == 'enabled'
63
- # also don't display Enabled/Disabled
64
- next
65
- end
66
-
67
- label = Gtk::Label.new('_' + variable.description + ':')
68
- label.use_underline = true
69
- label.xalign = 0
70
- table.attach_defaults(label, 0, 1, i, i + 1)
71
-
72
- if variable.possible_values.nil?
73
- entry = Gtk::Entry.new
74
- entry.text = variable.value.to_s
75
- entry.mandatory = variable.mandatory?
76
- else
77
- entry = Gtk::ComboBoxText.new
78
- variable.possible_values.each do |value|
79
- entry.append_text(value.to_s)
80
- end
81
- index = variable.possible_values.index(variable.value)
82
- entry.active = index
83
- end
84
- label.mnemonic_widget = entry
85
-
86
- @controls << [variable, entry]
87
-
88
- table.attach_defaults(entry, 1, 2, i, i + 1)
89
- i += 1
90
- end
91
- table
92
- end
93
-
94
- def sync_variables
95
- @controls.each do |variable, entry|
96
- variable.new_value = case entry
97
- when Gtk::ComboBox
98
- variable.possible_values[entry.active]
99
- when Gtk::Entry
100
- entry.text
101
- end
102
- end
103
- end
104
- end
105
-
106
- class ProviderPreferencesDialog < ProviderPreferencesBaseDialog
107
- include GetText
108
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
109
-
110
- def initialize(parent, provider)
111
- super(title: _('Preferences for %s') % provider.fullname,
112
- parent: parent,
113
- flags: :modal,
114
- buttons: [[Gtk::Stock::CLOSE, :close]])
115
-
116
- table = Gtk::Table.new(0, 0)
117
- fill_table(table, provider)
118
- child.pack_start(table)
119
-
120
- signal_connect('destroy') { sync_variables }
121
-
122
- show_all
123
- run
124
- destroy
125
- end
126
- end
127
-
128
- class NewProviderDialog < ProviderPreferencesBaseDialog
129
- include GetText
130
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
131
-
132
- def initialize(parent)
133
- super(_('New Provider'),
134
- parent,
135
- Gtk::Dialog::MODAL,
136
- [Gtk::Stock::CANCEL, :cancel])
137
- @add_button = add_button(Gtk::Stock::ADD,
138
- :accept)
139
-
140
- instances = BookProviders.abstract_classes.map(&:new)
141
- @selected_instance = nil
142
-
143
- @table = Gtk::Table.new(2, 2)
144
- vbox.pack_start(@table)
145
-
146
- # Name.
147
-
148
- label_name = Gtk::Label.new(_('_Name:'))
149
- label_name.use_underline = true
150
- label_name.xalign = 0
151
- @table.attach_defaults(label_name, 0, 1, 0, 1)
152
-
153
- entry_name = Gtk::Entry.new
154
- entry_name.mandatory = true
155
- label_name.mnemonic_widget = entry_name
156
- @table.attach_defaults(entry_name, 1, 2, 0, 1)
157
-
158
- # Type.
159
-
160
- label_type = Gtk::Label.new(_('_Type:'))
161
- label_type.use_underline = true
162
- label_type.xalign = 0
163
- @table.attach_defaults(label_type, 0, 1, 1, 2)
164
-
165
- combo_type = Gtk::ComboBox.new
166
- instances.each do |instance|
167
- combo_type.append_text(instance.name)
168
- end
169
- combo_type.signal_connect('changed') do |cb|
170
- @selected_instance = instances[cb.active]
171
- fill_table(@table, @selected_instance)
172
- sensitize
173
- # FIXME: this should be re-written once we have multiple
174
- # abstract providers.
175
- end
176
- combo_type.active = 0
177
- label_type.mnemonic_widget = combo_type
178
- @table.attach_defaults(combo_type, 1, 2, 1, 2)
179
-
180
- show_all
181
- if run == :accept
182
- @selected_instance.reinitialize(entry_name.text)
183
- sync_variables
184
- else
185
- @selected_instance = nil
186
- end
187
- destroy
188
- end
189
-
190
- def instance
191
- @selected_instance
192
- end
193
-
194
- private
195
-
196
- def sensitize
197
- entries = @table.children.select { |x| x.is_a?(Gtk::Entry) }
198
- entries.each do |entry|
199
- entry.signal_connect('changed') do
200
- sensitive = true
201
- entries.each do |entry2|
202
- if entry2.mandatory?
203
- sensitive = !entry2.text.strip.empty?
204
- break unless sensitive
205
- end
206
- end
207
- @add_button.sensitive = sensitive
208
- end
209
- end
210
- @add_button.sensitive = false
211
- end
212
- end
213
-
214
- class PreferencesDialog < BuilderBase
215
- include Alexandria::Logging
216
- include GetText
217
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
218
-
219
- def initialize(parent, &changed_block)
220
- super('preferences_dialog__builder.glade', widget_names)
221
- @preferences_dialog.transient_for = parent
222
- @changed_block = changed_block
223
-
224
- @cols = {
225
- @checkbutton_col_authors => 'col_authors_visible',
226
- @checkbutton_col_isbn => 'col_isbn_visible',
227
- @checkbutton_col_publisher => 'col_publisher_visible',
228
- @checkbutton_col_publish_date => 'col_publish_date_visible',
229
- @checkbutton_col_edition => 'col_edition_visible',
230
- @checkbutton_col_redd => 'col_redd_visible',
231
- @checkbutton_col_own => 'col_own_visible',
232
- @checkbutton_col_want => 'col_want_visible',
233
- @checkbutton_col_rating => 'col_rating_visible',
234
- @checkbutton_col_tags => 'col_tags_visible',
235
- @checkbutton_col_loaned_to => 'col_loaned_to_visible'
236
- }
237
- @cols.each_pair do |checkbutton, pref_name|
238
- if checkbutton
239
- checkbutton.active = Preferences.instance.send(pref_name)
240
- else
241
- log.warn {
242
- "no CheckButton for property #{pref_name} " \
243
- '(probably conflicting versions of GUI and lib code)'
244
- }
245
- end
246
- end
247
-
248
- model = Gtk::ListStore.new(String, String, TrueClass, Integer)
249
- @treeview_providers.model = model
250
- reload_providers
251
- model.signal_connect_after('row-changed') { update_priority }
252
-
253
- renderer = Gtk::CellRendererToggle.new
254
- renderer.activatable = true
255
- renderer.signal_connect('toggled') do |_rndrr, path|
256
- tree_path = Gtk::TreePath.new(path)
257
- @treeview_providers.selection.select_path(tree_path)
258
- prov = selected_provider
259
- if prov
260
- prov.toggle_enabled
261
- adjust_selected_provider(prov)
262
- # reload_providers
263
- end
264
- end
265
-
266
- # renderer.active = true
267
- column = Gtk::TreeViewColumn.new('Enabled', renderer)
268
- column.set_cell_data_func(renderer) do |_col, rndr, _mod, iter|
269
- value = iter[2]
270
- rndr.active = value
271
- end
272
-
273
- @treeview_providers.append_column(column)
274
-
275
- renderer = Gtk::CellRendererText.new
276
- column = Gtk::TreeViewColumn.new('Providers',
277
- renderer)
278
- # :text => 0)
279
- column.set_cell_data_func(renderer) do |_col, rndr, _mod, iter|
280
- rndr.markup = iter[0]
281
- # enabled = iter[2]
282
- # unless enabled
283
- # rndr.foreground = "gray"
284
- # end
285
- # rndr.active = value
286
- end
287
- @treeview_providers.append_column(column)
288
- @treeview_providers.selection.signal_connect('changed') \
289
- { sensitize_providers }
290
-
291
- @button_prov_setup.sensitive = false
292
- @button_prov_up.sensitive = @button_prov_down.sensitive = BookProviders.length > 1
293
-
294
- @buttonbox_prov.set_child_secondary(@button_prov_add, true)
295
- @buttonbox_prov.set_child_secondary(@button_prov_remove, true)
296
-
297
- if BookProviders.abstract_classes.empty?
298
- @checkbutton_prov_advanced.sensitive = false
299
- else
300
- view_advanced = Preferences.instance.view_advanced_settings
301
- @checkbutton_prov_advanced.active = true if view_advanced
302
- end
303
-
304
- setup_enable_disable_popup
305
- sensitize_providers
306
- setup_barcode_scanner_tab
307
- end
308
-
309
- def widget_names
310
- [:button_prov_add, :button_prov_down, :button_prov_remove,
311
- :button_prov_setup, :button_prov_up, :buttonbox_prov,
312
- :checkbutton_col_authors, :checkbutton_col_edition,
313
- :checkbutton_col_isbn, :checkbutton_col_loaned_to,
314
- :checkbutton_col_own, :checkbutton_col_publish_date,
315
- :checkbutton_col_publisher, :checkbutton_col_rating,
316
- :checkbutton_col_redd, :checkbutton_col_tags,
317
- :checkbutton_col_want, :checkbutton_prov_advanced,
318
- :preferences_dialog, :treeview_providers,
319
- :scanner_device_type, :use_scanning_sound, :use_scan_sound]
320
- end
321
-
322
- def setup_barcode_scanner_tab
323
- @scanner_device_model = Gtk::ListStore.new(String, String)
324
- chosen_scanner_name = Preferences.instance.barcode_scanner
325
- index = 0
326
- @scanner_device_type.model = @scanner_device_model
327
- renderer = Gtk::CellRendererText.new
328
- @scanner_device_type.pack_start(renderer, true)
329
- @scanner_device_type.add_attribute(renderer, 'text', 0)
330
-
331
- Alexandria::Scanners.each_scanner do |scanner|
332
- iter = @scanner_device_model.append
333
- iter[0] = scanner.display_name
334
- iter[1] = scanner.name
335
- @scanner_device_type.active = index if chosen_scanner_name == scanner.name
336
- index += 1
337
- end
338
-
339
- @use_scanning_sound.active = Preferences.instance.play_scanning_sound
340
- @use_scan_sound.active = Preferences.instance.play_scan_sound
341
- end
342
-
343
- def setup_enable_disable_popup
344
- # New Enable/Disable pop-up menu...
345
- @enable_disable_providers_menu = Gtk::Menu.new
346
- @enable_item = Gtk::MenuItem.new(label: _('Disable Provider'))
347
- @enable_item.signal_connect('activate') {
348
- prov = selected_provider
349
- prov.toggle_enabled
350
- adjust_selected_provider(prov)
351
- }
352
- @enable_disable_providers_menu.append(@enable_item)
353
- @enable_disable_providers_menu.show_all
354
-
355
- @treeview_providers.signal_connect('button_press_event') do |widget, event|
356
- if event_is_right_click(event)
357
- if (path = widget.get_path_at_pos(event.x, event.y))
358
- widget.grab_focus
359
- obj = widget.selection
360
- path = path.first
361
- unless obj.path_is_selected?(path)
362
- widget.unselect_all
363
- obj.select_path(path)
364
- end
365
- sel = widget.selection.selected
366
- if sel
367
- already_enabled = sel[2]
368
- message = already_enabled ? _('Disable Provider') : _('Enable Provider')
369
- @enable_item.label = message
370
- GLib::Idle.add do
371
- @enable_disable_providers_menu.popup(nil, nil, event.button, event.time)
372
- false
373
- end
374
- end
375
- else
376
- puts 'not on a path'
377
- end
378
- end
379
- end
380
-
381
- # Popup the menu on Shift-F10
382
- @treeview_providers.signal_connect('popup_menu') {
383
- selected_prov = @treeview_providers.selection.selected
384
- puts selected_prov.inspect
385
- if selected_prov
386
- GLib::Idle.add do
387
- already_enabled = selected_prov[2]
388
- message = already_enabled ? _('Disable Provider') : _('Enable Provider')
389
- @enable_item.label = message
390
-
391
- @enable_disable_providers_menu.popup(nil, nil, 0, :current_time)
392
- false
393
- end
394
- else
395
- puts 'no action'
396
- end
397
- }
398
- end
399
-
400
- def event_is_right_click(event)
401
- (event.event_type == :button_press) && (event.button == 3)
402
- end
403
-
404
- def prefs_empty(prefs)
405
- prefs.empty? || ((prefs.size == 1) && (prefs.first.name == 'enabled'))
406
- end
407
-
408
- def on_provider_setup
409
- provider = selected_provider
410
- ProviderPreferencesDialog.new(@preferences_dialog, provider) unless prefs_empty(provider.prefs)
411
- end
412
-
413
- def on_provider_up
414
- iter = @treeview_providers.selection.selected
415
- previous_path = iter.path
416
- previous_path.prev!
417
- model = @treeview_providers.model
418
- model.move_after(model.get_iter(previous_path), iter)
419
- sensitize_providers
420
- update_priority
421
- end
422
-
423
- def on_provider_down
424
- iter = @treeview_providers.selection.selected
425
- next_iter = iter.dup
426
- next_iter.next!
427
- @treeview_providers.model.move_after(iter, next_iter)
428
- sensitize_providers
429
- update_priority
430
- end
431
-
432
- def on_provider_advanced_toggled(checkbutton)
433
- on = checkbutton.active?
434
- Preferences.instance.view_advanced_settings = on
435
- @button_prov_add.visible = @button_prov_remove.visible = on
436
- end
437
-
438
- def on_provider_add
439
- dialog = NewProviderDialog.new(@preferences_dialog)
440
- if dialog.instance
441
- BookProviders.update_priority
442
- reload_providers
443
- end
444
- end
445
-
446
- def on_scanner_device_type(_combo)
447
- iter = @scanner_device_type.active_iter
448
- Preferences.instance.barcode_scanner = iter[1] if iter && iter[1]
449
- end
450
-
451
- def on_use_scanning_sound(checkbox)
452
- Preferences.instance.play_scanning_sound = checkbox.active?
453
- end
454
-
455
- def on_use_scan_sound(checkbox)
456
- Preferences.instance.play_scan_sound = checkbox.active?
457
- end
458
-
459
- def on_provider_remove
460
- provider = selected_provider
461
- dialog = AlertDialog.new(@main_app,
462
- _('Are you sure you want to ' \
463
- 'permanently delete the provider ' \
464
- "'%s'?") % provider.fullname,
465
- Gtk::Stock::DIALOG_QUESTION,
466
- [[Gtk::Stock::CANCEL,
467
- :cancel],
468
- [Gtk::Stock::DELETE,
469
- :ok]],
470
- _('If you continue, the provider and ' \
471
- 'all of its preferences will be ' \
472
- 'permanently deleted.'))
473
- dialog.default_response = Gtk::ResponseType::CANCEL
474
- dialog.show_all
475
- if dialog.run == :ok
476
- provider.remove
477
- BookProviders.update_priority
478
- reload_providers
479
- end
480
- dialog.destroy
481
- end
482
-
483
- def on_column_toggled(checkbutton)
484
- raise if @cols[checkbutton].nil?
485
- Preferences.instance.send("#{@cols[checkbutton]}=",
486
- checkbutton.active?)
487
-
488
- @changed_block.call
489
- end
490
-
491
- def on_providers_button_press_event(_widget, event)
492
- # double left click
493
- on_provider_setup if (event.event_type == :'2button_press') && (event.button == 1)
494
- end
495
-
496
- def on_close
497
- @preferences_dialog.destroy
498
- Alexandria::Preferences.instance.save!
499
- end
500
-
501
- def on_help
502
- Alexandria::UI.display_help(@preferences_dialog,
503
- 'alexandria-preferences')
504
- end
505
-
506
- private
507
-
508
- def reload_providers
509
- model = @treeview_providers.model
510
- model.clear
511
- BookProviders.each_with_index do |x, index|
512
- iter = model.append
513
- iter[0] = if x.enabled
514
- x.fullname
515
- else
516
- "<i>#{x.fullname}</i>"
517
- end
518
- iter[1] = x.name
519
- iter[2] = x.enabled
520
- iter[3] = index
521
- end
522
- end
523
-
524
- def selected_provider
525
- iter = @treeview_providers.selection.selected
526
- BookProviders.find { |x| x.name == iter[1] } unless iter.nil?
527
- end
528
-
529
- def adjust_selected_provider(prov)
530
- iter = @treeview_providers.selection.selected
531
- iter[0] = if prov.enabled
532
- prov.fullname
533
- else
534
- "<i>#{prov.fullname}</i>"
535
- end
536
- iter[2] = prov.enabled
537
- end
538
-
539
- def sensitize_providers
540
- model = @treeview_providers.model
541
- sel_iter = @treeview_providers.selection.selected
542
- if sel_iter.nil?
543
- # No selection, we are probably called by ListStore#clear
544
- @button_prov_up.sensitive = false
545
- @button_prov_down.sensitive = false
546
- @button_prov_setup.sensitive = false
547
- @button_prov_remove.sensitive = false
548
- else
549
- last_iter = model.get_iter((BookProviders.length - 1).to_s)
550
- @button_prov_up.sensitive = sel_iter != model.iter_first
551
- @button_prov_down.sensitive = sel_iter != last_iter
552
- provider = BookProviders.find { |x| x.name == sel_iter[1] }
553
- @button_prov_setup.sensitive = !prefs_empty(provider.prefs)
554
- @button_prov_remove.sensitive = provider.abstract?
555
- end
556
- end
557
-
558
- def update_priority
559
- priority = []
560
- @treeview_providers.model.each do |_model, _path, iter|
561
- priority << iter[1]
562
- end
563
- Preferences.instance.providers_priority = priority
564
- BookProviders.update_priority
565
- end
566
- end
567
- end
568
- end