Make search thread cancellable
[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         public MovieSource source;
42         private MovieFilter filter;
43         public bool update_running { get; set; }
44         private weak Thread thread;
45         private Cancellable cancellable;
46
47         construct {
48                 set_column_types (base_type);
49                 no_poster = null;
50                 source = null;
51                 update_running = false;
52         }
53
54         public void add (Movie movie, out TreeIter iter) {
55                 TreeIter iter1;
56
57                 append (out iter1);
58                 base.set (iter1, 0, movie);
59
60                 movie.notify.connect ((source, property) => { on_movie_changed(source); });
61
62                 iter = iter1;
63         }
64
65         private void on_movie_changed (GLib.Object source) {
66                 Movie movie = (Movie) source;
67
68                 TreeIter iter;
69                 if (get_iter_from_movie (out iter, movie)) {
70                         TreePath path = get_path (iter);
71                         base.row_changed (path, iter);
72                 }
73         }
74
75         public bool get_iter_from_movie (out TreeIter iter, Movie movie_a) {
76                 if (get_iter_first (out iter)) {
77                         do {
78                                 Movie movie_b;
79                                 get (iter, Columns.MOVIE, out movie_b);
80                                 if (movie_a == movie_b)
81                                         return true;
82                         } while (iter_next (ref iter));
83                 }
84                 return false;
85         }
86
87         public bool start_search (MovieFilter _filter) {
88                 if (update_running) {
89                         stdout.printf ("aborting search thread (%p)...\n", thread);
90                         cancellable.cancel ();
91                 //      poster_factory.clear_queue ();
92                         thread.join ();
93                         stdout.printf ("search thread aborted\n");
94                 }
95                 if (cancellable == null || cancellable.is_cancelled ())
96                         cancellable = new Cancellable ();
97
98                 filter = _filter;
99                 try {
100                         thread = Thread.create (search_thread, true);
101                         update_running = true;
102                 } catch (ThreadError e) {
103                         warning ("Failed to start search thread: %s", e.message);
104                 }
105                 return update_running;
106         }
107
108         // Update thread
109         private void* search_thread () {
110                 stdout.printf ("search thread started: \"%s\"\n", filter.title);
111
112                 Gdk.threads_enter ();
113                 clear ();
114                 Gdk.threads_leave ();
115
116                 if (source != null)
117                         // FIXME - arbitrary limit
118                         source.get_movies (filter, receive_movie, 100, cancellable);
119
120                 Gdk.threads_enter ();
121                 update_running = false;
122                 Gdk.threads_leave ();
123
124                 stdout.printf ("search thread stopped\n");
125                 return null;
126         }
127
128         private void receive_movie (Movie movie) {
129                 TreeIter iter;
130
131                 if (cancellable.is_cancelled ())
132                         return;
133
134                 Gdk.threads_enter ();
135                 add (movie, out iter);
136                 Gdk.threads_leave ();
137         }
138
139         // Implement TreeModel interface
140         public virtual GLib.Type get_column_type (int index_) {
141                 return_val_if_fail (index_ >= 0 && index_ < Columns.N_COLUMNS, 0);
142
143                 return types[index_];
144         }
145
146         public virtual int get_n_columns () {
147                 return Columns.N_COLUMNS;
148         }
149
150         public virtual void get_value (TreeIter iter, int column, out GLib.Value value) {
151                 Movie movie;
152
153                 // FIXME
154                 if (no_poster == null) try {
155                         no_poster = new Gdk.Pixbuf.from_file ("/usr/share/icons/hicolor/64x64/hildon/general_video.png");
156                 } catch (Error e) {
157                         critical ("Missing general_video icon: %s\n", e.message);
158                 }
159
160                 return_if_fail (column >= 0 && column < Columns.N_COLUMNS);
161
162                 // Get the Movie from our parent's storage
163                 Value val;
164                 base.get_value (iter, 0, out val);
165                 movie = (Movie) val.get_object ();
166
167                 value.init (get_column_type (column));
168
169                 switch (column) {
170                 case Columns.TITLE:
171                         if (movie != null) {
172                                 value.set_string (movie.title);
173                         } else {
174                                 value.set_string ("");
175                         }
176                         break;
177
178                 case Columns.YEAR:
179                         if (movie != null) {
180                                 value.set_int (movie.year);
181                         } else {
182                                 value.set_int (-1);
183                         }
184                         break;
185
186                 case Columns.RATING:
187                         if (movie != null) {
188                                 value.set_int (movie.rating);
189                         } else {
190                                 value.set_int (-1);
191                         }
192                         break;
193
194                 case Columns.POSTER:
195                         if ((movie.poster != null) && (movie.poster.thumbnail != null))
196                                 value.set_object (movie.poster.thumbnail);
197                         else
198                                 value.set_object (no_poster);
199                         break;
200
201                 case Columns.MOVIE:
202                         value.set_object (movie);
203                         break;
204
205                 default:
206                         assert_not_reached ();
207                 }
208         }
209 }