X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=src%2Fmovie-list-view.vala;h=d469148d70b6316bc2aa9d483085c33c4e33dec5;hb=4224094782a4c69dd6f822eca16be9240184bbcf;hp=4f2c95695d6ac6a4f11cd0093f6f27a0c42adbaf;hpb=19be1caff054ad60c552ead67cfbc185b407378c;p=cinaest diff --git a/src/movie-list-view.vala b/src/movie-list-view.vala index 4f2c956..d469148 100644 --- a/src/movie-list-view.vala +++ b/src/movie-list-view.vala @@ -20,90 +20,386 @@ using Gtk; using Hildon; public class MovieListView : PannableArea { - private enum Columns { - TITLE, - YEAR, - RATING, - N_COLUMNS - } - - ListStore store; + public MovieListStore store; TreeView tree; - public TreeSortable sorted_store; + IconView icons; - construct { - store = new ListStore (3, typeof (string), typeof (int), typeof (int)); + private bool more_movies_available; + private CellRendererText title_renderer; + private CellRendererText secondary_renderer; + private CellRendererText rating_renderer; + private CellRendererText date_renderer; + private MoviePoster.Factory poster_factory; - // FIXME - fill store with test data - int i; - for (i = 0; i < 10; i++) { - TreeIter iter; - - store.append (out iter); - store.set_value (iter, Columns.TITLE, "Test %d".printf (i)); - store.set_value (iter, Columns.YEAR, 2000 - i); - store.set_value (iter, Columns.RATING, 10 * (i % 5) + i); + private bool poster_mode_; + public bool poster_mode { + get { + return poster_mode_; } + set { + if (value & !poster_mode_) { + remove (tree); + add (icons); + } else if (!value & poster_mode_) { + remove (icons); + add (tree); + } + poster_mode_ = value; + } + } - // Add filter wrapper - var filtered_store = new TreeModelFilter (store, null); - - // Add sort wrapper - sorted_store = new TreeModelSort.with_model (filtered_store); + public signal void movie_activated (Movie movie); + private Gtk.TreeView create_treeview (Gtk.Window window, bool show_date) { // Tree View - tree = (TreeView) Hildon.gtk_tree_view_new_with_model (UIMode.NORMAL, sorted_store); + var tree = (TreeView) Hildon.gtk_tree_view_new_with_model (UIMode.NORMAL, store); tree.set_headers_visible (false); - - add (tree); - tree.set_rules_hint (true); // Tree selection object var selection = tree.get_selection (); selection.set_mode (SelectionMode.SINGLE); - // Title column + // Title column with poster var title_column = new TreeViewColumn (); - title_column.set_title ("Movie"); - title_column.set_sort_column_id (Columns.TITLE); + title_column.set_title (_("Movie")); + title_column.set_sort_column_id (MovieListStore.Columns.TITLE); title_column.set_reorderable (false); title_column.set_sizing (TreeViewColumnSizing.AUTOSIZE); title_column.set_expand (true); + // Add poster icon to column + var pixbuf_renderer = new CellRendererPixbuf (); + pixbuf_renderer.width = 64; + pixbuf_renderer.xalign = 0.0f; + title_column.pack_start (pixbuf_renderer, false); + title_column.add_attribute (pixbuf_renderer, "pixbuf", MovieListStore.Columns.ICON); + // Add text to column - var renderer = new CellRendererText (); - renderer.set ("ellipsize", Pango.EllipsizeMode.END); - title_column.pack_start (renderer, true); - title_column.add_attribute (renderer, "text", Columns.TITLE); - tree.append_column (title_column); + var vbox_renderer = new CellRendererVBox (); - // Sort by title - sorted_store.set_sort_column_id (Columns.TITLE, SortType.ASCENDING); + title_renderer = new CellRendererText (); + title_renderer.yalign = 1.0f; + title_renderer.ellipsize = Pango.EllipsizeMode.END; + + vbox_renderer.append (title_renderer, true); + + // Add secondary text to column (Genres, Director, etc.) + secondary_renderer = new CellRendererText (); + secondary_renderer.yalign = 0; + secondary_renderer.ellipsize = Pango.EllipsizeMode.END; + secondary_renderer.attributes = get_attributes (window, "SmallSystemFont", "SecondaryTextColor"); + + vbox_renderer.append (secondary_renderer, true); + + title_column.pack_start (vbox_renderer, true); + title_column.set_cell_data_func (vbox_renderer, title_data_func); + + tree.append_column (title_column); // Year column - renderer = new CellRendererText (); - var year_column = new TreeViewColumn.with_attributes ("Year", renderer, "text", Columns.YEAR); - year_column.set_sort_column_id (Columns.YEAR); + var year_column = new TreeViewColumn (); + year_column.set_title (_("Year")); + year_column.set_sort_column_id (MovieListStore.Columns.YEAR); year_column.set_reorderable (false); year_column.set_sort_order (SortType.DESCENDING); tree.append_column (year_column); // Rating column - renderer = new CellRendererText (); - var rating_column = new TreeViewColumn.with_attributes ("Rating", renderer, "text", Columns.RATING); - rating_column.set_sort_column_id (Columns.RATING); + var rating_column = new TreeViewColumn (); + rating_column.set_title (_("Rating")); + rating_column.set_sort_column_id (MovieListStore.Columns.RATING); rating_column.set_reorderable (false); rating_column.set_sort_order (SortType.DESCENDING); - rating_column.set_cell_data_func (renderer, rating_data_func); - rating_column.xalign = (float) 1.0; + rating_column.xalign = 1.0f; + + vbox_renderer = new CellRendererVBox (); + + rating_renderer = new CellRendererText (); + rating_renderer.xalign = 1.0f; + if (show_date) + rating_renderer.yalign = 1.0f; + + vbox_renderer.append (rating_renderer, true); + + date_renderer = new CellRendererText (); + date_renderer.yalign = 0; + date_renderer.attributes = get_attributes (window, "SmallSystemFont", "SecondaryTextColor"); + + if (show_date) + vbox_renderer.append (date_renderer, true); + + rating_column.pack_start (vbox_renderer, true); + rating_column.set_cell_data_func (vbox_renderer, rating_data_func); + tree.append_column (rating_column); + tree.show (); + return tree; + } + + private Gtk.IconView create_iconview () { + var iconview = (Gtk.IconView) Hildon.gtk_icon_view_new_with_model (Hildon.UIMode.NORMAL, store); + iconview.set_column_spacing (0); + iconview.set_pixbuf_column (MovieListStore.Columns.POSTER); + iconview.margin = 0; + iconview.item_width = Poster.SMALL_WIDTH; + iconview.column_spacing = Hildon.MARGIN_HALF; + iconview.row_spacing = Hildon.MARGIN_HALF; + iconview.show (); + + return iconview; + } + + public MovieListView (Gtk.Window window, bool show_date = false) { + store = new MovieListStore (); + + // Sort by title + store.set_sort_column_id (MovieListStore.Columns.TITLE, SortType.ASCENDING); + + Gdk.Color color; + window.ensure_style (); + if (window.style.lookup_color ("SecondaryTextColor", out color)) { + store.year_markup = "(%%d)".printf (color.to_string ()); + } + + tree = create_treeview (window, show_date); + store.view = (Widget) tree; + + icons = create_iconview (); + + add (tree); + + // Connect signals + get_vadjustment ().value_changed.connect (on_adjustment_value_changed); + tree.row_activated.connect (on_row_activated); + icons.item_activated.connect (on_item_activated); + store.row_changed.connect (on_row_changed); + store.search_finished.connect (on_search_finished); + } + + construct { + hscrollbar_policy = Gtk.PolicyType.NEVER; + + poster_factory = MoviePoster.Factory.get_instance (); + } + + public void set_hildon_ui_mode (UIMode mode) { + var selection = tree.get_selection (); + + if (mode == UIMode.NORMAL) { + selection.set_mode (SelectionMode.NONE); + icons.set_selection_mode (SelectionMode.NONE); + } + Hildon.gtk_tree_view_set_ui_mode (tree, mode); + Hildon.gtk_icon_view_set_ui_mode (icons, mode); + if (mode == UIMode.EDIT) { + selection.set_mode (SelectionMode.MULTIPLE); + icons.set_selection_mode (SelectionMode.MULTIPLE); + } + } + + private Pango.AttrList get_attributes (Gtk.Window window, string font_name, string color_name) { + Pango.AttrList attr_list = new Pango.AttrList (); + var style = Gtk.rc_get_style_by_paths (Gtk.Settings.get_default (), font_name, null, typeof (void)); + if (style != null) { + var attr_font_desc = new Pango.AttrFontDesc (style.font_desc.copy ()); + attr_list.insert ((owned) attr_font_desc); + } + Gdk.Color color; + window.ensure_style (); + if (window.style.lookup_color (color_name, out color)) { + Pango.Attribute attr_color = Pango.attr_foreground_new (color.red, color.green, color.blue); + attr_list.insert ((owned) attr_color); + } + return attr_list; + } + + public void unselect_all () { + tree.get_selection ().unselect_all (); + icons.unselect_all (); + } + + public List get_selected_movies () { + var movies = new List (); + List paths; + + if (poster_mode_) + paths = icons.get_selected_items (); + else + paths = tree.get_selection ().get_selected_rows (null); + + // get selected movies from the store + foreach (TreePath path in paths) { + TreeIter iter; + + if (store.get_iter (out iter, path)) { + Movie movie; + + store.get (iter, MovieListStore.Columns.MOVIE, out movie); + if (movie != null) { + movies.append (movie); + } + } + } + + return movies; + } + + // TODO: after scrolling down 80% of the list, load more + // results if available. + int last_a = 0; + int last_b = 0; + private void on_adjustment_value_changed () { + if (more_movies_available) { + var vadj = get_vadjustment (); + if (vadj.value > 0.8 * vadj.upper) { + Banner.show_information (this, null, _("More results available - refine search to reduce the dataset")); + more_movies_available = false; + } + } + + TreePath a_; + TreePath b_; + bool range; + if (poster_mode_) { + range = icons.get_visible_range (out a_, out b_); + } else { + range = tree.get_visible_range (out a_, out b_); + } + if (range) { + // We know the list store is flat + int a = a_.get_indices ()[0]; + int b = b_.get_indices ()[0]; + assert (a <= b); + + if (a == last_a && b == last_b) + return; + + if (a > last_b || b < last_a) { + check_posters (a, b); + } else if (a >= last_a && b > last_b) { + check_posters (last_b + 1, b); + } else if (b <= last_b && a < last_a) { + check_posters (a, last_a - 1); + } + + last_a = a; + last_b = b; + } + } + + private void check_posters (int a, int b) { + for (int i = a; i <= b; i++) { + var path = new TreePath.from_indices (i); + TreeIter iter; + if (store.get_iter (out iter, path)) { + Movie movie; + store.get (iter, MovieListStore.Columns.MOVIE, out movie); + if (movie != null) { + if (poster_mode_) { + if (movie.poster == null || movie.poster.small == null) try { + poster_factory.queue_thumbnail (movie, Poster.SMALL_WIDTH, Poster.SMALL_HEIGHT, false, receive_poster_small); + } catch (Error e) { + warning ("Failed to queue poster request: %s\n", e.message); + } + } else { + if (movie.poster == null || movie.poster.icon == null) try { + poster_factory.queue_thumbnail (movie, Poster.ICON_WIDTH, Poster.ICON_HEIGHT, false, receive_poster_icon); + } catch (Error e) { + warning ("Failed to queue poster request: %s\n", e.message); + } + } + } + } + } + } + + private void on_row_activated (TreeView tree, TreePath path, TreeViewColumn column) { + on_item_activated (path); + } + + private void on_item_activated (TreePath path) { + TreeIter iter; + + if (store.get_iter (out iter, path)) { + Movie movie; + store.get (iter, MovieListStore.Columns.MOVIE, out movie); + movie_activated (movie); + } + } + + private void on_row_changed (TreePath path, TreeIter iter) { + TreePath a; + TreePath b; + bool range; + + if (poster_mode_) { + range = icons.get_visible_range (out a, out b); + } else { + range = tree.get_visible_range (out a, out b); + } + if (!range || + (range && path.compare (a) >= 0 && path.compare (b) <= 0)) { + Movie movie; + + store.get (iter, MovieListStore.Columns.MOVIE, out movie); + if (movie == null) + return; + + int i = path.get_indices ()[0]; + check_posters (i, i); + } + } + + private void receive_poster_small (Gdk.Pixbuf pixbuf, Movie movie) { + Poster poster; + if (movie.poster != null) + poster = movie.poster; + else + poster = new Poster (); + poster.small = pixbuf; + movie.poster = poster; + } + + private void receive_poster_icon (Gdk.Pixbuf pixbuf, Movie movie) { + Poster poster; + if (movie.poster != null) + poster = movie.poster; + else + poster = new Poster (); + poster.icon = pixbuf; + movie.poster = poster; + } + + private void on_search_finished (int movies) { + more_movies_available = (movies > 100); // FIXME + } + + private void title_data_func (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter) { + Movie movie; + string markup; + + model.get (iter, MovieListStore.Columns.MOVIE, out movie, + MovieListStore.Columns.MARKUP, out markup); + title_renderer.markup = markup; + secondary_renderer.text = movie.secondary; } private void rating_data_func (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter) { - int rating; + string rating; + Movie movie; - model.get (iter, MovieListStore.Columns.RATING, out rating); - ((CellRendererText) cell).text = "%d.%d".printf (rating / 10, rating % 10); + model.get (iter, MovieListStore.Columns.RATING, out rating, + MovieListStore.Columns.MOVIE, out movie); + rating_renderer.text = rating; + if (movie.julian_date != 0) { + var date = Date (); + date.set_julian (movie.julian_date); + var s = new char[64]; + date.strftime (s, "%x"); + date_renderer.text = (string) s; + } else { + date_renderer.text = ""; + } } }