Plugins: replace get_editable with get_flags, add support more MovieSource flags
[cinaest] / src / plugins / catalog-plugin.vala
1 /* This file is part of Cinaest.
2  *
3  * Copyright (C) 2009 Philipp Zabel
4  *
5  * Cinaest is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * Cinaest is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Cinaest. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 using Gtk;
20 using Hildon;
21
22 class CatalogPlugin : Plugin {
23         List<CatalogSource> sources;
24         private CatalogSqlite sqlite;
25         private Gtk.Dialog dialog;
26
27         public override void hello (Gtk.Window window, Osso.Context context) {
28                 string data_dir = Path.build_filename (Environment.get_user_data_dir(), "cinaest");
29                 string filename = Path.build_filename (data_dir, "catalog.db");
30
31                 // Make sure the data directory is available
32                 DirUtils.create_with_parents (data_dir, 0770);
33
34                 string hidden_sources = null;
35                 try {
36                         var config_file = Path.build_filename (Environment.get_user_config_dir (), "cinaest", "cinaest.cfg");
37                         var keyfile = new KeyFile ();
38                         if (keyfile.load_from_file (config_file, KeyFileFlags.NONE)
39                             && keyfile.has_group ("CatalogPlugin")) {
40                                 if (keyfile.has_key ("CatalogPlugin", "HiddenSources")) {
41                                         hidden_sources = keyfile.get_string ("CatalogPlugin", "HiddenSources");
42                                 }
43                         }
44                 } catch (Error e) {
45                         if (!(e is KeyFileError.NOT_FOUND))
46                                 stdout.printf ("Error loading configuration: %s\n", e.message);
47                 }
48
49                 sqlite = new CatalogSqlite (filename);
50                 sources = new List<CatalogSource> ();
51
52                 var source = new CatalogSource ("Collection", _("Collection"), _("Personal movie list"), sqlite, !("Collection" in hidden_sources));
53                 sources.append (source);
54
55                 source = new CatalogSource ("Loaned", _("Loaned movies"), _("Movies loaned to friends"), sqlite, !("Loaned" in hidden_sources));
56                 sources.append (source);
57
58                 source = new CatalogSource ("Watched", _("Watched movies"), _("Watched / rated movies"), sqlite, !("Watched" in hidden_sources));
59                 sources.append (source);
60
61                 source = new CatalogSource ("Watchlist", _("Watchlist"), _("Movies of interest"), sqlite, !("Watchlist" in hidden_sources));
62                 sources.append (source);
63
64                 stdout.printf ("Catalog Plugin Loaded.\n");
65         }
66
67         public override unowned List<MovieSource> get_sources () {
68                 return (List<MovieSource>) sources;
69         }
70
71         public override List<MovieAction> get_actions (Movie movie, Gtk.Window window) {
72                 var list = new List<MovieAction> ();
73
74                 list.append (new MovieAction (_("Add to catalog"), on_add_to_catalog, movie, window));
75
76                 return list;
77         }
78
79         private void on_add_to_catalog (Movie movie, Gtk.Window window) {
80                 dialog = new Gtk.Dialog ();
81                 dialog.set_transient_for (window);
82                 dialog.set_title (_("Add movie to catalog - Select list"));
83
84                 int i = 0;
85                 var available_sources = new List<MovieSource> ();
86                 foreach (CatalogSource s in sources) {
87                         if (!s.contains (movie)) {
88                                 available_sources.append ((MovieSource) s);
89                                 i++;
90                         }
91                 }
92
93                 var source_list = new SourceListView (available_sources, true, window);
94
95                 var content = (VBox) dialog.get_content_area ();
96                 content.pack_start (source_list, true, true, 0);
97                 if (i > 5)
98                         i = 5;
99                 content.set_size_request (-1, i*70);
100
101                 // Connect signals
102                 source_list.source_activated.connect (on_source_activated);
103
104                 dialog.show_all ();
105                 int res = dialog.run ();
106                 if (res >= 0) {
107                         var source = sources.nth_data (res);
108
109                         if (source.table == "Loaned") {
110                                 var dialog = new Gtk.Dialog ();
111                                 dialog.set_title (_("Add to loaned movies"));
112
113                                 var contact = new Hildon.Entry (SizeType.FINGER_HEIGHT);
114                                 contact.set_placeholder ("Contact");
115                                 var date = new Hildon.DateButton (SizeType.FINGER_HEIGHT, ButtonArrangement.VERTICAL);
116                                 date.set_title (_("Loaned on"));
117                                 date.set_alignment (0.0f, 0.5f, 1.0f, 1.0f);
118
119                                 content = (Gtk.VBox) dialog.get_content_area ();
120                                 content.pack_start (contact, true, false, 0);
121                                 content.pack_start (date, true, false, 0);
122
123                                 dialog.add_button (_("Done"), Gtk.ResponseType.OK);
124                                 dialog.show_all ();
125                                 res = dialog.run ();
126                                 dialog.destroy ();
127                                 if (res == Gtk.ResponseType.OK) {
128                                         source.add_movie (movie);
129
130                                         var banner = (Banner) Banner.show_information_with_markup (window, null, _("'%s' added to list of loaned movies").printf (movie.title, source.get_name ()));
131                                         banner.set_timeout (1500);
132                                 }
133                         } else if (source.table == "Watched") {
134                                 var dialog = new Gtk.Dialog ();
135                                 dialog.set_title (_("Add to watched movies"));
136
137                                 var rating = new RatingWidget ();
138                                 var date = new Hildon.DateButton (SizeType.FINGER_HEIGHT, ButtonArrangement.VERTICAL);
139                                 date.set_title (_("Watched on"));
140                                 date.set_alignment (0.0f, 0.5f, 1.0f, 1.0f);
141
142                                 content = (Gtk.VBox) dialog.get_content_area ();
143                                 content.pack_start (rating, true, false, 0);
144                                 content.pack_start (date, true, false, 0);
145
146                                 dialog.add_button (_("Done"), Gtk.ResponseType.OK);
147                                 dialog.show_all ();
148                                 res = dialog.run ();
149                                 dialog.destroy ();
150                                 if (res == Gtk.ResponseType.OK) {
151                                         if (rating.get_rating () > 0)
152                                                 movie.rating = 10 * rating.get_rating ();
153                                         source.add_movie (movie);
154
155                                         var banner = (Banner) Banner.show_information_with_markup (window, null, _("'%s' added to list of watched movies").printf (movie.title, source.get_name ()));
156                                         banner.set_timeout (1500);
157                                 }
158                         } else {
159                                 source.add_movie (movie);
160
161                                 var banner = (Banner) Banner.show_information_with_markup (window, null, _("'%s' added to list '%s'").printf (movie.title, source.get_name ()));
162                                 banner.set_timeout (1500);
163                         }
164                 }
165                 dialog.destroy ();
166                 dialog = null;
167         }
168
169         private void on_source_activated (MovieSource source) {
170                 int n = sources.index ((CatalogSource) source);
171
172                 dialog.response (n);
173         }
174
175         public override void settings_dialog (Gtk.Window window) {
176                 var dialog = new Gtk.Dialog ();
177                 dialog.set_transient_for (window);
178                 dialog.set_title (_("Catalog plugin settings"));
179
180                 var button = new Hildon.Button (SizeType.FINGER_HEIGHT, ButtonArrangement.VERTICAL);
181                 button.set_title (_("Select active movie lists"));
182                 button.set_value (active_sources_text ());
183                 button.set_style (ButtonStyle.PICKER);
184
185                 var content = (VBox) dialog.get_content_area ();
186                 content.pack_start (button, true, true, 0);
187
188                 dialog.add_button (_("Done"), ResponseType.ACCEPT);
189
190                 // Connect signals
191                 button.clicked.connect (() => { on_select_active_lists (button, window); });
192
193                 dialog.show_all ();
194                 int res = dialog.run ();
195                 if (res == ResponseType.ACCEPT) {
196                 }
197                 dialog.destroy ();
198         }
199
200         private void on_select_active_lists (Hildon.Button button, Gtk.Window window) {
201                 dialog = new Gtk.Dialog ();
202                 dialog.set_transient_for (window);
203                 dialog.set_title (_("Select active movie lists"));
204
205                 var source_list = new SourceListView (sources, false, window);
206                 source_list.set_hildon_ui_mode (UIMode.EDIT);
207
208                 var selection = source_list.get_selection ();
209                 foreach (CatalogSource s in sources) {
210                         var iter = TreeIter ();
211
212                         if (s.active && source_list.get_iter (s, out iter)) {
213                                 selection.select_iter (iter);
214                         }
215                 }
216
217                 var content = (VBox) dialog.get_content_area ();
218                 content.pack_start (source_list, true, true, 0);
219                 var i = sources.length ();
220                 if (i > 5)
221                         i = 5;
222                 content.set_size_request (-1, (int) i*70);
223
224                 dialog.add_button (_("Done"), ResponseType.ACCEPT);
225
226                 dialog.show_all ();
227                 int res = dialog.run ();
228                 if (res == ResponseType.ACCEPT) {
229                         foreach (CatalogSource s in sources) {
230                                 TreeIter iter;
231
232                                 if (source_list.get_iter (s, out iter)) {
233                                         s.active = selection.iter_is_selected (iter);
234                                 }
235                         }
236
237                         var config_file = Path.build_filename (Environment.get_user_config_dir (), "cinaest", "cinaest.cfg");
238                         var keyfile = new KeyFile ();
239                         try {
240                                 keyfile.load_from_file (config_file, KeyFileFlags.NONE);
241                         } catch (Error e) {
242                                 if (!(e is KeyFileError.NOT_FOUND))
243                                         stdout.printf ("Error loading configuration: %s\n", e.message);
244                         }
245                         keyfile.set_string ("CatalogPlugin", "HiddenSources", hidden_sources_list ());
246
247                         try {
248                                 var file = File.new_for_path (config_file + ".part");
249                                 var stream = file.create (FileCreateFlags.REPLACE_DESTINATION, null);
250                                 var data = keyfile.to_data ();
251
252                                 stream.write (data, data.length, null);
253                                 FileUtils.rename (config_file + ".part", config_file);
254                         } catch (Error e) {
255                                 stdout.printf ("Failed to store configuration: %s\n", e.message);
256                         }
257
258                         button.set_value (active_sources_text ());
259                 }
260                 dialog.destroy ();
261                 dialog = null;
262         }
263
264         private string active_sources_text () {
265                 string text = null;
266
267                 foreach (CatalogSource s in sources) {
268                         if (s.active) {
269                                 if (text == null) {
270                                         text = s.get_name ();
271                                 } else {
272                                         text += ", " + s.get_name ();
273                                 }
274                         }
275                 }
276                 return text;
277         }
278
279         private string hidden_sources_list () {
280                 string list = "";
281
282                 foreach (CatalogSource s in sources) {
283                         if (!s.active) {
284                                 if (list == "") {
285                                         list = s.table;
286                                 } else {
287                                         list += ", " + s.table;
288                                 }
289                         }
290                 }
291                 return list;
292         }
293
294         public override unowned string get_name () {
295                 return _("Catalog");
296         }
297 }
298
299 class CatalogSource : MovieSource {
300         internal string table;
301         private string name;
302         private string description;
303         private CatalogSqlite sqlite;
304
305         public CatalogSource (string _table, string _name, string _description, CatalogSqlite _sqlite, bool _active) {
306                 GLib.Object (active: _active);
307                 table = _table;
308                 name = _name;
309                 description = _description;
310                 sqlite = _sqlite;
311         }
312
313         public override bool active { get; set construct; }
314
315         public override async int get_movies (MovieFilter filter, MovieSource.ReceiveMovieFunction callback, int limit, Cancellable? cancellable) {
316                 int n = yield sqlite.query (table, filter, callback, limit, cancellable);
317                 return n;
318         }
319
320         public override void add_movie (Movie movie) {
321                 sqlite.add_movie (table, movie);
322         }
323
324         public override void delete_movie (Movie movie) {
325                 sqlite.delete_movie (table, movie);
326         }
327
328         internal bool contains (Movie movie) {
329                 return sqlite.contains (table, movie);
330         }
331
332         public override unowned string get_name () {
333                 return name;
334         }
335
336         public override unowned string get_description () {
337                 return description;
338         }
339
340         public override SourceFlags get_flags () {
341                 return SourceFlags.EDITABLE;
342         }
343 }
344
345 [ModuleInit]
346 public Type register_plugin (TypeModule module) {
347         // types are registered automatically
348         return typeof (CatalogPlugin);
349 }