/* 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,
MOVIE,
MARKUP,
N_COLUMNS
}
private GLib.Type[] types = {
typeof (string),
typeof (int),
typeof (string),
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 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);
}
append (out iter1);
base.set (iter1, 0, movie,
1, markup.str,
2, (movie.rating >= 0) ? "%d.%d".printf (movie.rating / 10, movie.rating % 10) : null);
movie.notify.connect (this.on_movie_changed);
iter = iter1;
}
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;
foreach (Movie movie in movies) {
add (movie, out iter);
try {
poster_factory.queue_thumbnail (movie, 64, 64, false, receive_poster_thumbnail);
} catch (Error e) {
warning ("Failed to queue poster request: %s\n", e.message);
}
}
}
private void receive_poster_thumbnail (Gdk.Pixbuf pixbuf, Movie movie) {
var poster = new Poster ();
poster.thumbnail = pixbuf;
movie.poster = poster;
}
// 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;
// FIXME
if (no_poster == null) try {
no_poster = new Gdk.Pixbuf.from_file ("/usr/share/icons/hicolor/64x64/hildon/general_video.png");
} catch (Error e) {
critical ("Missing general_video icon: %s\n", e.message);
}
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.thumbnail != null))
value.set_object (movie.poster.thumbnail);
else
value.set_object (no_poster);
break;
case Columns.MOVIE:
value.set_object (movie);
break;
case Columns.MARKUP:
base.get_value (iter, 1, out value);
break;
default:
assert_not_reached ();
}
}
}