1 /*
2  * Photos - access, organize and share your photos on GNOME
3  * Copyright © 2013 Intel Corporation. All rights reserved.
4  * Copyright © 2013 – 2019 Red Hat, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 
21 #include "config.h"
22 
23 #include "photos-dlna-renderers-dialog.h"
24 
25 #include <gio/gio.h>
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 
29 #include "photos-base-manager.h"
30 #include "photos-dlna-renderer.h"
31 #include "photos-dlna-renderers-manager.h"
32 #include "photos-item-manager.h"
33 #include "photos-local-item.h"
34 #include "photos-remote-display-manager.h"
35 #include "photos-search-context.h"
36 #include "photos-utils.h"
37 
38 
39 struct _PhotosDlnaRenderersDialog
40 {
41   GtkDialog parent_instance;
42   PhotosBaseManager *item_mngr;
43   PhotosDlnaRenderersManager *renderers_mngr;
44   PhotosRemoteDisplayManager *remote_mngr;
45   PhotosModeController *mode_cntrlr;
46   GtkListBox *listbox;
47   gchar *urn;
48 };
49 
50 enum
51 {
52   PROP_0,
53   PROP_URN
54 };
55 
56 
57 G_DEFINE_TYPE (PhotosDlnaRenderersDialog, photos_dlna_renderers_dialog, GTK_TYPE_DIALOG);
58 
59 
60 static void
photos_dlna_renderers_dialog_row_activated_cb(PhotosDlnaRenderersDialog * self,GtkListBoxRow * row,gpointer user_data)61 photos_dlna_renderers_dialog_row_activated_cb (PhotosDlnaRenderersDialog *self,
62                                                GtkListBoxRow             *row,
63                                                gpointer                   user_data)
64 {
65   PhotosBaseItem *item;
66   PhotosDlnaRenderer *renderer;
67 
68   renderer = g_object_get_data (G_OBJECT (row), "renderer");
69   item = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (self->item_mngr,
70                                                                  self->urn));
71   photos_remote_display_manager_set_renderer (self->remote_mngr, renderer);
72   photos_remote_display_manager_render (self->remote_mngr, item);
73 
74   photos_mode_controller_go_back (self->mode_cntrlr);
75 
76   gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
77 }
78 
79 
80 static void
photos_dlna_renderers_dialog_dispose(GObject * object)81 photos_dlna_renderers_dialog_dispose (GObject *object)
82 {
83   PhotosDlnaRenderersDialog *self = PHOTOS_DLNA_RENDERERS_DIALOG (object);
84 
85   g_clear_object (&self->item_mngr);
86   g_clear_object (&self->renderers_mngr);
87   g_clear_object (&self->remote_mngr);
88   g_clear_object (&self->mode_cntrlr);
89 
90   G_OBJECT_CLASS (photos_dlna_renderers_dialog_parent_class)->dispose (object);
91 }
92 
93 
94 static void
photos_dlna_renderers_dialog_finalize(GObject * object)95 photos_dlna_renderers_dialog_finalize (GObject *object)
96 {
97   PhotosDlnaRenderersDialog *self = PHOTOS_DLNA_RENDERERS_DIALOG (object);
98 
99   g_free (self->urn);
100 
101   G_OBJECT_CLASS (photos_dlna_renderers_dialog_parent_class)->finalize (object);
102 }
103 
104 
105 static void
photos_dlna_renderers_dialog_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)106 photos_dlna_renderers_dialog_set_property (GObject      *object,
107                                            guint         prop_id,
108                                            const GValue *value,
109                                            GParamSpec   *pspec)
110 {
111   PhotosDlnaRenderersDialog *self = PHOTOS_DLNA_RENDERERS_DIALOG (object);
112 
113   switch (prop_id)
114     {
115     case PROP_URN:
116       self->urn = g_value_dup_string (value);
117       break;
118 
119     default:
120       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
121       break;
122     }
123 }
124 
125 
126 static void
photos_dlna_renderers_dialog_set_icon_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)127 photos_dlna_renderers_dialog_set_icon_cb (GObject      *source_object,
128                                           GAsyncResult *res,
129                                           gpointer      user_data)
130 {
131   GdkPixbuf *pixbuf;
132   GtkImage *image = GTK_IMAGE (user_data);
133   PhotosDlnaRenderer *renderer = PHOTOS_DLNA_RENDERER (source_object);
134   GError *error = NULL;
135 
136   pixbuf = photos_dlna_renderer_get_icon_finish (renderer, res, &error);
137   if (error != NULL)
138     {
139       g_warning ("Unable to load renderer icon: %s", error->message);
140       g_error_free (error);
141       goto out;
142     }
143 
144   gtk_image_set_from_pixbuf (image, pixbuf);
145 
146 out:
147   /* release the ref we took before the async call */
148   g_object_unref (image);
149 }
150 
151 
152 static void
photos_dlna_renderers_dialog_add_renderer(PhotosDlnaRenderersDialog * self,PhotosDlnaRenderer * renderer)153 photos_dlna_renderers_dialog_add_renderer (PhotosDlnaRenderersDialog *self, PhotosDlnaRenderer *renderer)
154 {
155   GIcon *icon;
156   GtkWidget *row;
157   GtkWidget *row_grid;
158   GtkWidget *image;
159   GtkWidget *label;
160   const gchar *name;
161 
162   row = gtk_list_box_row_new ();
163   gtk_container_add (GTK_CONTAINER (self->listbox), row);
164 
165   row_grid = gtk_grid_new ();
166   gtk_container_set_border_width (GTK_CONTAINER (row_grid), 12);
167   gtk_orientable_set_orientation (GTK_ORIENTABLE (row_grid), GTK_ORIENTATION_HORIZONTAL);
168   gtk_grid_set_column_spacing (GTK_GRID (row_grid), 12);
169   gtk_container_add (GTK_CONTAINER (row), row_grid);
170 
171   g_object_set_data_full (G_OBJECT (row), "renderer", g_object_ref (renderer), g_object_unref);
172 
173   name = photos_dlna_renderer_get_friendly_name (renderer);
174 
175   icon = g_themed_icon_new_with_default_fallbacks ("video-display-symbolic");
176   image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
177 
178   g_object_ref (image); /* keep a ref for the following async call and release it in the callback */
179   photos_dlna_renderer_get_icon (renderer,
180                                  "",
181                                  "",
182                                  GTK_ICON_SIZE_DIALOG,
183                                  NULL,
184                                  photos_dlna_renderers_dialog_set_icon_cb,
185                                  image);
186 
187   gtk_container_add (GTK_CONTAINER (row_grid), image);
188 
189   label = gtk_label_new (NULL);
190   gtk_label_set_text (GTK_LABEL (label), name);
191   gtk_container_add (GTK_CONTAINER (row_grid), label);
192 }
193 
194 
195 static void
photos_dlna_renderers_dialog_init(PhotosDlnaRenderersDialog * self)196 photos_dlna_renderers_dialog_init (PhotosDlnaRenderersDialog *self)
197 {
198   GApplication *app;
199   GList *renderers;
200   PhotosSearchContextState *state;
201 
202   app = g_application_get_default ();
203   state = photos_search_context_get_state (PHOTOS_SEARCH_CONTEXT (app));
204 
205   self->item_mngr = g_object_ref (state->item_mngr);
206   self->renderers_mngr = photos_dlna_renderers_manager_dup_singleton ();
207   self->remote_mngr = photos_remote_display_manager_dup_singleton ();
208   self->mode_cntrlr = g_object_ref (state->mode_cntrlr);
209 
210   gtk_widget_init_template (GTK_WIDGET (self));
211 
212   gtk_list_box_set_header_func (self->listbox, photos_utils_list_box_header_func, NULL, NULL);
213 
214   renderers = photos_dlna_renderers_manager_dup_renderers (self->renderers_mngr);
215 
216   while (renderers != NULL)
217     {
218       PhotosDlnaRenderer *renderer = PHOTOS_DLNA_RENDERER (renderers->data);
219 
220       photos_dlna_renderers_dialog_add_renderer (self, renderer);
221       renderers = g_list_delete_link (renderers, renderers);
222       g_object_unref (renderer);
223     }
224 
225   g_signal_connect (self, "response", G_CALLBACK (gtk_widget_destroy), NULL);
226 }
227 
228 
229 static void
photos_dlna_renderers_dialog_class_init(PhotosDlnaRenderersDialogClass * class)230 photos_dlna_renderers_dialog_class_init (PhotosDlnaRenderersDialogClass *class)
231 {
232   GObjectClass *object_class = G_OBJECT_CLASS (class);
233   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
234 
235   object_class->dispose = photos_dlna_renderers_dialog_dispose;
236   object_class->finalize = photos_dlna_renderers_dialog_finalize;
237   object_class->set_property = photos_dlna_renderers_dialog_set_property;
238 
239   g_object_class_install_property (object_class,
240                                    PROP_URN,
241                                    g_param_spec_string ("urn",
242                                                         "Uniform Resource Name",
243                                                         "An unique ID associated with this item",
244                                                         NULL,
245                                                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
246 
247   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Photos/dlna-renderers-dialog.ui");
248 
249   gtk_widget_class_bind_template_child (widget_class, PhotosDlnaRenderersDialog, listbox);
250   gtk_widget_class_bind_template_callback (widget_class, photos_dlna_renderers_dialog_row_activated_cb);
251 }
252 
253 
254 GtkWidget *
photos_dlna_renderers_dialog_new(GtkWindow * parent,const gchar * urn)255 photos_dlna_renderers_dialog_new (GtkWindow *parent, const gchar *urn)
256 {
257   g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
258 
259   return g_object_new (PHOTOS_TYPE_DLNA_RENDERERS_DIALOG,
260                        "transient-for", parent,
261                        "urn", urn,
262                        "use-header-bar", TRUE,
263                        NULL);
264 }
265