Catalog plugin: enable configuration of visible movie lists
[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 ("Watchlist", _("Watchlist"), _("Movies of interest"), sqlite, !("Watchlist" in hidden_sources));
59                 sources.append (source);
60
61                 stdout.printf ("Catalog Plugin Loaded.\n");
62         }
63
64         public override unowned List<MovieSource> get_sources () {
65                 return (List<MovieSource>) sources;
66         }
67
68         public override List<MovieAction> get_actions (Movie movie, Gtk.Window window) {
69                 var list = new List<MovieAction> ();
70
71                 list.append (new MovieAction (_("Add to catalog"), on_add_to_catalog, movie, window));
72
73                 return list;
74         }
75
76         private void on_add_to_catalog (Movie movie, Gtk.Window window) {
77                 dialog = new Gtk.Dialog ();
78                 dialog.set_transient_for (window);
79                 dialog.set_title (_("Add movie to catalog - Select list"));
80
81                 int i = 0;
82                 var available_sources = new List<MovieSource> ();
83                 foreach (CatalogSource s in sources) {
84                         if (!s.contains (movie)) {
85                                 available_sources.append ((MovieSource) s);
86                                 i++;
87                         }
88                 }
89
90                 var source_list = new SourceListView (available_sources, true);
91
92                 var content = (VBox) dialog.get_content_area ();
93                 content.pack_start (source_list, true, true, 0);
94                 if (i > 5)
95                         i = 5;
96                 content.set_size_request (-1, i*70);
97
98                 // Connect signals
99                 source_list.source_activated.connect (on_source_activated);
100
101                 dialog.show_all ();
102                 int res = dialog.run ();
103                 if (res >= 0) {
104                         var source = sources.nth_data (res);
105                         source.add_movie (movie);
106
107                         var banner = (Banner) Banner.show_information_with_markup (window, null, _("'%s' added to list '%s'").printf (movie.title, source.get_name ()));
108                         banner.set_timeout (1500);
109                 }
110                 dialog.destroy ();
111                 dialog = null;
112         }
113
114         private void on_source_activated (MovieSource source) {
115                 int n = sources.index ((CatalogSource) source);
116
117                 dialog.response (n);
118         }
119
120         public override void settings_dialog (Gtk.Window window) {
121                 var dialog = new Gtk.Dialog ();
122                 dialog.set_transient_for (window);
123                 dialog.set_title (_("Catalog plugin settings"));
124
125                 var button = new Hildon.Button (SizeType.FINGER_HEIGHT, ButtonArrangement.VERTICAL);
126                 button.set_title (_("Select active movie lists"));
127                 button.set_value (active_sources_text ());
128                 button.set_style (ButtonStyle.PICKER);
129
130                 var content = (VBox) dialog.get_content_area ();
131                 content.pack_start (button, true, true, 0);
132
133                 dialog.add_button (_("Done"), ResponseType.ACCEPT);
134
135                 // Connect signals
136                 button.clicked.connect (() => { on_select_active_lists (button, window); });
137
138                 dialog.show_all ();
139                 int res = dialog.run ();
140                 if (res == ResponseType.ACCEPT) {
141                 }
142                 dialog.destroy ();
143         }
144
145         private void on_select_active_lists (Hildon.Button button, Gtk.Window window) {
146                 dialog = new Gtk.Dialog ();
147                 dialog.set_transient_for (window);
148                 dialog.set_title (_("Select active movie lists"));
149
150                 var source_list = new SourceListView (sources, false);
151                 source_list.set_hildon_ui_mode (UIMode.EDIT);
152
153                 var selection = source_list.get_selection ();
154                 foreach (CatalogSource s in sources) {
155                         var iter = TreeIter ();
156
157                         if (s.active && source_list.get_iter (s, out iter)) {
158                                 selection.select_iter (iter);
159                         }
160                 }
161
162                 var content = (VBox) dialog.get_content_area ();
163                 content.pack_start (source_list, true, true, 0);
164                 var i = sources.length ();
165                 if (i > 5)
166                         i = 5;
167                 content.set_size_request (-1, (int) i*70);
168
169                 dialog.add_button (_("Done"), ResponseType.ACCEPT);
170
171                 dialog.show_all ();
172                 int res = dialog.run ();
173                 if (res == ResponseType.ACCEPT) {
174                         foreach (CatalogSource s in sources) {
175                                 TreeIter iter;
176
177                                 if (source_list.get_iter (s, out iter)) {
178                                         s.active = selection.iter_is_selected (iter);
179                                 }
180                         }
181
182                         var config_file = Path.build_filename (Environment.get_user_config_dir (), "cinaest", "cinaest.cfg");
183                         var keyfile = new KeyFile ();
184                         try {
185                                 keyfile.load_from_file (config_file, KeyFileFlags.NONE);
186                         } catch (Error e) {
187                                 if (!(e is KeyFileError.NOT_FOUND))
188                                         stdout.printf ("Error loading configuration: %s\n", e.message);
189                         }
190                         keyfile.set_string ("CatalogPlugin", "HiddenSources", hidden_sources_list ());
191
192                         try {
193                                 var file = File.new_for_path (config_file + ".part");
194                                 var stream = file.create (FileCreateFlags.REPLACE_DESTINATION, null);
195                                 var data = keyfile.to_data ();
196
197                                 stream.write (data, data.length, null);
198                                 FileUtils.rename (config_file + ".part", config_file);
199                         } catch (Error e) {
200                                 stdout.printf ("Failed to store configuration: %s\n", e.message);
201                         }
202
203                         button.set_value (active_sources_text ());
204                 }
205                 dialog.destroy ();
206                 dialog = null;
207         }
208
209         private string active_sources_text () {
210                 string text = null;
211
212                 foreach (CatalogSource s in sources) {
213                         if (s.active) {
214                                 if (text == null) {
215                                         text = s.get_name ();
216                                 } else {
217                                         text += ", " + s.get_name ();
218                                 }
219                         }
220                 }
221                 return text;
222         }
223
224         private string hidden_sources_list () {
225                 string list = "";
226
227                 foreach (CatalogSource s in sources) {
228                         if (!s.active) {
229                                 if (list == "") {
230                                         list = s.table;
231                                 } else {
232                                         list += ", " + s.table;
233                                 }
234                         }
235                 }
236                 return list;
237         }
238
239         public override unowned string get_name () {
240                 return _("Catalog");
241         }
242 }
243
244 class CatalogSource : MovieSource {
245         internal string table;
246         private string name;
247         private string description;
248         private CatalogSqlite sqlite;
249
250         public CatalogSource (string _table, string _name, string _description, CatalogSqlite _sqlite, bool _active) {
251                 GLib.Object (active: _active);
252                 table = _table;
253                 name = _name;
254                 description = _description;
255                 sqlite = _sqlite;
256         }
257
258         public override bool active { get; set construct; }
259
260         public override async void get_movies (MovieFilter filter, MovieSource.ReceiveMovieFunction callback, int limit, Cancellable? cancellable) {
261                 yield sqlite.query (table, filter, callback, limit, cancellable);
262         }
263
264         public override void add_movie (Movie movie) {
265                 sqlite.add_movie (table, movie);
266         }
267
268         public override void delete_movie (Movie movie) {
269                 sqlite.delete_movie (table, movie);
270         }
271
272         internal bool contains (Movie movie) {
273                 return sqlite.contains (table, movie);
274         }
275
276         public override unowned string get_name () {
277                 return name;
278         }
279
280         public override unowned string get_description () {
281                 return description;
282         }
283
284         public override bool get_editable () {
285                 return true;
286         }
287 }
288
289 [ModuleInit]
290 public Type register_plugin () {
291         // types are registered automatically
292         return typeof (CatalogPlugin);
293 }