1 /*
2  * frogr-create-new-set-dialog.c -- 'Create new set' dialog
3  *
4  * Copyright (C) 2010-2018 Mario Sanchez Prada
5  * Authors: Mario Sanchez Prada <msanchez@gnome.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 3 of the GNU General Public
9  * License as published by the Free Software Foundation.
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 GNU
14  * 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 "frogr-create-new-set-dialog.h"
22 
23 #include "frogr-photoset.h"
24 #include "frogr-controller.h"
25 #include "frogr-model.h"
26 #include "frogr-picture.h"
27 #include "frogr-util.h"
28 
29 #include <config.h>
30 #include <glib/gi18n.h>
31 
32 
33 struct _FrogrCreateNewSetDialog {
34   GtkDialog parent;
35 
36   GtkWidget *title_entry;
37   GtkWidget *description_tv;
38   GtkWidget *copy_to_pictures_cb;
39   GtkTextBuffer *description_buffer;
40 
41   GSList *pictures;
42   GSList *photosets;
43   gboolean copy_to_pictures;
44 };
45 
46 G_DEFINE_TYPE (FrogrCreateNewSetDialog, frogr_create_new_set_dialog, GTK_TYPE_DIALOG)
47 
48 
49 /* Properties */
50 enum  {
51   PROP_0,
52   PROP_PICTURES,
53   PROP_PHOTOSETS
54 };
55 
56 
57 /* Prototypes */
58 
59 static void _set_pictures (FrogrCreateNewSetDialog *self, const GSList *pictures);
60 
61 static void _set_photosets (FrogrCreateNewSetDialog *self, const GSList *photosets);
62 
63 static void _on_button_toggled (GtkToggleButton *button, gpointer data);
64 
65 static gboolean _validate_dialog_data (FrogrCreateNewSetDialog *self);
66 
67 static gboolean _save_data (FrogrCreateNewSetDialog *self);
68 
69 static void _update_model (FrogrCreateNewSetDialog *self,
70                            const gchar *title,
71                            const gchar *description);
72 
73 static void _dialog_response_cb (GtkDialog *dialog, gint response, gpointer data);
74 
75 /* Private API */
76 
77 static void
_set_pictures(FrogrCreateNewSetDialog * self,const GSList * pictures)78 _set_pictures (FrogrCreateNewSetDialog *self, const GSList *pictures)
79 {
80   self->pictures = g_slist_copy ((GSList*) pictures);
81   g_slist_foreach (self->pictures, (GFunc)g_object_ref, NULL);
82 }
83 
84 static void
_set_photosets(FrogrCreateNewSetDialog * self,const GSList * photosets)85 _set_photosets (FrogrCreateNewSetDialog *self, const GSList *photosets)
86 {
87   self->photosets = g_slist_copy ((GSList*)photosets);
88   g_slist_foreach (self->photosets, (GFunc)g_object_ref, NULL);
89 }
90 
91 static void
_on_button_toggled(GtkToggleButton * button,gpointer data)92 _on_button_toggled (GtkToggleButton *button, gpointer data)
93 {
94   FrogrCreateNewSetDialog *self = NULL;
95   gboolean active = FALSE;
96 
97   self = FROGR_CREATE_NEW_SET_DIALOG (data);
98   active = gtk_toggle_button_get_active (button);
99 
100   if (GTK_WIDGET (button) == self->copy_to_pictures_cb)
101     self->copy_to_pictures = active;
102 }
103 
104 static gboolean
_validate_dialog_data(FrogrCreateNewSetDialog * self)105 _validate_dialog_data (FrogrCreateNewSetDialog *self)
106 {
107   g_autofree gchar *title = NULL;
108   gboolean result = TRUE;
109 
110   /* Validate set's title */
111   title = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->title_entry)));
112   if ((title == NULL) || g_str_equal (g_strstrip (title), ""))
113     result = FALSE;
114 
115   return result;
116 }
117 
118 static gboolean
_save_data(FrogrCreateNewSetDialog * self)119 _save_data (FrogrCreateNewSetDialog *self)
120 {
121   GtkTextIter start;
122   GtkTextIter end;
123   g_autofree gchar *title = NULL;
124   g_autofree gchar *description = NULL;
125   gboolean result = FALSE;
126 
127   /* Save data */
128   title = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->title_entry)));
129   title = g_strstrip (title);
130 
131   gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (self->description_buffer),
132                               &start, &end);
133   description = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (self->description_buffer),
134                                           &start, &end, FALSE);
135   description = g_strstrip (description);
136 
137   /* validate and update */
138   result = _validate_dialog_data (self);
139   if (result)
140     _update_model (self, title, description);
141   else
142     frogr_util_show_error_dialog (GTK_WINDOW (self), _("Missing data required"));
143 
144   /* Return result */
145   return result;
146 }
147 
148 static void
_update_model(FrogrCreateNewSetDialog * self,const gchar * title,const gchar * description)149 _update_model (FrogrCreateNewSetDialog *self,
150                const gchar *title,
151                const gchar *description)
152 {
153   FrogrController *controller = NULL;
154   FrogrModel *model = NULL;
155   g_autoptr(FrogrPhotoSet) new_set = NULL;
156   FrogrPicture *picture = NULL;
157   GSList *item = NULL;
158 
159   controller = frogr_controller_get_instance ();
160   model = frogr_controller_get_model (controller);
161 
162   /* Add the set to the model */
163   new_set = frogr_photoset_new_local (title, description);
164   frogr_model_add_local_photoset (model, new_set);
165 
166   /* Add the set to the list of sets for each picture */
167   for (item = self->pictures; item; item = g_slist_next (item))
168     {
169       picture = FROGR_PICTURE (item->data);
170       frogr_picture_add_photoset (picture, new_set);
171 
172       /* Copy album's details over pictures if requested */
173       if (self->copy_to_pictures)
174         {
175           frogr_picture_set_title (picture, title);
176           frogr_picture_set_description (picture, description);
177         }
178     }
179 
180   frogr_model_notify_changes_in_pictures (model);
181 }
182 
183 static void
_dialog_response_cb(GtkDialog * dialog,gint response,gpointer data)184 _dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
185 {
186   FrogrCreateNewSetDialog *self = NULL;
187 
188   /* Try to save data if response is OK */
189   self = FROGR_CREATE_NEW_SET_DIALOG (dialog);
190   if (response == GTK_RESPONSE_ACCEPT && _save_data (self) == FALSE)
191     return;
192 
193   /* Destroy the dialog */
194   gtk_widget_destroy (GTK_WIDGET (dialog));
195 }
196 
197 static void
_frogr_create_new_set_dialog_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)198 _frogr_create_new_set_dialog_set_property (GObject *object,
199                                            guint prop_id,
200                                            const GValue *value,
201                                            GParamSpec *pspec)
202 {
203   switch (prop_id)
204     {
205     case PROP_PICTURES:
206       _set_pictures (FROGR_CREATE_NEW_SET_DIALOG (object), g_value_get_pointer (value));
207       break;
208     case PROP_PHOTOSETS:
209       _set_photosets (FROGR_CREATE_NEW_SET_DIALOG (object), g_value_get_pointer (value));
210       break;
211     default:
212       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
213       break;
214     }
215 }
216 
217 static void
_frogr_create_new_set_dialog_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)218 _frogr_create_new_set_dialog_get_property (GObject *object,
219                                            guint prop_id,
220                                            GValue *value,
221                                            GParamSpec *pspec)
222 {
223   FrogrCreateNewSetDialog *dialog = FROGR_CREATE_NEW_SET_DIALOG (object);
224 
225   switch (prop_id)
226     {
227     case PROP_PICTURES:
228       g_value_set_pointer (value, dialog->pictures);
229       break;
230     case PROP_PHOTOSETS:
231       g_value_set_pointer (value, dialog->photosets);
232       break;
233     default:
234       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
235       break;
236     }
237 }
238 
239 static void
_frogr_create_new_set_dialog_dispose(GObject * object)240 _frogr_create_new_set_dialog_dispose (GObject *object)
241 {
242   FrogrCreateNewSetDialog *dialog = FROGR_CREATE_NEW_SET_DIALOG (object);
243 
244   if (dialog->pictures)
245     {
246       g_slist_free_full (dialog->pictures, g_object_unref);
247       dialog->pictures = NULL;
248     }
249 
250   if (dialog->photosets)
251     {
252       g_slist_free_full (dialog->photosets, g_object_unref);
253       dialog->photosets = NULL;
254     }
255 
256   G_OBJECT_CLASS(frogr_create_new_set_dialog_parent_class)->dispose (object);
257 }
258 
259 static void
frogr_create_new_set_dialog_class_init(FrogrCreateNewSetDialogClass * klass)260 frogr_create_new_set_dialog_class_init (FrogrCreateNewSetDialogClass *klass)
261 {
262   GObjectClass *obj_class = (GObjectClass *)klass;
263   GParamSpec *pspec;
264 
265   /* GObject signals */
266   obj_class->set_property = _frogr_create_new_set_dialog_set_property;
267   obj_class->get_property = _frogr_create_new_set_dialog_get_property;
268   obj_class->dispose = _frogr_create_new_set_dialog_dispose;
269 
270   /* Install properties */
271   pspec = g_param_spec_pointer ("pictures",
272                                 "pictures",
273                                 "List of pictures for "
274                                 "the 'add to set' dialog",
275                                 G_PARAM_READWRITE
276                                 | G_PARAM_CONSTRUCT_ONLY);
277   g_object_class_install_property (obj_class, PROP_PICTURES, pspec);
278 
279   pspec = g_param_spec_pointer ("photosets",
280                                 "photosets",
281                                 "List of sets currently available "
282                                 "for the 'add to set' dialog",
283                                 G_PARAM_READWRITE
284                                 | G_PARAM_CONSTRUCT_ONLY);
285   g_object_class_install_property (obj_class, PROP_PHOTOSETS, pspec);
286 }
287 
288 static void
frogr_create_new_set_dialog_init(FrogrCreateNewSetDialog * self)289 frogr_create_new_set_dialog_init (FrogrCreateNewSetDialog *self)
290 {
291   GtkWidget *content_area = NULL;
292   GtkWidget *vbox = NULL;
293   GtkWidget *grid = NULL;
294   GtkWidget *scroller = NULL;
295   GtkWidget *widget = NULL;
296 
297   self->pictures = NULL;
298   self->photosets = NULL;
299 
300   /* Create widgets */
301   gtk_dialog_add_buttons (GTK_DIALOG (self),
302                           _("_Cancel"),
303                           GTK_RESPONSE_CANCEL,
304                           _("_Add"),
305                           GTK_RESPONSE_ACCEPT,
306                           NULL);
307   gtk_container_set_border_width (GTK_CONTAINER (self), 6);
308 
309   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
310   gtk_widget_set_margin_bottom (vbox, 12);
311   gtk_widget_show (vbox);
312 
313   grid = gtk_grid_new ();
314   gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
315   gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
316   gtk_box_pack_start (GTK_BOX (vbox), grid, TRUE, TRUE, 0);
317   gtk_widget_show (grid);
318 
319   widget = gtk_label_new (_("Title:"));
320   gtk_widget_set_halign (GTK_WIDGET (widget), GTK_ALIGN_END);
321   gtk_grid_attach (GTK_GRID (grid), widget, 0, 0, 1, 1);
322   gtk_widget_show (widget);
323 
324   widget = gtk_entry_new ();
325   gtk_widget_set_hexpand (GTK_WIDGET (widget), TRUE);
326   gtk_grid_attach (GTK_GRID (grid), widget, 1, 0, 1, 1);
327   self->title_entry = widget;
328   gtk_widget_show (widget);
329 
330   widget = gtk_label_new (_("Description:"));
331   gtk_widget_set_halign (GTK_WIDGET (widget), GTK_ALIGN_END);
332   gtk_widget_set_valign (GTK_WIDGET (widget), GTK_ALIGN_START);
333   gtk_grid_attach (GTK_GRID (grid), widget, 0, 1, 1, 1);
334   gtk_widget_show (widget);
335 
336   widget = gtk_text_view_new ();
337   scroller = gtk_scrolled_window_new (NULL, NULL);
338   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
339                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
340   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
341                                        GTK_SHADOW_ETCHED_IN);
342   gtk_container_add (GTK_CONTAINER (scroller), widget);
343   gtk_widget_show (widget);
344 
345   gtk_text_view_set_accepts_tab (GTK_TEXT_VIEW (widget), FALSE);
346   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (widget), GTK_WRAP_WORD);
347   gtk_widget_set_hexpand (GTK_WIDGET (scroller), TRUE);
348   gtk_widget_set_vexpand (GTK_WIDGET (scroller), TRUE);
349   gtk_grid_attach (GTK_GRID (grid), scroller, 1, 1, 1, 1);
350   gtk_widget_show (scroller);
351 
352   self->description_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
353   self->description_tv = widget;
354 
355   widget = gtk_check_button_new_with_mnemonic (_("Fill Pictures Details with Title and Description"));
356   gtk_widget_set_hexpand (GTK_WIDGET (widget), TRUE);
357   gtk_grid_attach (GTK_GRID (grid), widget, 1, 2, 1, 1);
358   gtk_widget_show (widget);
359   self->copy_to_pictures_cb = widget;
360 
361   content_area = gtk_dialog_get_content_area (GTK_DIALOG (self));
362   gtk_container_add (GTK_CONTAINER (content_area), vbox);
363   gtk_widget_show (content_area);
364 
365   g_signal_connect (G_OBJECT (self->copy_to_pictures_cb), "toggled",
366                     G_CALLBACK (_on_button_toggled),
367                     self);
368 
369   gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
370 
371   self->copy_to_pictures = FALSE;
372 }
373 
374 /* Public API */
375 
376 void
frogr_create_new_set_dialog_show(GtkWindow * parent,const GSList * pictures,const GSList * photosets)377 frogr_create_new_set_dialog_show (GtkWindow *parent,
378                                   const GSList *pictures,
379                                   const GSList *photosets)
380 {
381   GtkWidget *dialog = NULL;
382   dialog = GTK_WIDGET (g_object_new (FROGR_TYPE_CREATE_NEW_SET_DIALOG,
383                                      "title", _("Create New Set"),
384                                      "modal", TRUE,
385                                      "pictures", pictures,
386                                      "photosets", photosets,
387                                      "transient-for", parent,
388                                      "width-request", -1,
389                                      "height-request", 300,
390                                      "resizable", TRUE,
391 #if GTK_CHECK_VERSION (3, 12, 0)
392                                      "use-header-bar", USE_HEADER_BAR,
393 #endif
394                                      NULL));
395 
396   g_signal_connect (G_OBJECT (dialog), "response",
397                     G_CALLBACK (_dialog_response_cb), NULL);
398 
399   gtk_widget_show (dialog);
400 }
401