b77c138ab2f6a150414b810738fc846f9e234c2a
[cinaest] / src / movie-list-store.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
21 public class MovieListStore : ListStore, TreeModel {
22         public enum Columns {
23                 TITLE,
24                 YEAR,
25                 RATING,
26                 POSTER,
27                 MOVIE,
28                 N_COLUMNS
29         }
30         private GLib.Type[] types = {
31                 typeof (string),
32                 typeof (int),
33                 typeof (int),
34                 typeof (Gdk.Pixbuf),
35                 typeof (Movie)
36         };
37         private GLib.Type[] base_type = {
38                 typeof (Movie)
39         };
40         private Gdk.Pixbuf no_poster;
41         private MoviePoster.Factory poster_factory;
42         public MovieSource source;
43         private MovieFilter filter;
44         public bool update_running { get; set; }
45         private Cancellable cancellable;
46
47         public signal void search_finished (int movies);
48
49         construct {
50                 set_column_types (base_type);
51                 no_poster = null;
52                 source = null;
53                 update_running = false;
54
55                 poster_factory = MoviePoster.Factory.get_instance ();
56         }
57
58         public void add (Movie movie, out TreeIter iter) {
59                 TreeIter iter1;
60
61                 append (out iter1);
62                 base.set (iter1, 0, movie);
63
64                 movie.notify.connect (this.on_movie_changed);
65
66                 iter = iter1;
67         }
68
69         public new bool remove (Movie movie) {
70                 TreeIter iter;
71
72                 if (get_iter_from_movie (out iter, movie)) {
73                         movie.notify.disconnect (this.on_movie_changed);
74                         base.remove (iter);
75
76                         if (source.get_editable ()) {
77                                 source.delete_movie (movie);
78                         }
79
80                         return true;
81                 }
82
83                 return false;
84         }
85
86         private void on_movie_changed (GLib.Object source, GLib.ParamSpec spec) {
87                 var movie = (Movie) source;
88
89                 TreeIter iter;
90                 if (get_iter_from_movie (out iter, movie)) {
91                         TreePath path = get_path (iter);
92                         base.row_changed (path, iter);
93                 }
94         }
95
96         public bool get_editable () {
97                 return source.get_editable ();
98         }
99
100         public bool get_iter_from_movie (out TreeIter iter, Movie movie_a) {
101                 if (get_iter_first (out iter)) {
102                         do {
103                                 Movie movie_b;
104                                 get (iter, Columns.MOVIE, out movie_b);
105                                 if (movie_a == movie_b)
106                                         return true;
107                         } while (iter_next (ref iter));
108                 }
109                 return false;
110         }
111
112         public bool start_search (MovieFilter _filter) {
113                 if (update_running) {
114                         stdout.printf ("aborting search ...\n");
115                         cancellable.cancel ();
116                         poster_factory.clear_queue ();
117                         return false;
118                 }
119                 if (cancellable == null || cancellable.is_cancelled ())
120                         cancellable = new Cancellable ();
121
122                 filter = _filter;
123                 stdout.printf ("begin search\n");
124                 search_async.begin ();
125                 update_running = true;
126                 return true;
127         }
128
129         // Asynchronous update method
130         private async void search_async () {
131                 stdout.printf ("search started: \"%s\"\n", filter.title);
132
133                 clear ();
134
135                 if (source != null) {
136                         // FIXME - arbitrary limit
137                         int n = yield source.get_movies (filter, receive_movie, 100, cancellable);
138                         search_finished (n);
139                 }
140
141                 update_running = false;
142                 if (cancellable.is_cancelled ()) {
143                         stdout.printf ("search aborted, starting new\n");
144                         cancellable.reset ();
145                         if (cancellable.is_cancelled ()) {
146                                 stdout.printf ("OW WEY\n");
147                         }
148                         start_search (filter);
149                 } else {
150                         stdout.printf ("search stopped\n");
151                 }
152         }
153
154         private void receive_movie (Movie movie) {
155                 TreeIter iter;
156
157                 if (cancellable.is_cancelled ())
158                         return;
159
160                 add (movie, out iter);
161                 try {
162                         poster_factory.queue_thumbnail (movie, 64, 64, false, receive_poster_thumbnail);
163                 } catch (Error e) {
164                         warning ("Failed to queue poster request: %s\n", e.message);
165                 }
166         }
167
168         private void receive_poster_thumbnail (Gdk.Pixbuf pixbuf, Movie movie) {
169                 var poster = new Poster ();
170                 poster.thumbnail = pixbuf;
171                 movie.poster = poster;
172         }
173
174         // Implement TreeModel interface
175         public virtual GLib.Type get_column_type (int index_) {
176                 return_val_if_fail (index_ >= 0 && index_ < Columns.N_COLUMNS, 0);
177
178                 return types[index_];
179         }
180
181         public virtual int get_n_columns () {
182                 return Columns.N_COLUMNS;
183         }
184
185         public virtual void get_value (TreeIter iter, int column, out GLib.Value value) {
186                 Movie movie;
187
188                 // FIXME
189                 if (no_poster == null) try {
190                         no_poster = new Gdk.Pixbuf.from_file ("/usr/share/icons/hicolor/64x64/hildon/general_video.png");
191                 } catch (Error e) {
192                         critical ("Missing general_video icon: %s\n", e.message);
193                 }
194
195                 return_if_fail (column >= 0 && column < Columns.N_COLUMNS);
196
197                 // Get the Movie from our parent's storage
198                 Value val;
199                 base.get_value (iter, 0, out val);
200                 movie = (Movie) val.get_object ();
201
202                 value.init (get_column_type (column));
203
204                 switch (column) {
205                 case Columns.TITLE:
206                         if (movie != null) {
207                                 value.set_string (movie.title);
208                         } else {
209                                 value.set_string ("");
210                         }
211                         break;
212
213                 case Columns.YEAR:
214                         if (movie != null) {
215                                 value.set_int (movie.year);
216                         } else {
217                                 value.set_int (-1);
218                         }
219                         break;
220
221                 case Columns.RATING:
222                         if (movie != null) {
223                                 value.set_int (movie.rating);
224                         } else {
225                                 value.set_int (-1);
226                         }
227                         break;
228
229                 case Columns.POSTER:
230                         if ((movie.poster != null) && (movie.poster.thumbnail != null))
231                                 value.set_object (movie.poster.thumbnail);
232                         else
233                                 value.set_object (no_poster);
234                         break;
235
236                 case Columns.MOVIE:
237                         value.set_object (movie);
238                         break;
239
240                 default:
241                         assert_not_reached ();
242                 }
243         }
244 }