/* 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 ();
}
}
}