Movie list store: use insert_with_values instead of append + set
[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                 ICON,
28                 MOVIE,
29                 MARKUP,
30                 N_COLUMNS
31         }
32         private GLib.Type[] types = {
33                 typeof (string),
34                 typeof (int),
35                 typeof (string),
36                 typeof (Gdk.Pixbuf),
37                 typeof (Gdk.Pixbuf),
38                 typeof (Movie),
39                 typeof (string)
40         };
41         private GLib.Type[] base_type = {
42                 typeof (Movie),
43                 typeof (string), // Markup: "Title (Year)"
44                 typeof (string)  // Rating
45         };
46         private Gdk.Pixbuf no_poster;
47         private MoviePoster.Factory poster_factory;
48         private MovieFilter filter;
49         public bool update_running { get; set; }
50         public string year_markup = "<span size=\"small\">[%d]</span>";
51         private Cancellable cancellable;
52         public Widget view;
53
54         public signal void search_finished (int movies);
55
56         private MovieSource _source;
57         public MovieSource source {
58                 get {
59                         return _source;
60                 }
61                 set {
62                         _source = value;
63                 }
64         }
65
66         construct {
67                 set_column_types (base_type);
68                 no_poster = null;
69                 source = null;
70                 update_running = false;
71
72                 poster_factory = MoviePoster.Factory.get_instance ();
73         }
74
75         public void add (Movie movie, out TreeIter iter) {
76                 TreeIter iter1;
77                 var markup = new StringBuilder ();
78                 markup.append (Markup.escape_text (movie.title));
79                 if (movie.year > 0) {
80                         markup.append (" ");
81                         markup.append_printf (year_markup, movie.year);
82                 }
83
84                 append (out iter1);
85
86                 base.insert_with_values (out iter1, -1,
87                                          0, movie,
88                                          1, markup.str,
89                                          2, (movie.rating >= 0) ? "%.1f".printf (movie.rating / 10.0) : null);
90
91                 movie.notify.connect (this.on_movie_changed);
92
93                 iter = iter1;
94         }
95
96         public new bool remove (Movie movie) {
97                 TreeIter iter;
98
99                 if (get_iter_from_movie (out iter, movie)) {
100                         movie.notify.disconnect (this.on_movie_changed);
101                         base.remove (iter);
102
103                         if (SourceFlags.EDITABLE in source.get_flags ()) {
104                                 source.delete_movie (movie);
105                         }
106
107                         return true;
108                 }
109
110                 return false;
111         }
112
113         private void on_movie_changed (GLib.Object source, GLib.ParamSpec spec) {
114                 var movie = (Movie) source;
115
116                 TreeIter iter;
117                 if (get_iter_from_movie (out iter, movie)) {
118                         TreePath path = get_path (iter);
119                         base.row_changed (path, iter);
120                 }
121         }
122
123         public bool get_editable () {
124                 return (SourceFlags.EDITABLE in source.get_flags ());
125         }
126
127         public bool get_iter_from_movie (out TreeIter iter, Movie movie_a) {
128                 if (get_iter_first (out iter)) {
129                         do {
130                                 Movie movie_b;
131                                 get (iter, Columns.MOVIE, out movie_b);
132                                 if (movie_a == movie_b)
133                                         return true;
134                         } while (iter_next (ref iter));
135                 }
136                 return false;
137         }
138
139         public bool start_search (MovieFilter _filter) {
140                 if (update_running) {
141                         stdout.printf ("aborting search ...\n");
142                         cancellable.cancel ();
143                         poster_factory.clear_queue ();
144                         return false;
145                 }
146                 if (cancellable == null || cancellable.is_cancelled ())
147                         cancellable = new Cancellable ();
148
149                 filter = _filter;
150                 stdout.printf ("begin search\n");
151                 search_async.begin ();
152                 update_running = true;
153                 return true;
154         }
155
156         // Asynchronous update method
157         private async void search_async () {
158                 stdout.printf ("search started: \"%s\"\n", filter.title);
159
160                 clear ();
161
162                 if (source != null) {
163                         // FIXME - arbitrary limit
164                         int n = yield source.get_movies (filter, receive_movie, 100, cancellable);
165                         search_finished (n);
166                 }
167
168                 update_running = false;
169                 if (cancellable.is_cancelled ()) {
170                         stdout.printf ("search aborted, starting new\n");
171                         cancellable.reset ();
172                         if (cancellable.is_cancelled ()) {
173                                 stdout.printf ("OW WEY\n");
174                         }
175                         start_search (filter);
176                 } else {
177                         stdout.printf ("search stopped\n");
178                 }
179         }
180
181         private void receive_movie (SList<Movie> movies) {
182                 TreeIter iter;
183
184                 if (cancellable.is_cancelled ())
185                         return;
186
187                 view.freeze_child_notify ();
188                 foreach (Movie movie in movies)
189                         add (movie, out iter);
190                 view.thaw_child_notify ();
191         }
192
193         private void receive_poster_icon (Gdk.Pixbuf pixbuf, Movie movie) {
194                 var poster = new Poster ();
195                 poster.icon = pixbuf;
196                 movie.poster = poster;
197         }
198
199         private void receive_poster_small (Gdk.Pixbuf pixbuf, Movie movie) {
200                 var poster = new Poster ();
201                 if (movie.poster != null && movie.poster.icon != null)
202                         poster.icon = movie.poster.icon;
203                 poster.small = pixbuf;
204                 movie.poster = poster;
205         }
206
207         // Implement TreeModel interface
208         public virtual GLib.Type get_column_type (int index_) {
209                 return_val_if_fail (index_ >= 0 && index_ < Columns.N_COLUMNS, 0);
210
211                 return types[index_];
212         }
213
214         public virtual int get_n_columns () {
215                 return Columns.N_COLUMNS;
216         }
217
218         public virtual void get_value (TreeIter iter, int column, out GLib.Value value) {
219                 Movie movie;
220
221                 return_if_fail (column >= 0 && column < Columns.N_COLUMNS);
222
223                 // Get the Movie from our parent's storage
224                 Value val;
225                 base.get_value (iter, 0, out val);
226                 movie = (Movie) val.get_object ();
227
228                 value.init (get_column_type (column));
229
230                 switch (column) {
231                 case Columns.TITLE:
232                         if (movie != null) {
233                                 value.set_string (movie.title);
234                         } else {
235                                 value.set_string ("");
236                         }
237                         break;
238
239                 case Columns.YEAR:
240                         if (movie != null) {
241                                 value.set_int (movie.year);
242                         } else {
243                                 value.set_int (-1);
244                         }
245                         break;
246
247                 case Columns.RATING:
248                         base.get_value (iter, 2, out value);
249                         break;
250
251                 case Columns.POSTER:
252                         if ((movie.poster != null) && (movie.poster.small != null)) {
253                                 value.set_object (movie.poster.small);
254                         } else {
255                                 // FIXME
256                                 if (no_poster == null) try {
257                                 //      var no_pic = new Gdk.Pixbuf.from_file ("/usr/share/icons/hicolor/64x64/hildon/imageviewer_no_pic.png");
258                                         var no_pic = new Gdk.Pixbuf.from_file ("/usr/share/icons/hicolor/64x64/hildon/general_no_thumbnail.png");
259                                         no_poster = new Gdk.Pixbuf (Gdk.Colorspace.RGB, true, 8, Poster.SMALL_WIDTH, Poster.SMALL_HEIGHT);
260                                         no_poster.fill (0);
261                                         no_pic.copy_area (0, 0, no_pic.width, no_pic.height, no_poster,
262                                                           (Poster.SMALL_WIDTH - no_pic.width) / 2, (Poster.SMALL_HEIGHT - no_pic.height) / 2);
263                                 } catch (Error e) {
264                                         critical ("Missing general_video icon: %s\n", e.message);
265                                 }
266                                 value.set_object (no_poster);
267                         }
268                         break;
269
270                 case Columns.ICON:
271                         if ((movie.poster != null) && (movie.poster.icon != null)) {
272                                 value.set_object (movie.poster.icon);
273                         } else {
274                                 value.set_object (null);
275                         }
276                         break;
277
278                 case Columns.MOVIE:
279                         value.set_object (movie);
280                         break;
281
282                 case Columns.MARKUP:
283                         base.get_value (iter, 1, out value);
284                         break;
285
286                 default:
287                         assert_not_reached ();
288                 }
289         }
290 }