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
@@ -1,46 +1,36 @@
1
- # Copyright (C) 2004-2006 Laurent Sansonetti
2
- # Copyright (C) 2011, 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/smart_library_rule_box"
18
8
 
19
9
  module Alexandria
20
10
  module UI
21
- class SmartLibraryPropertiesDialogBase < Gtk::Dialog
11
+ class SmartLibraryPropertiesDialogBase
22
12
  include Logging
23
13
  include GetText
24
- GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
14
+ GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
25
15
 
26
- attr_reader :predicate_operator_rule
16
+ attr_reader :predicate_operator_rule, :dialog
27
17
 
28
18
  def initialize(parent)
29
- super(title: '',
30
- parent: parent,
31
- flags: :modal,
32
- buttons: [[Gtk::Stock::HELP, :help]])
19
+ @dialog = Gtk::Dialog.new(title: "",
20
+ parent: parent,
21
+ flags: :modal,
22
+ buttons: [[Gtk::Stock::HELP, :help]])
33
23
 
34
- self.window_position = :center
35
- self.resizable = true
36
- self.border_width = 4
37
- child.border_width = 12
24
+ @dialog.window_position = :center
25
+ @dialog.resizable = true
26
+ @dialog.border_width = 4
27
+ @dialog.child.border_width = 12
38
28
 
39
29
  main_box = Gtk::Box.new :vertical
40
30
  main_box.border_width = 4
41
31
  main_box.spacing = 8
42
32
 
43
- child << main_box
33
+ @dialog.child << main_box
44
34
 
45
35
  @smart_library_rules = []
46
36
 
@@ -62,36 +52,58 @@ module Alexandria
62
52
  setup_calendar_widgets
63
53
  end
64
54
 
65
- protected
55
+ def handle_date_icon_press(widget, primary, _icon)
56
+ display_calendar_popup(widget) if primary.nick == "primary"
57
+ end
66
58
 
67
- def smart_library_rules
68
- fill_smart_library_rules_values
69
- @smart_library_rules
59
+ def handle_add_rule_clicked
60
+ insert_new_rule
61
+ end
62
+
63
+ def handle_remove_rule_clicked(box_controller)
64
+ remove_rule_box(box_controller.rule_box)
65
+ end
66
+
67
+ # TODO: Move logic to SmartLibraryRuleBox
68
+ def apply_smart_rule_for_rule_box(rule_box, operand, operation)
69
+ idx = @rules_box.children.index(rule_box)
70
+ smart_library_rules[idx] ||= SmartLibrary::Rule.new(operand,
71
+ operation.first,
72
+ nil)
73
+ new_rule = smart_library_rules[idx]
74
+ new_rule.operand = operand
75
+ new_rule.operation = operation.first
76
+ new_rule.value = nil
70
77
  end
71
78
 
79
+ protected
80
+
81
+ attr_reader :smart_library_rules
82
+
72
83
  def has_weirdnesses?
73
84
  fill_smart_library_rules_values
74
85
  smart_library_rules.each do |rule|
75
- return true if rule.value == ''
86
+ return true if rule.value == ""
76
87
  end
77
88
  false
78
89
  end
79
90
 
80
91
  def user_confirms_possible_weirdnesses_before_saving?
81
92
  return true unless has_weirdnesses?
93
+
82
94
  dialog = AlertDialog.new(
83
- self,
84
- _('Empty or conflictive condition'),
95
+ @dialog,
96
+ _("Empty or conflictive condition"),
85
97
  Gtk::Stock::DIALOG_QUESTION,
86
- [[Gtk::Stock::CANCEL, :cancel],
87
- [_('_Save However'), :yes]],
88
- _('This smart library contains one or more conditions ' \
89
- 'which are empty or conflict with each other. This is ' \
90
- 'likely to result in never matching a book. Are you ' \
91
- 'sure you want to save this library?'))
98
+ [[Gtk::Stock::CANCEL, Gtk::ResponseType::CANCEL],
99
+ [_("_Save However"), Gtk::ResponseType::YES]],
100
+ _("This smart library contains one or more conditions " \
101
+ "which are empty or conflict with each other. This is " \
102
+ "likely to result in never matching a book. Are you " \
103
+ "sure you want to save this library?"))
92
104
  dialog.default_response = Gtk::ResponseType::CANCEL
93
105
  dialog.show_all
94
- confirmed = dialog.run == :yes
106
+ confirmed = dialog.run == Gtk::ResponseType::YES
95
107
  dialog.destroy
96
108
  confirmed
97
109
  end
@@ -102,19 +114,20 @@ module Alexandria
102
114
  if @rules_box.children.length > 1
103
115
  label1 = Gtk::Label.new
104
116
  label1.set_alignment(0.0, 0.5)
105
- label1.text = _('Match')
117
+ label1.text = _("Match")
106
118
 
107
119
  cb = Gtk::ComboBoxText.new
108
- [_('all'), _('any')].each { |x| cb.append_text(x) }
109
- cb.signal_connect('changed') do
110
- @predicate_operator_rule = cb.active.zero? ? SmartLibrary::ALL_RULES : SmartLibrary::ANY_RULE
120
+ [_("all"), _("any")].each { |x| cb.append_text(x) }
121
+ cb.signal_connect("changed") do
122
+ @predicate_operator_rule =
123
+ cb.active.zero? ? SmartLibrary::ALL_RULES : SmartLibrary::ANY_RULE
111
124
  end
112
125
  cb.active =
113
126
  predicate_operator_rule == SmartLibrary::ALL_RULES ? 0 : 1
114
127
 
115
128
  label2 = Gtk::Label.new
116
129
  label2.set_alignment(0.0, 0.5)
117
- label2.text = _('of the following rules:')
130
+ label2.text = _("of the following rules:")
118
131
 
119
132
  @rules_header_box.pack_start(label1, expand: false, fill: false)
120
133
  @rules_header_box.pack_start(cb, expand: false, fill: false)
@@ -122,7 +135,7 @@ module Alexandria
122
135
  else
123
136
  label = Gtk::Label.new
124
137
  label.set_alignment(0.0, 0.5)
125
- label.text = _('Match the following rule:')
138
+ label.text = _("Match the following rule:")
126
139
  @rules_header_box << label
127
140
  @predicate_operator_rule = SmartLibrary::ALL_RULES
128
141
  end
@@ -130,149 +143,59 @@ module Alexandria
130
143
  @rules_header_box.show_all
131
144
  end
132
145
 
133
- def insert_new_rule(rule = nil)
134
- rule_box = Gtk::Box.new :horizontal
135
- rule_box.spacing = 8
136
-
137
- left_operand_combo = Gtk::ComboBoxText.new
138
- operator_combo = Gtk::ComboBoxText.new
139
-
140
- value_entry = Gtk::Entry.new
141
-
142
- date_entry = Gtk::Entry.new
143
- date_entry.primary_icon_name = Gtk::Stock::EDIT
144
- date_entry.primary_icon_activatable = true
145
- date_entry.signal_connect('icon-press') do |entry, primary, _icon|
146
- if primary.nick == 'primary'
147
- display_calendar_popup(entry)
148
- end
149
- end
150
-
151
- # Really hide the time part of the date entry, as the constructor
152
- # does not seem to do it...
153
- # ##date_entry.children[2..3].each { |x| date_entry.remove(x) }
154
- # ##date_entry.spacing = 8
155
- entry_label = Gtk::Label.new('')
156
-
157
- add_button = Gtk::Button.new(label: '')
158
- add_button.remove(add_button.children.first)
159
- add_button << Gtk::Image.new(stock: Gtk::Stock::ADD,
160
- size: Gtk::IconSize::BUTTON)
161
-
162
- add_button.signal_connect('clicked') { insert_new_rule }
163
-
164
- remove_button = Gtk::Button.new(label: '')
165
- remove_button.remove(remove_button.children.first)
166
- remove_button << Gtk::Image.new(stock: Gtk::Stock::REMOVE,
167
- size: Gtk::IconSize::BUTTON)
168
-
169
- remove_button.signal_connect('clicked') do |_button|
170
- idx = @rules_box.children.index(rule_box)
171
- raise if idx.nil?
172
- @smart_library_rules.delete_at(idx)
173
- @rules_box.remove(rule_box)
174
- sensitize_remove_rule_buttons
175
- update_rules_header_box
176
- end
177
-
178
- operands = SmartLibrary::Rule::Operands::LEFT
179
- operands.each do |operand|
180
- left_operand_combo.append_text(operand.name)
181
- end
182
- operator_combo.signal_connect('changed') do
183
- operand = operands[left_operand_combo.active]
184
- operations = SmartLibrary::Rule.operations_for_operand(operand)
185
- operation = operations[operator_combo.active]
186
-
187
- value_entry.visible = date_entry.visible = entry_label.visible = false
188
- right_operand = operation.last
189
- unless right_operand.nil?
190
- entry = case right_operand.klass.name
191
- when 'Time'
192
- date_entry
193
- else
194
- value_entry
195
- end
196
- entry.visible = true
197
- unless right_operand.name.nil?
198
- entry_label.text = right_operand.name
199
- entry_label.visible = true
200
- end
201
- end
202
-
203
- idx = @rules_box.children.index(rule_box)
204
- new_rule = @smart_library_rules[idx]
205
- if new_rule.nil?
206
- new_rule = SmartLibrary::Rule.new(operand,
207
- operation.first,
208
- nil)
209
- @smart_library_rules << new_rule
210
- end
211
- new_rule.operand = operand
212
- new_rule.operation = operation.first
213
- new_rule.value = nil
214
- end
215
- left_operand_combo.signal_connect('changed') do
216
- operand = operands[left_operand_combo.active]
217
- operator_combo.freeze_notify do
218
- operator_combo.remove_all
219
- operations = SmartLibrary::Rule.operations_for_operand(operand)
220
- operations.each do |operation|
221
- operator = operation.first
222
- operator_combo.append_text(operator.name)
223
- end
224
- operator_combo.active = 0
225
- end
226
- end
227
-
228
- rule_box.pack_start(left_operand_combo, expand: false, fill: false)
229
- rule_box.pack_start(operator_combo, expand: false, fill: false)
230
- rule_box.pack_start(value_entry)
231
- rule_box.pack_start(date_entry)
232
- rule_box.pack_start(entry_label, expand: false, fill: false)
233
- rule_box.pack_end(remove_button, expand: false, fill: false)
234
- rule_box.pack_end(add_button, expand: false, fill: false)
235
-
146
+ def make_rule_box(rule = nil)
147
+ box_controller = SmartLibraryRuleBox.new self
148
+ rule_box = box_controller.rule_box
236
149
  rule_box.show_all
237
- value_entry.visible = date_entry.visible = entry_label.visible = false
238
-
239
150
  @rules_box.pack_start(rule_box, expand: false, fill: true)
240
151
 
241
152
  if rule
153
+ operands = SmartLibrary::Rule::Operands::LEFT
242
154
  operand_idx = operands.index(rule.operand)
243
155
  operations =
244
156
  SmartLibrary::Rule.operations_for_operand(rule.operand)
245
157
  operation_idx = operations.map(&:first).index(rule.operation)
246
158
 
247
159
  if !operand_idx.nil? && !operation_idx.nil?
248
- left_operand_combo.active = operand_idx
249
- operator_combo.active = operation_idx
160
+ box_controller.left_operand_combo.active = operand_idx
161
+ box_controller.operator_combo.active = operation_idx
250
162
  unless rule.value.nil?
251
163
  case rule.value
252
164
  when String
253
- value_entry.text = rule.value
165
+ box_controller.value_entry.text = rule.value
254
166
  when Time
255
- date_entry.text = format_date(rule.value)
167
+ box_controller.date_entry.text = format_date(rule.value)
256
168
  end
257
169
  end
258
170
  end
259
171
  else
260
- left_operand_combo.active = 0
172
+ box_controller.left_operand_combo.active = 0
261
173
  end
174
+ end
262
175
 
176
+ def insert_new_rule(rule = nil)
177
+ make_rule_box(rule)
263
178
  @rules_box.check_resize # force a layout
264
179
  update_rules_header_box
265
180
  sensitize_remove_rule_buttons
266
181
  end
267
182
 
183
+ def remove_rule_box(rule_box)
184
+ idx = @rules_box.children.index(rule_box)
185
+ raise if idx.nil?
186
+
187
+ smart_library_rules.delete_at(idx)
188
+ @rules_box.remove(rule_box)
189
+ sensitize_remove_rule_buttons
190
+ update_rules_header_box
191
+ end
192
+
268
193
  def sensitize_remove_rule_buttons
269
194
  boxes = @rules_box.children
270
195
  state = boxes.length > 1
271
196
  boxes.each do |box|
272
197
  button = box.children[-1]
273
- if button.is_a?(Gtk::Button)
274
- button.sensitive = state
275
- end
198
+ button.sensitive = state if button.is_a?(Gtk::Button)
276
199
  end
277
200
  end
278
201
 
@@ -285,7 +208,7 @@ module Alexandria
285
208
  elsif date.visible?
286
209
  begin
287
210
  value = parse_date(date.text)
288
- rescue => ex
211
+ rescue StandardError => ex
289
212
  trace = ex.backtrace.join("\n > ")
290
213
  log.warn { "Possibly invalid date entered #{ex.message}" }
291
214
  log.warn { "Date widget returned #{date.text} / #{trace}" }
@@ -294,7 +217,7 @@ module Alexandria
294
217
  value = Time.now
295
218
  end
296
219
  end
297
- @smart_library_rules[i].value = value
220
+ smart_library_rules[i].value = value
298
221
  end
299
222
  end
300
223
 
@@ -309,14 +232,14 @@ module Alexandria
309
232
  @calendar_popup.skip_pager_hint = true
310
233
  @calendar_popup.events = [:focus_change_mask]
311
234
 
312
- @calendar_popup.set_transient_for(self)
235
+ @calendar_popup.set_transient_for(@dialog)
313
236
  @calendar_popup.set_type_hint(:dialog)
314
- @calendar_popup.name = 'calendar-popup'
237
+ @calendar_popup.name = "calendar-popup"
315
238
  @calendar_popup.resizable = false
316
239
  # @calendar_popup.border_width = 4
317
240
  # @calendar_popup.app_paintable = true
318
241
 
319
- @calendar_popup.signal_connect('focus-out-event') do |_popup, _event|
242
+ @calendar_popup.signal_connect("focus-out-event") do |_popup, _event|
320
243
  hide_calendar_popup
321
244
  false
322
245
  end
@@ -324,7 +247,7 @@ module Alexandria
324
247
  @calendar = Gtk::Calendar.new
325
248
  @calendar_popup.add(@calendar)
326
249
 
327
- @calendar.signal_connect('day-selected') do
250
+ @calendar.signal_connect("day-selected") do
328
251
  date_arr = @calendar.date
329
252
  year = date_arr[0]
330
253
  month = date_arr[1] # + 1 # gtk : months 0-indexed, Time.gm : 1-index
@@ -335,7 +258,7 @@ module Alexandria
335
258
  end
336
259
  end
337
260
 
338
- @calendar.signal_connect('day-selected-double-click') do
261
+ @calendar.signal_connect("day-selected-double-click") do
339
262
  date_arr = @calendar.date
340
263
  year = date_arr[0]
341
264
  month = date_arr[1] # + 1 # gtk : months 0-indexed, Time.gm : 1-index
@@ -404,18 +327,19 @@ module Alexandria
404
327
  end
405
328
 
406
329
  def parse_date(datestring)
407
- date_format = '%d/%m/%Y' # or '%m/%d/%Y' for USA and Canada ; or '%Y-%m-%d' for most of Asia
408
- ## http://en.wikipedia.org/wiki/Calendar_date#Middle_endian_forms.2C_starting_with_the_month
330
+ # '%m/%d/%Y' for USA and Canada ; or '%Y-%m-%d' for most of Asia
331
+ # http://en.wikipedia.org/wiki/Calendar_date#Middle_endian_forms.2C_starting_with_the_month
332
+ date_format = "%d/%m/%Y"
409
333
  begin
410
334
  d = Date.strptime(datestring, date_format)
411
335
  Time.gm(d.year, d.month, d.day)
412
- rescue
336
+ rescue StandardError
413
337
  nil
414
338
  end
415
339
  end
416
340
 
417
341
  def format_date(datetime)
418
- datetime.strftime('%d/%m/%Y')
342
+ datetime.strftime("%d/%m/%Y")
419
343
  end
420
344
  end
421
345
  end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Alexandria.
4
+ #
5
+ # See the file README.md for authorship and licensing information.
6
+
7
+ module Alexandria
8
+ module UI
9
+ class SmartLibraryRuleBox
10
+ attr_accessor :rule_box,
11
+ :left_operand_combo, :operator_combo,
12
+ :value_entry, :date_entry, :entry_label,
13
+ :add_button, :remove_button
14
+
15
+ def initialize(parent)
16
+ @parent = parent
17
+
18
+ self.rule_box = Gtk::Box.new :horizontal
19
+ rule_box.spacing = 8
20
+
21
+ self.left_operand_combo = Gtk::ComboBoxText.new
22
+ self.operator_combo = Gtk::ComboBoxText.new
23
+
24
+ self.value_entry = Gtk::Entry.new
25
+
26
+ self.date_entry = Gtk::Entry.new.tap do |entry|
27
+ entry.primary_icon_name = Gtk::Stock::EDIT
28
+
29
+ entry.primary_icon_activatable = true
30
+ entry.signal_connect("icon-press") do |widget, primary, _icon|
31
+ @parent.handle_date_icon_press(widget, primary, icon)
32
+ end
33
+ end
34
+
35
+ self.entry_label = Gtk::Label.new("")
36
+
37
+ self.add_button = Gtk::Button.new(label: "").tap do |widget|
38
+ widget.remove(widget.children.first)
39
+ widget << Gtk::Image.new(stock: Gtk::Stock::ADD,
40
+ size: Gtk::IconSize::BUTTON)
41
+
42
+ widget.signal_connect("clicked") { @parent.handle_add_rule_clicked }
43
+ end
44
+
45
+ self.remove_button = Gtk::Button.new(label: "")
46
+ remove_button.remove(remove_button.children.first)
47
+ remove_button << Gtk::Image.new(stock: Gtk::Stock::REMOVE,
48
+ size: Gtk::IconSize::BUTTON)
49
+
50
+ remove_button.signal_connect("clicked") do |_button|
51
+ @parent.handle_remove_rule_clicked(self)
52
+ end
53
+
54
+ operands.each do |operand|
55
+ left_operand_combo.append_text(operand.name)
56
+ end
57
+
58
+ operator_combo.signal_connect("changed") do
59
+ handle_operator_changed
60
+ end
61
+
62
+ left_operand_combo.signal_connect("changed") do
63
+ handle_left_operand_changed
64
+ end
65
+
66
+ rule_box.pack_start(left_operand_combo, expand: false, fill: false)
67
+ rule_box.pack_start(operator_combo, expand: false, fill: false)
68
+ rule_box.pack_start(value_entry)
69
+ rule_box.pack_start(date_entry)
70
+ rule_box.pack_start(entry_label, expand: false, fill: false)
71
+ rule_box.pack_end(remove_button, expand: false, fill: false)
72
+ rule_box.pack_end(add_button, expand: false, fill: false)
73
+
74
+ value_entry.visible = date_entry.visible = entry_label.visible = false
75
+ end
76
+
77
+ def operands
78
+ SmartLibrary::Rule::Operands::LEFT
79
+ end
80
+
81
+ def handle_operator_changed
82
+ operand = operands[left_operand_combo.active]
83
+ operations = SmartLibrary::Rule.operations_for_operand(operand)
84
+ operation = operations[operator_combo.active]
85
+
86
+ value_entry.visible = date_entry.visible = entry_label.visible = false
87
+ right_operand = operation.last
88
+ unless right_operand.nil?
89
+ entry = case right_operand.klass.name
90
+ when "Time"
91
+ date_entry
92
+ else
93
+ value_entry
94
+ end
95
+ entry.visible = true
96
+ unless right_operand.name.nil?
97
+ entry_label.text = right_operand.name
98
+ entry_label.visible = true
99
+ end
100
+ end
101
+
102
+ @parent.apply_smart_rule_for_rule_box(rule_box, operand, operation)
103
+ end
104
+
105
+ def handle_left_operand_changed
106
+ operand = operands[left_operand_combo.active]
107
+ operator_combo.freeze_notify do
108
+ operator_combo.remove_all
109
+ operations = SmartLibrary::Rule.operations_for_operand(operand)
110
+ operations.each do |operation|
111
+ operator = operation.first
112
+ operator_combo.append_text(operator.name)
113
+ end
114
+ operator_combo.active = 0
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end