1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2016-2018 elementary LLC. (https://elementary.io)
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 enum LibraryItemStyle { THUMBNAIL, ROW }
24
25    public class LibraryItem : Gtk.FlowBoxChild {
26        Audience.Services.LibraryManager manager;
27
28        public Gee.ArrayList<Audience.Objects.Video> episodes { get; private set; }
29        public LibraryItemStyle item_style { get; construct set; }
30
31        private Gtk.Label title_label;
32        private Gtk.Spinner spinner;
33        private Gtk.Grid spinner_container;
34        private Gtk.Menu context_menu;
35
36        public Gtk.Image poster { get; set; }
37        public string episode_poster_path { get; construct set; }
38        public string poster_cache_file { get; construct set; }
39        public string hash { get; construct set; }
40
41        public LibraryItem (Audience.Objects.Video video, LibraryItemStyle item_style) {
42            Object (item_style: item_style);
43            episodes = new Gee.ArrayList<Audience.Objects.Video> ();
44            add_episode (video);
45            video.title_changed.connect (video_title_changed);
46            video.poster_changed.connect (video_poster_changed);
47
48            hash = GLib.Checksum.compute_for_string (ChecksumType.MD5, video.video_file.get_parent ().get_uri (), video.video_file.get_parent ().get_uri ().length);
49            episode_poster_path = Path.build_filename (video.video_file.get_parent ().get_path (), video.video_file.get_parent ().get_basename () + ".jpg");
50            poster_cache_file = Path.build_filename (App.get_instance ().get_cache_directory (), hash + ".jpg");
51        }
52
53        construct {
54            manager = Audience.Services.LibraryManager.get_instance ();
55
56            var grid = new Gtk.Grid ();
57            grid.valign = Gtk.Align.START;
58            grid.expand = true;
59
60            title_label = new Gtk.Label ("");
61
62            context_menu = new Gtk.Menu ();
63
64            var move_to_trash = new Gtk.MenuItem.with_label (_("Move to Trash"));
65            move_to_trash.activate.connect ( move_video_to_trash );
66
67            if (item_style == LibraryItemStyle.THUMBNAIL) {
68                margin_bottom = 12;
69
70                title_label.set_line_wrap (true);
71                title_label.max_width_chars = 0;
72                title_label.justify = Gtk.Justification.CENTER;
73
74                grid.halign = Gtk.Align.CENTER;
75                grid.row_spacing = 12;
76
77                var new_cover = new Gtk.MenuItem.with_label (_("Set Artwork"));
78                new_cover.activate.connect ( set_new_cover );
79                context_menu.append (new_cover);
80                context_menu.append (new Gtk.SeparatorMenuItem ());
81
82                poster = new Gtk.Image ();
83                poster.margin_top = poster.margin_start = poster.margin_end = 12;
84                poster.get_style_context ().add_class ("card");
85                poster.pixbuf = null;
86                poster.notify ["pixbuf"].connect (poster_visibility);
87
88                spinner_container = new Gtk.Grid ();
89                spinner_container.height_request = Audience.Services.POSTER_HEIGHT;
90                spinner_container.width_request = Audience.Services.POSTER_WIDTH;
91                spinner_container.margin_top = spinner_container.margin_start = spinner_container.margin_end = 12;
92                spinner_container.get_style_context ().add_class ("card");
93
94                spinner = new Gtk.Spinner ();
95                spinner.expand = true;
96                spinner.active = true;
97                spinner.valign = Gtk.Align.CENTER;
98                spinner.halign = Gtk.Align.CENTER;
99                spinner.height_request = 32;
100                spinner.width_request = 32;
101
102                spinner_container.add (spinner);
103
104                grid.attach (spinner_container, 0, 0, 1, 1);
105                grid.attach (poster, 0, 0, 1, 1);
106                grid.attach (title_label, 0, 1);
107
108                map.connect (poster_visibility);
109            } else {
110                grid.halign = Gtk.Align.FILL;
111                grid.attach (title_label, 0, 0);
112                grid.margin = 12;
113            }
114
115            context_menu.append (move_to_trash);
116            context_menu.show_all ();
117
118            var event_box = new Gtk.EventBox ();
119            event_box.button_press_event.connect (show_context_menu);
120            event_box.add (grid);
121
122            add (event_box);
123            show_all ();
124        }
125
126        private void poster_visibility () {
127            if (poster.pixbuf != null) {
128                spinner.active = false;
129                spinner_container.hide ();
130                poster.show_all ();
131            } else {
132                spinner.active = true;
133                spinner_container.show ();
134                poster.hide ();
135            }
136        }
137
138        private bool show_context_menu (Gtk.Widget sender, Gdk.EventButton evt) {
139            if (evt.type == Gdk.EventType.BUTTON_PRESS && evt.button == 3) {
140                context_menu.popup_at_pointer (evt);
141                return true;
142            }
143            return false;
144        }
145
146        private void video_poster_changed (Audience.Objects.Video video) {
147            if (item_style == LibraryItemStyle.THUMBNAIL && (episodes.size == 1 || poster.pixbuf == null)) {
148                poster.pixbuf = video.poster;
149            }
150        }
151
152        private void video_title_changed (Audience.Objects.Video video) {
153             if (episodes.size == 1) {
154                 title_label.label = video.title;
155             } else {
156                 title_label.label = video.container;
157             }
158             title_label.show ();
159        }
160
161        private void set_new_cover () {
162            var image_filter = new Gtk.FileFilter ();
163            image_filter.set_filter_name (_("Image files"));
164            image_filter.add_mime_type ("image/*");
165
166            var filechooser = new Gtk.FileChooserNative (
167                _("Open"),
168                Audience.App.get_instance ().mainwindow,
169                Gtk.FileChooserAction.OPEN,
170                _("_Open"),
171                _("_Cancel")
172            );
173            filechooser.add_filter (image_filter);
174
175            if (filechooser.run () == Gtk.ResponseType.ACCEPT) {
176                Gdk.Pixbuf? pixbuf = manager.get_poster_from_file (filechooser.get_filename ());
177                if (pixbuf != null) {
178                    try {
179                        if (episodes.size == 1) {
180                            pixbuf.save (episodes.first ().video_file.get_path () + ".jpg", "jpeg");
181                            episodes.first ().set_new_poster (pixbuf);
182                            episodes.first ().initialize_poster.begin ();
183                        } else {
184                            manager.clear_cache.begin (poster_cache_file);
185                            pixbuf.save (episode_poster_path, "jpeg");
186                            create_episode_poster ();
187                        }
188                    } catch (Error e) {
189                        warning (e.message);
190                    }
191                }
192            }
193            filechooser.destroy ();
194        }
195
196        private void move_video_to_trash () {
197            debug (episodes.size.to_string ());
198            if (episodes.size == 1) {
199                var video = episodes.first ();
200                video.trashed ();
201                try {
202                    video.video_file.trash ();
203                    manager.deleted_items (video.video_file.get_path ());
204                } catch (Error e) {
205                    warning (e.message);
206                }
207            } else {
208                try {
209                    episodes.first ().video_file.get_parent ().trash ();
210                    manager.deleted_items (episodes.first ().video_file.get_parent ().get_path ());
211                } catch (Error e) {
212                    warning (e.message);
213                }
214            }
215        }
216
217        public void add_episode (Audience.Objects.Video episode) {
218            episode.trashed.connect (() => {
219                episodes.remove (episode);
220            });
221            episodes.add (episode);
222            if (episodes.size == 1) {
223                title_label.label = episode.title;
224            } else if (episodes.size == 2) {
225                title_label.label = episode.container;
226                create_episode_poster ();
227            }
228        }
229
230        public string get_title () {
231            return title_label.label;
232        }
233
234        public void create_episode_poster () {
235            if (FileUtils.test (poster_cache_file, FileTest.EXISTS)) {
236                try {
237                    poster.pixbuf = new Gdk.Pixbuf.from_file (poster_cache_file);
238                } catch (Error e) {
239                    warning (e.message);
240                }
241            } else if (FileUtils.test (episode_poster_path, FileTest.EXISTS)) {
242                poster.pixbuf = manager.get_poster_from_file (episode_poster_path);
243                try {
244                    poster.pixbuf.save (poster_cache_file, "jpeg");
245                } catch (Error e) {
246                    warning (e.message);
247                }
248            }
249        }
250    }
251}
252