Czech translation update (via transifex.net)
[cinaest] / src / movie-list-window.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 using Hildon;
21
22 public class MovieListWindow : StackableWindow {
23         private MovieListMenu menu;
24         private Hildon.EditToolbar edit_toolbar;
25         private Hildon.Entry search_field;
26         private Toolbar search_bar;
27         private uint source_id;
28         private MovieListView movie_list;
29         private MovieFilter filter;
30         private MovieListStore store;
31         private Label no_movies;
32         private bool search_bar_visible;
33         private MovieWindow movie_window;
34         private Alignment alignment;
35         private int count;
36         private MovieSource source;
37         private bool portrait_mode;
38         private bool auto_open_movie_window;
39
40         public MovieListWindow (MovieSource source_, string? title = null, int year = 0) {
41                 source = source_;
42                 set_title (source.get_name ());
43
44                 auto_open_movie_window = (title != null);
45
46                 // View menu
47                 menu = new MovieListMenu (this);
48                 menu.source = source;
49
50                 set_main_menu (menu);
51
52                 // Search bar
53                 search_field = new Hildon.Entry (SizeType.FINGER_HEIGHT);
54
55                 var search_field_item = new ToolItem ();
56                 search_field_item.set_expand (true);
57                 search_field_item.add (search_field);
58
59                 var icon_theme = Gtk.IconTheme.get_default ();
60                 var pixbuf = icon_theme.load_icon ("general_close",
61                                                    48,
62                                                    Gtk.IconLookupFlags.NO_SVG);
63                 var close_image = new Image.from_pixbuf (pixbuf);
64                 var close_button = new ToolButton (close_image, _("Close"));
65
66                 search_bar = new Toolbar ();
67                 search_bar.insert (search_field_item, 0);
68                 search_bar.insert (close_button, 1);
69                 search_bar.show_all ();
70
71                 add_toolbar (search_bar);
72
73                 // Movie list - connected to menu for sorting
74                 movie_list = new MovieListView (this, source.get_name () == _("Watched movies"));
75                 menu.store = movie_list.store;
76                 store = movie_list.store;
77                 store.source = source;
78
79                 no_movies = new Label (_("No movies"));
80                 Hildon.helper_set_logical_font (no_movies, "LargeSystemFont");
81                 Hildon.helper_set_logical_color (no_movies, RcFlags.FG, StateType.NORMAL, "SecondaryTextColor");
82                 no_movies.set_size_request (-1, 6 * 70);
83                 no_movies.set_alignment ((float) 0.5, (float) 0.42);
84
85                 var vbox = new VBox (false, 0);
86                 vbox.pack_start (movie_list, true, true, 0);
87                 vbox.pack_start (no_movies, false, false, 0);
88
89                 alignment = new Alignment (0.0f, 0.0f, 1.0f, 1.0f);
90                 alignment.top_padding = MARGIN_HALF;
91
92                 // Reduced padding in portrait mode to fit 3 posters in width
93                 int padding = portrait_mode ? MARGIN_DEFAULT : MARGIN_DOUBLE;
94                 alignment.left_padding = padding;
95                 alignment.right_padding = padding;
96
97                 alignment.add (vbox);
98                 add (alignment);
99
100                 edit_toolbar = new Hildon.EditToolbar.with_text (_("Select movies"), _("Delete"));
101                 set_edit_toolbar (edit_toolbar);
102
103                 // Connect signals
104                 menu.filter_changed.connect (on_filter_changed);
105                 edit_toolbar.button_clicked.connect (on_delete_button_clicked);
106                 edit_toolbar.arrow_clicked.connect (leave_edit_mode); 
107                 search_field.changed.connect (on_search_field_changed);
108                 close_button.clicked.connect (on_close_button_clicked);
109                 key_press_event.connect (on_key_press_event);
110                 movie_list.movie_activated.connect (on_movie_activated);
111                 store.row_deleted.connect (on_row_deleted);
112                 store.row_inserted.connect (on_row_inserted);
113                 Gdk.Screen.get_default ().size_changed.connect (on_orientation_changed);
114                 store.search_finished.connect (on_search_finished);
115
116                 store.notify["update-running"].connect (on_update_running_changed);
117
118                 search_field.set_flags (WidgetFlags.CAN_DEFAULT);
119                 search_field.grab_default ();
120
121                 alignment.show_all ();
122                 edit_toolbar.hide ();
123                 search_bar_visible = false;
124                 search_bar.hide ();
125
126                 filter = new MovieFilter ();
127                 menu.filter = filter;
128                 filter.title = (title != null) ? title : "";
129                 if (year != 0) {
130                         filter.year_min = year;
131                         filter.year_max = year;
132                 }
133                 if (SourceFlags.NOEMPTY in source.get_flags ()) {
134                         no_movies.hide ();
135                         search_bar_visible = true;
136                         search_bar.show ();
137                         search_field.grab_focus ();
138                         return;
139                 }
140                 if (store.start_search (filter)) {
141                         no_movies.hide ();
142                 } else {
143                         movie_list.hide ();
144                 }
145         }
146
147         private void on_filter_changed () {
148                 var markup = new StringBuilder ();
149                 if (filter.year_min != 0 || filter.year_max != 0)
150                         markup.append (_(" - years"));
151                 if (filter.rating_min != 0)
152                         markup.append (_(" - rating"));
153                 if (filter.genres.field != 0)
154                         markup.append (_(" - genres"));
155                 if (markup.str.length > 0) {
156                         Gdk.Color color;
157                         this.ensure_style ();
158                         if (this.style.lookup_color ("ActiveTextColor", out color)) {
159                                 markup.prepend ("<span fgcolor=\"%s\">".printf (color.to_string ()));
160                                 markup.append ("</span>");
161                         }
162                         markup.prepend (source.get_name ());
163                         set_markup (markup.str);
164                 } else {
165                         set_markup (source.get_name ());
166                 }
167
168                 start_search ();
169         }
170
171         public void on_delete_movies_clicked () {
172                 fullscreen ();
173                 edit_toolbar.show ();
174                 movie_list.set_hildon_ui_mode (UIMode.EDIT);
175
176                 movie_list.unselect_all ();
177         }
178
179         private void on_delete_button_clicked () {
180                 var movies = movie_list.get_selected_movies ();
181                 int count = (int) movies.length ();
182                 if (count == 0) {
183                         Banner.show_information (this, null, _("No movies selected"));
184                         leave_edit_mode ();
185                         return;
186                 }
187
188                 var dialog = new Note.confirmation (this, 
189                                                     ngettext ("Delete %d movie?",
190                                                               "Delete %d movies?",
191                                                               count).printf (count));
192                 var res = dialog.run ();
193
194                 if (res == Gtk.ResponseType.OK) {
195                         // Remove selected movies
196                         foreach (Movie movie in movies) {
197                                 store.remove (movie);
198                         }
199
200                         // Switch to "No movies" label if the store is emptied
201                         TreeIter iter;
202                         if (!store.get_iter_first (out iter)) {
203                                 movie_list.hide ();
204                                 no_movies.show ();
205                         }
206                 }
207                 dialog.destroy ();
208                 leave_edit_mode ();
209         }
210
211         private void leave_edit_mode () {
212                 movie_list.set_hildon_ui_mode (UIMode.NORMAL);
213                 edit_toolbar.hide ();
214                 unfullscreen ();
215         }
216
217         private void on_close_button_clicked () {
218                 search_field.set_text ("");
219                 search_bar_visible = false;
220                 search_bar.hide ();
221         }
222
223         private bool on_key_press_event (Widget widget, Gdk.EventKey event) {
224                 if (event.str != "") {
225                         if (!search_bar_visible) {
226                                 search_bar_visible = true;
227                                 search_bar.show ();
228                         }
229                         search_field.grab_focus ();
230                 }
231
232                 return false;
233         }
234
235         private void on_search_field_changed () {
236                 // With every change we reset the timer to 500ms
237                 if (source_id != 0) {
238                         Source.remove (source_id);
239                 }
240                 source_id = Timeout.add (500, start_search);
241         }
242
243         private bool start_search () {
244                 filter.title = search_field.get_text ();
245                 if (store.start_search (filter)) {
246                         movie_list.show ();
247                         no_movies.hide ();
248                 }
249
250                 // One-shot only
251                 return false;
252         }
253
254         private void on_orientation_changed (Gdk.Screen screen) {
255                 if (CinaestProgram.orientation.portrait == portrait_mode)
256                         return;
257
258                 portrait_mode = CinaestProgram.orientation.portrait;
259
260                 // Reduced padding in portrait mode to fit 3 posters in width
261                 int padding = portrait_mode ? MARGIN_DEFAULT : MARGIN_DOUBLE;
262                 alignment.left_padding = padding;
263
264                 // Make space for the pannable area's scroll position indicator
265                 if (count > movies_per_screen ())
266                         padding -= MARGIN_DEFAULT;
267                 alignment.right_padding = padding;
268         }
269
270         private void on_search_finished (int movies) {
271                 int padding = portrait_mode ? MARGIN_DEFAULT : MARGIN_DOUBLE;
272
273                 // Make space for the pannable area's scroll position indicator
274                 if (count > movies_per_screen ())
275                         padding -= MARGIN_DEFAULT;
276                 alignment.right_padding = padding;
277
278                 if (count == 1 && auto_open_movie_window) {
279                         TreeIter iter;
280                         if (store.get_iter_first (out iter)) {
281                                 Movie movie;
282                                 store.get (iter, MovieListStore.Columns.MOVIE, out movie);
283                                 on_movie_activated (movie);
284                         }
285                 }
286         }
287
288         private int movies_per_screen () {
289                 if (portrait_mode) {
290                         // 3x3 full posters or 10 full list items,
291                         // 10 full list items in list mode without search bar
292                         return (search_bar_visible || movie_list.poster_mode) ? 9 : 10;
293                 } else {
294                         if (search_bar_visible) {
295                                 // 5x1 full posters or 5 list items
296                                 return 5;
297                         } else {
298                                 // 5x2 posters or 6 list items
299                                 return movie_list.poster_mode ? 10 : 6;
300                         }
301                 }
302         }
303
304         private void on_movie_activated (Movie movie) {
305                 if (movie_window != null)
306                         return;
307
308                 movie_window = new MovieWindow.with_movie (movie, store);
309                 movie_window.destroy.connect (() => { movie_window = null; });
310                 get_stack ().push_1 (movie_window);
311         }
312
313         private void on_row_deleted (TreePath path) {
314                 if (--count == 0) {
315                         no_movies.show ();
316                         movie_list.hide ();
317                 }
318         }
319
320         private void on_row_inserted (TreePath path, TreeIter iter) {
321                 if (count++ == 0) {
322                         no_movies.hide ();
323                         movie_list.show ();
324                 }
325         }
326
327         private void on_update_running_changed (GLib.Object source, ParamSpec spec) {
328                 TreeIter iter;
329
330                 Hildon.gtk_window_set_progress_indicator (this, (int) store.update_running);
331                 // Update finished, but store still empty?
332                 if (!store.update_running && !store.get_iter_first (out iter)) {
333                         movie_list.hide ();
334                         no_movies.show ();
335                 }
336         }
337
338         public unowned MovieListView get_movie_list_view () {
339                 return movie_list;
340         }
341 }
342