/* This file is part of Cinaest. * * Copyright (C) 2009 Philipp Zabel * * Cinaest is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Cinaest is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Cinaest. If not, see . */ using Gtk; public class MovieListStore : ListStore, TreeModel { public enum Columns { TITLE, YEAR, RATING, POSTER, ICON, MOVIE, MARKUP, N_COLUMNS } private GLib.Type[] types = { typeof (string), typeof (int), typeof (string), typeof (Gdk.Pixbuf), typeof (Gdk.Pixbuf), typeof (Movie), typeof (string) }; private GLib.Type[] base_type = { typeof (Movie), typeof (string), // Markup: "Title (Year)" typeof (string) // Rating }; private Gdk.Pixbuf no_poster; private MoviePoster.Factory poster_factory; private MovieFilter filter; public bool update_running { get; set; } public string year_markup = "[%d]"; private Cancellable cancellable; public Widget view; public signal void search_finished (int movies); private MovieSource _source; public MovieSource source { get { return _source; } set { _source = value; } } construct { set_column_types (base_type); no_poster = null; source = null; update_running = false; poster_factory = MoviePoster.Factory.get_instance (); } public void add (Movie movie, out TreeIter iter) { TreeIter iter1; var markup = new StringBuilder (); markup.append (Markup.escape_text (movie.title)); if (movie.year > 0) { markup.append (" "); markup.append_printf (year_markup, movie.year); } base.insert_with_values (out iter1, -1, 0, movie, 1, markup.str, 2, rating_string (movie.rating)); movie.notify.connect (this.on_movie_changed); iter = iter1; } private string? rating_string (int rating) { if (rating >= 0) { return "%d.%d".printf (rating / 10, rating % 10); } else { return null; } } public new bool remove (Movie movie) { TreeIter iter; if (get_iter_from_movie (out iter, movie)) { movie.notify.disconnect (this.on_movie_changed); base.remove (iter); if (SourceFlags.EDITABLE in source.get_flags ()) { source.delete_movie (movie); } return true; } return false; } private void on_movie_changed (GLib.Object source, GLib.ParamSpec spec) { var movie = (Movie) source; TreeIter iter; if (get_iter_from_movie (out iter, movie)) { TreePath path = get_path (iter); base.row_changed (path, iter); } } public bool get_editable () { return (SourceFlags.EDITABLE in source.get_flags ()); } public bool get_iter_from_movie (out TreeIter iter, Movie movie_a) { if (get_iter_first (out iter)) { do { Movie movie_b; get (iter, Columns.MOVIE, out movie_b); if (movie_a == movie_b) return true; } while (iter_next (ref iter)); } return false; } public bool start_search (MovieFilter _filter) { if (update_running) { stdout.printf ("aborting search ...\n"); cancellable.cancel (); poster_factory.clear_queue (); return false; } if (cancellable == null || cancellable.is_cancelled ()) cancellable = new Cancellable (); filter = _filter; stdout.printf ("begin search\n"); search_async.begin (); update_running = true; return true; } // Asynchronous update method private async void search_async () { stdout.printf ("search started: \"%s\"\n", filter.title); clear (); if (source != null) { // FIXME - arbitrary limit int n = yield source.get_movies (filter, receive_movie, 100, cancellable); search_finished (n); } update_running = false; if (cancellable.is_cancelled ()) { stdout.printf ("search aborted, starting new\n"); cancellable.reset (); if (cancellable.is_cancelled ()) { stdout.printf ("OW WEY\n"); } start_search (filter); } else { stdout.printf ("search stopped\n"); } } private void receive_movie (SList movies) { TreeIter iter; if (cancellable.is_cancelled ()) return; view.freeze_child_notify (); foreach (Movie movie in movies) add (movie, out iter); view.thaw_child_notify (); } // Implement TreeModel interface public virtual GLib.Type get_column_type (int index_) { return_val_if_fail (index_ >= 0 && index_ < Columns.N_COLUMNS, 0); return types[index_]; } public virtual int get_n_columns () { return Columns.N_COLUMNS; } public virtual void get_value (TreeIter iter, int column, out GLib.Value value) { Movie movie; return_if_fail (column >= 0 && column < Columns.N_COLUMNS); // Get the Movie from our parent's storage Value val; base.get_value (iter, 0, out val); movie = (Movie) val.get_object (); value.init (get_column_type (column)); switch (column) { case Columns.TITLE: if (movie != null) { value.set_string (movie.title); } else { value.set_string (""); } break; case Columns.YEAR: if (movie != null) { value.set_int (movie.year); } else { value.set_int (-1); } break; case Columns.RATING: base.get_value (iter, 2, out value); break; case Columns.POSTER: if ((movie.poster != null) && (movie.poster.small != null)) { value.set_object (movie.poster.small); } else { // FIXME if (no_poster == null) try { // var no_pic = new Gdk.Pixbuf.from_file ("/usr/share/icons/hicolor/64x64/hildon/imageviewer_no_pic.png"); var no_pic = new Gdk.Pixbuf.from_file ("/usr/share/icons/hicolor/64x64/hildon/general_no_thumbnail.png"); no_poster = new Gdk.Pixbuf (Gdk.Colorspace.RGB, true, 8, Poster.SMALL_WIDTH, Poster.SMALL_HEIGHT); no_poster.fill (0); no_pic.copy_area (0, 0, no_pic.width, no_pic.height, no_poster, (Poster.SMALL_WIDTH - no_pic.width) / 2, (Poster.SMALL_HEIGHT - no_pic.height) / 2); } catch (Error e) { critical ("Missing general_video icon: %s\n", e.message); } value.set_object (no_poster); } break; case Columns.ICON: if ((movie.poster != null) && (movie.poster.icon != null)) { value.set_object (movie.poster.icon); } else { value.set_object (null); } break; case Columns.MOVIE: value.set_object (movie); break; case Columns.MARKUP: base.get_value (iter, 1, out value); break; default: assert_not_reached (); } } }