Catalog plugin: add WatchedSource, a CatalogSource with rating flag enabled
[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 WatchedSource (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) && s.table != "Watched") {
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 {
134                                 source.add_movie (movie);
135
136                                 var banner = (Banner) Banner.show_information_with_markup (window, null, _("'%s' added to list '%s'").printf (movie.title, source.get_name ()));
137                                 banner.set_timeout (1500);
138                         }
139                 }
140                 dialog.destroy ();
141                 dialog = null;
142         }
143
144         private void on_source_activated (MovieSource source) {
145                 int n = sources.index ((CatalogSource) source);
146
147                 dialog.response (n);
148         }
149
150         public override void settings_dialog (Gtk.Window window) {
151                 var dialog = new Gtk.Dialog ();
152                 dialog.set_transient_for (window);
153                 dialog.set_title (_("Catalog plugin settings"));
154
155                 var button = new Hildon.Button (SizeType.FINGER_HEIGHT, ButtonArrangement.VERTICAL);
156                 button.set_title (_("Select active movie lists"));
157                 button.set_value (active_sources_text ());
158                 button.set_style (ButtonStyle.PICKER);
159
160                 var content = (VBox) dialog.get_content_area ();
161                 content.pack_start (button, true, true, 0);
162
163                 dialog.add_button (_("Done"), ResponseType.ACCEPT);
164
165                 // Connect signals
166                 button.clicked.connect (() => { on_select_active_lists (button, window); });
167
168                 dialog.show_all ();
169                 int res = dialog.run ();
170                 if (res == ResponseType.ACCEPT) {
171                 }
172                 dialog.destroy ();
173         }
174
175         private void on_select_active_lists (Hildon.Button button, Gtk.Window window) {
176                 dialog = new Gtk.Dialog ();
177                 dialog.set_transient_for (window);
178                 dialog.set_title (_("Select active movie lists"));
179
180                 var source_list = new SourceListView (sources, false, window);
181                 source_list.set_hildon_ui_mode (UIMode.EDIT);
182
183                 var selection = source_list.get_selection ();
184                 foreach (CatalogSource s in sources) {
185                         var iter = TreeIter ();
186
187                         if (s.active && source_list.get_iter (s, out iter)) {
188                                 selection.select_iter (iter);
189                         }
190                 }
191
192                 var content = (VBox) dialog.get_content_area ();
193                 content.pack_start (source_list, true, true, 0);
194                 var i = sources.length ();
195                 if (i > 5)
196                         i = 5;
197                 content.set_size_request (-1, (int) i*70);
198
199                 dialog.add_button (_("Done"), ResponseType.ACCEPT);
200
201                 dialog.show_all ();
202                 int res = dialog.run ();
203                 if (res == ResponseType.ACCEPT) {
204                         foreach (CatalogSource s in sources) {
205                                 TreeIter iter;
206
207                                 if (source_list.get_iter (s, out iter)) {
208                                         s.active = selection.iter_is_selected (iter);
209                                 }
210                         }
211
212                         var config_file = Path.build_filename (Environment.get_user_config_dir (), "cinaest", "cinaest.cfg");
213                         var keyfile = new KeyFile ();
214                         try {
215                                 keyfile.load_from_file (config_file, KeyFileFlags.NONE);
216                         } catch (Error e) {
217                                 if (!(e is KeyFileError.NOT_FOUND))
218                                         stdout.printf ("Error loading configuration: %s\n", e.message);
219                         }
220                         keyfile.set_string ("CatalogPlugin", "HiddenSources", hidden_sources_list ());
221
222                         try {
223                                 var file = File.new_for_path (config_file + ".part");
224                                 var stream = file.create (FileCreateFlags.REPLACE_DESTINATION, null);
225                                 var data = keyfile.to_data ();
226
227                                 stream.write (data, data.length, null);
228                                 FileUtils.rename (config_file + ".part", config_file);
229                         } catch (Error e) {
230                                 stdout.printf ("Failed to store configuration: %s\n", e.message);
231                         }
232
233                         button.set_value (active_sources_text ());
234                 }
235                 dialog.destroy ();
236                 dialog = null;
237         }
238
239         private string active_sources_text () {
240                 string text = null;
241
242                 foreach (CatalogSource s in sources) {
243                         if (s.active) {
244                                 if (text == null) {
245                                         text = s.get_name ();
246                                 } else {
247                                         text += ", " + s.get_name ();
248                                 }
249                         }
250                 }
251                 return text;
252         }
253
254         private string hidden_sources_list () {
255                 string list = "";
256
257                 foreach (CatalogSource s in sources) {
258                         if (!s.active) {
259                                 if (list == "") {
260                                         list = s.table;
261                                 } else {
262                                         list += ", " + s.table;
263                                 }
264                         }
265                 }
266                 return list;
267         }
268
269         public override unowned string get_name () {
270                 return _("Catalog");
271         }
272 }
273
274 class CatalogSource : MovieSource {
275         internal string table;
276         internal string name;
277         internal string description;
278         internal CatalogSqlite sqlite;
279
280         public CatalogSource (string _table, string _name, string _description, CatalogSqlite _sqlite, bool _active) {
281                 GLib.Object (active: _active);
282                 table = _table;
283                 name = _name;
284                 description = _description;
285                 sqlite = _sqlite;
286         }
287
288         public override bool active { get; set construct; }
289
290         public override async int get_movies (MovieFilter filter, MovieSource.ReceiveMovieFunction callback, int limit, Cancellable? cancellable) {
291                 int n = yield sqlite.query (table, filter, callback, limit, cancellable);
292                 return n;
293         }
294
295         public override void add_movie (Movie movie) {
296                 sqlite.add_movie (table, movie);
297         }
298
299         public override void delete_movie (Movie movie) {
300                 sqlite.delete_movie (table, movie);
301         }
302
303         internal bool contains (Movie movie) {
304                 return sqlite.contains (table, movie);
305         }
306
307         public override unowned string get_name () {
308                 return name;
309         }
310
311         public override unowned string get_description () {
312                 return description;
313         }
314
315         public override SourceFlags get_flags () {
316                 return SourceFlags.EDITABLE;
317         }
318 }
319
320 class WatchedSource : CatalogSource {
321         public WatchedSource (CatalogSqlite _sqlite, bool _active) {
322                 GLib.Object (active: _active);
323                 table = "Watched";
324                 name = _("Watched movies");
325                 description = _("Watched / rated movies");
326                 sqlite = _sqlite;
327         }
328
329         public override async int get_movies (MovieFilter filter, MovieSource.ReceiveMovieFunction callback, int limit, Cancellable? cancellable) {
330                 int n = yield sqlite.query (table, filter, callback, limit, cancellable);
331                 return n;
332         }
333
334         public override SourceFlags get_flags () {
335                 return SourceFlags.EDITABLE | SourceFlags.RATING;
336         }
337 }
338
339 [ModuleInit]
340 public Type register_plugin (TypeModule module) {
341         // types are registered automatically
342         return typeof (CatalogPlugin);
343 }