1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- 2/*- 3 * Copyright (c) 2016-2016 elementary LLC. 4 * 5 * This program 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 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 * Authored by: Artem Anufrij <artem.anufrij@live.de> 19 * 20 */ 21 22namespace Audience { 23 public class EpisodesPage : Gtk.Grid { 24 public Gtk.Image poster { get; set; } 25 Gtk.ScrolledWindow scrolled_window; 26 Gtk.FlowBox view_episodes; 27 Granite.Widgets.AlertView alert_view; 28 Gee.ArrayList<Audience.Objects.Video> shown_episodes; 29 30 public Audience.Services.LibraryManager manager; 31 32 string query; 33 Objects.Video poster_source; 34 35 construct { 36 query = ""; 37 poster_source = null; 38 39 poster = new Gtk.Image (); 40 poster.margin = 24; 41 poster.margin_end = 0; 42 poster.valign = Gtk.Align.START; 43 poster.get_style_context ().add_class ("card"); 44 45 view_episodes = new Gtk.FlowBox (); 46 view_episodes.margin = 24; 47 view_episodes.homogeneous = true; 48 view_episodes.valign = Gtk.Align.START; 49 view_episodes.selection_mode = Gtk.SelectionMode.NONE; 50 view_episodes.set_sort_func (episode_sort_func); 51 view_episodes.set_filter_func (episodes_filter_func); 52 view_episodes.child_activated.connect (play_video); 53 view_episodes.max_children_per_line = 1; 54 55 scrolled_window = new Gtk.ScrolledWindow (null, null); 56 scrolled_window.expand = true; 57 scrolled_window.add (view_episodes); 58 59 alert_view = new Granite.Widgets.AlertView ("", "", ""); 60 alert_view.get_style_context ().remove_class (Gtk.STYLE_CLASS_VIEW); 61 alert_view.hide (); 62 63 expand = true; 64 attach (poster, 0, 1, 1, 1); 65 attach (scrolled_window, 1, 1, 1, 1); 66 attach (alert_view, 1, 1, 1, 1); 67 68 manager = Audience.Services.LibraryManager.get_instance (); 69 manager.video_file_deleted.connect (remove_item_from_path); 70 manager.video_file_detected.connect (add_item); 71 } 72 73 public void set_episodes_items (Gee.ArrayList<Audience.Objects.Video> episodes) { 74 view_episodes.forall ((item) => { 75 item.dispose (); 76 }); 77 shown_episodes = new Gee.ArrayList<Audience.Objects.Video> (); 78 foreach (Audience.Objects.Video episode in episodes) { 79 view_episodes.add (new Audience.LibraryItem (episode, LibraryItemStyle.ROW)); 80 shown_episodes.add (episode); 81 } 82 shown_episodes.sort ((a, b) => { 83 var item1 = (Audience.Objects.Video)a; 84 var item2 = (Audience.Objects.Video)b; 85 if (item1 != null && item2 != null) { 86 return item1.file.collate (item2.file); 87 } 88 return 0; 89 }); 90 if (poster_source != null) { 91 poster_source.poster_changed.disconnect (update_poster); 92 } 93 poster_source = episodes.first (); 94 update_poster (poster_source); 95 poster_source.poster_changed.connect (update_poster); 96 } 97 98 private void update_poster (Objects.Video episode) { 99 poster.pixbuf = episode.poster; 100 } 101 102 private void play_video (Gtk.FlowBoxChild item) { 103 var selected = (item as Audience.LibraryItem); 104 var video = selected.episodes.first (); 105 if (video.video_file.query_exists ()) { 106 string uri = video.video_file.get_uri (); 107 bool from_beginning = uri != settings.get_string ("current-video"); 108 var window = App.get_instance ().mainwindow;// Clean playlist 109 window.clear_playlist (); 110 window.add_to_playlist (uri, false); 111 window.play_file (uri, Window.NavigationPage.EPISODES, from_beginning); 112 if (window.autoqueue_next_active ()) { 113 // Add next from the current view to the queue 114 int played_index = shown_episodes.index_of (video); 115 foreach (Audience.Objects.Video episode in shown_episodes.slice (played_index, shown_episodes.size)) { 116 window.append_to_playlist (episode.video_file); 117 } 118 } 119 } 120 } 121 122 public void filter (string text) { 123 query = text.strip (); 124 view_episodes.invalidate_filter (); 125 if (!has_child ()) { 126 show_alert (_("No Results for “%s”").printf (text), _("Try changing search terms."), "edit-find-symbolic"); 127 } else { 128 alert_view.hide (); 129 } 130 } 131 132 private bool episodes_filter_func (Gtk.FlowBoxChild child) { 133 if (query.length == 0) { 134 return true; 135 } 136 137 string[] filter_elements = query.split (" "); 138 var video_title = ((LibraryItem)(child)).get_title (); 139 140 foreach (string filter_element in filter_elements) { 141 if (!video_title.down ().contains (filter_element.down ())) { 142 return false; 143 } 144 } 145 return true; 146 } 147 148 private int episode_sort_func (Gtk.FlowBoxChild child1, Gtk.FlowBoxChild child2) { 149 var item1 = (LibraryItem)child1; 150 var item2 = (LibraryItem)child2; 151 if (item1 != null && item2 != null) { 152 return item1.episodes.first ().file.collate (item2.episodes.first ().file); 153 } 154 return 0; 155 } 156 157 private void add_item (Audience.Objects.Video episode) { 158 if (view_episodes.get_children ().length () > 0 ) { 159 var first = (view_episodes.get_children ().first ().data as Audience.LibraryItem); 160 if (first != null && first.episodes.first ().video_file.get_parent ().get_path () == episode.video_file.get_parent ().get_path ()) { 161 view_episodes.add (new Audience.LibraryItem (episode, LibraryItemStyle.ROW)); 162 } 163 } 164 } 165 166 private async void remove_item_from_path (string path ) { 167 foreach (var child in view_episodes.get_children ()) { 168 if (((LibraryItem)(child)).episodes.size == 0 || 169 ((LibraryItem)(child)).episodes.first ().video_file.get_path ().has_prefix (path)) { 170 171 child.dispose (); 172 } 173 } 174 175 if (Audience.App.get_instance ().mainwindow.get_visible_child () == this && view_episodes.get_children ().length () == 0) { 176 Audience.App.get_instance ().mainwindow.navigate_back (); 177 } 178 } 179 180 public bool has_child () { 181 if (view_episodes.get_children ().length () > 0) { 182 foreach (unowned Gtk.Widget child in view_episodes.get_children ()) { 183 if (child.get_child_visible ()) { 184 return true; 185 } 186 } 187 } 188 return false; 189 } 190 191 public void show_alert (string primary_text, string secondary_text, string icon_name) { 192 alert_view.no_show_all = false; 193 alert_view.show_all (); 194 alert_view.title = primary_text; 195 alert_view.description = secondary_text; 196 alert_view.icon_name = icon_name; 197 alert_view.show (); 198 } 199 } 200} 201