1 /*
2  * Copyright (c) 2005-2006 Jean-François Wauthy (pollux@xfce.org)
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #ifdef	HAVE_CONFIG_H
20 #include <config.h>
21 #endif /* !HAVE_CONFIG_H */
22 
23 #ifdef HAVE_STDLIB_H
24 #include <stdlib.h>
25 #endif
26 
27 #ifdef HAVE_STRING_H
28 #include <string.h>
29 #endif
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <time.h>
35 
36 #include <gtk/gtk.h>
37 #include <libxfce4util/libxfce4util.h>
38 #include <libxfce4ui/libxfce4ui.h>
39 
40 #include <gio/gio.h>
41 
42 #include <exo/exo.h>
43 
44 #include <libisofs.h>
45 #include <glib/gstdio.h>
46 
47 #include "xfburn-data-composition.h"
48 #include "xfburn-global.h"
49 
50 #include "xfburn-adding-progress.h"
51 #include "xfburn-composition.h"
52 #include "xfburn-burn-data-cd-composition-dialog.h"
53 #include "xfburn-burn-data-dvd-composition-dialog.h"
54 #include "xfburn-data-disc-usage.h"
55 #include "xfburn-main-window.h"
56 #include "xfburn-utils.h"
57 #include "xfburn-settings.h"
58 #include "xfburn-main.h"
59 
60 #define XFBURN_DATA_COMPOSITION_GET_PRIVATE(obj) (xfburn_data_composition_get_instance_private (XFBURN_DATA_COMPOSITION (obj)))
61 
62 enum
63 {
64   DATA_COMPOSITION_COLUMN_ICON,
65   DATA_COMPOSITION_COLUMN_CONTENT,
66   DATA_COMPOSITION_COLUMN_HUMANSIZE,
67   DATA_COMPOSITION_COLUMN_SIZE,
68   DATA_COMPOSITION_COLUMN_PATH,
69   DATA_COMPOSITION_COLUMN_TYPE,
70   DATA_COMPOSITION_N_COLUMNS
71 };
72 
73 typedef enum
74 {
75   DATA_COMPOSITION_TYPE_FILE,
76   DATA_COMPOSITION_TYPE_DIRECTORY
77 } DataCompositionEntryType;
78 
79 
80 /* thread parameters */
81 typedef struct {
82   GSList * filelist;
83   XfburnDataComposition *dc;
84 } ThreadAddFilesCLIParams;
85 
86 typedef struct {
87   XfburnDataComposition *dc;
88   GtkTreeModel *model;
89   GtkTreeIter iter_where_insert;
90   DataCompositionEntryType type;
91 } ThreadAddFilesActionParams;
92 
93 typedef struct {
94   XfburnDataComposition *composition;
95   DataCompositionEntryType type;
96   GtkWidget *widget;
97   GtkTreeViewDropPosition position;
98   GtkTreeIter iter_dummy;
99 } ThreadAddFilesDragParams;
100 
101 /* prototypes */
102 static void composition_interface_init (XfburnCompositionInterface *composition, gpointer iface_data);
103 static void xfburn_data_composition_finalize (GObject * object);
104 
105 static void show_custom_controls (XfburnComposition *composition);
106 static void hide_custom_controls (XfburnComposition *composition);
107 static void load_from_file (XfburnComposition *composition, const gchar *file);
108 static void save_to_file (XfburnComposition *composition);
109 
110 static gint directory_tree_sortfunc (GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer user_data);
111 
112 static void set_default_name (XfburnDataComposition * dc);
113 static gboolean has_default_name (XfburnDataComposition * dc);
114 
115 static void action_create_directory (GSimpleAction *, GVariant *, XfburnDataComposition *);
116 static void action_clear (GSimpleAction *, GVariant *, XfburnDataComposition *);
117 static void action_remove_selection (GSimpleAction *, GVariant *, XfburnDataComposition *);
118 static void action_rename_selection (GSimpleAction *, GVariant *, XfburnDataComposition *);
119 static void action_add_or_select (GSimpleAction *, GVariant *, XfburnDataComposition *);
120 static void add_files (gchar * files, XfburnDataComposition *);
121 static void add_cb (GtkWidget * widget, gpointer data);
122 
123 static gboolean cb_treeview_button_pressed (GtkTreeView * treeview, GdkEventButton * event, XfburnDataComposition * dc);
124 static void cb_treeview_row_activated (GtkTreeView * treeview, GtkTreePath * path, GtkTreeViewColumn * column, XfburnDataComposition * composition);
125 static void cb_selection_changed (GtkTreeSelection *selection, XfburnDataComposition * dc);
126 static void cb_begin_burn (XfburnDataDiscUsage * du, XfburnDataComposition * dc);
127 static void cb_cell_file_edited (GtkCellRenderer * renderer, gchar * path, gchar * newtext, XfburnDataComposition * dc);
128 
129 static void cb_content_drag_data_rcv (GtkWidget * widget, GdkDragContext * dc, guint x, guint y, GtkSelectionData * sd,
130                                       guint info, guint t, XfburnDataComposition * composition);
131 static void cb_content_drag_data_get (GtkWidget * widget, GdkDragContext * dc, GtkSelectionData * data, guint info,
132                                       guint timestamp, XfburnDataComposition * content);
133 static void cb_adding_done (XfburnAddingProgress *progress, XfburnDataComposition *dc);
134 
135 /* thread entry points */
136 static gpointer thread_add_files_cli (ThreadAddFilesCLIParams *params);
137 static gpointer thread_add_files_action (ThreadAddFilesActionParams *params);
138 static gpointer thread_add_files_drag (ThreadAddFilesDragParams *params);
139 
140 /* thread helpers */
141 static gboolean thread_add_file_to_list_with_name (const gchar *name, XfburnDataComposition * dc,
142                                                    GtkTreeModel * model, const gchar * path, GtkTreeIter * iter,
143                                                    GtkTreeIter * insertion, GtkTreeViewDropPosition position);
144 static gboolean thread_add_file_to_list (XfburnDataComposition * dc, GtkTreeModel * model, const gchar * path,
145                                          GtkTreeIter * iter, GtkTreeIter * insertion, GtkTreeViewDropPosition position);
146 static void concat_free (char * msg, char ** combined_msg);
147 static IsoImage * generate_iso_image (XfburnDataComposition * dc);
148 static gboolean show_add_home_question_dialog (void);
149 
150 typedef struct
151 {
152   gchar *filename;
153   gboolean modified;
154 
155   guint n_new_directory;
156 
157   GList *full_paths_to_add;
158   gchar *selected_files;
159   GtkTreePath *path_where_insert;
160 
161   GdkDragContext * dc;
162   gboolean success;
163   gboolean del;
164   guint32 time;
165 
166   void *thread_params;
167 
168   GSimpleActionGroup *action_group;
169   GtkBuilder *ui_manager;
170 
171   GtkWidget *toolbar;
172   GtkWidget *entry_volume_name;
173   GtkWidget *content;
174   GtkWidget *disc_usage;
175   GtkWidget *progress;
176   GtkTreeStore *model;
177   GtkWidget *add_filechooser;
178   gchar * last_directory;
179   GtkWidget *add_window;
180 
181   gchar *default_vol_name;
182 
183   gboolean large_files;
184 } XfburnDataCompositionPrivate;
185 
186 /* globals */
187 static GtkHPanedClass *parent_class = NULL;
188 static guint instances = 0;
189 
190 static const GActionEntry action_entries[] = {
191   {.name = "add-file", .activate = (gActionCallback)action_add_or_select},
192   {.name = "create-dir", .activate = (gActionCallback)action_create_directory},
193   {.name = "remove-file", .activate = (gActionCallback)action_remove_selection},
194   {.name = "clear", .activate = (gActionCallback)action_clear},
195   /*{.name = "import-session", .activate = (gActionCallback)action_remove_selection},*/
196   {.name = "rename-file", .activate = (gActionCallback)action_rename_selection},
197 };
198 /*
199 static const GtkActionEntry action_entries[] = {
200   {"add-file", "list-add", N_("Add"), NULL, N_("Add the selected file(s) to the composition"),
201    G_CALLBACK (action_add_or_select),},
202   {"create-dir", "document-new", N_("Create directory"), NULL, N_("Add a new directory to the composition"),
203    G_CALLBACK (action_create_directory),},
204   {"remove-file", "list-remove", N_("Remove"), NULL, N_("Remove the selected file(s) from the composition"),
205    G_CALLBACK (action_remove_selection),},
206   {"clear", "edit-clear", N_("Clear"), NULL, N_("Clear the content of the composition"),
207    G_CALLBACK (action_clear),},
208   /*{"import-session", "xfburn-import-session", N_("Import"), NULL, N_("Import existing session"),}, */
209 /*  {"rename-file", "gtk-edit", N_("Rename"), NULL, N_("Rename the selected file"),
210    G_CALLBACK (action_rename_selection),},
211 };*/
212 
213 static const gchar *toolbar_actions[] = {
214   "add-file",
215   "remove-file",
216   "create-dir",
217   "clear",
218   "import-session",
219 };
220 
221 static GdkPixbuf *icon_directory = NULL, *icon_file = NULL;
222 
223 /***************************/
224 /* XfburnDataComposition class */
225 /***************************/
226 G_DEFINE_TYPE_EXTENDED(
227   XfburnDataComposition,
228   xfburn_data_composition,
229   GTK_TYPE_BOX,
230   0,
231   G_ADD_PRIVATE (XfburnDataComposition)
232   G_IMPLEMENT_INTERFACE (XFBURN_TYPE_COMPOSITION, composition_interface_init)
233 );
234 
235 
236 static void
xfburn_data_composition_class_init(XfburnDataCompositionClass * klass)237 xfburn_data_composition_class_init (XfburnDataCompositionClass * klass)
238 {
239   GObjectClass *object_class = G_OBJECT_CLASS (klass);
240 
241   parent_class = g_type_class_peek_parent (klass);
242 
243   object_class->finalize = xfburn_data_composition_finalize;
244 }
245 
246 static void
composition_interface_init(XfburnCompositionInterface * composition,gpointer iface_data)247 composition_interface_init (XfburnCompositionInterface *composition, gpointer iface_data)
248 {
249   composition->show_custom_controls = show_custom_controls;
250   composition->hide_custom_controls = hide_custom_controls;
251   composition->load = load_from_file;
252   composition->save = save_to_file;
253 }
254 
255 static void
xfburn_data_composition_init(XfburnDataComposition * composition)256 xfburn_data_composition_init (XfburnDataComposition * composition)
257 {
258   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (composition);
259 
260   gtk_orientable_set_orientation(GTK_ORIENTABLE (composition), GTK_ORIENTATION_VERTICAL);
261 
262   gint x, y;
263 //  ExoToolbarsModel *model_toolbar;
264   gint toolbar_position;
265   GtkWidget *hbox_toolbar;
266   GtkWidget *hbox, *label;
267   GtkWidget *scrolled_window;
268   GtkTreeStore *model;
269   GtkTreeViewColumn *column_file;
270   GtkCellRenderer *cell_icon, *cell_file;
271   GtkTreeSelection *selection;
272   GAction *action = NULL;
273   GdkScreen *screen;
274   GtkIconTheme *icon_theme;
275 
276   GtkTargetEntry gte_src[] =  { { "XFBURN_TREE_PATHS", GTK_TARGET_SAME_WIDGET, DATA_COMPOSITION_DND_TARGET_INSIDE } };
277   GtkTargetEntry gte_dest[] = { { "XFBURN_TREE_PATHS", GTK_TARGET_SAME_WIDGET, DATA_COMPOSITION_DND_TARGET_INSIDE },
278                                 { "text/uri-list", 0, DATA_COMPOSITION_DND_TARGET_TEXT_URI_LIST },
279                                 { "text/plain;charset=utf-8", 0, DATA_COMPOSITION_DND_TARGET_TEXT_PLAIN },
280                               };
281 
282   priv->full_paths_to_add = NULL;
283 
284   instances++;
285 
286   /* initialize static members */
287   screen = gtk_widget_get_screen (GTK_WIDGET (composition));
288   icon_theme = gtk_icon_theme_get_for_screen (screen);
289 
290   gtk_icon_size_lookup (GTK_ICON_SIZE_SMALL_TOOLBAR, &x, &y);
291   if (!icon_directory)
292     icon_directory = gtk_icon_theme_load_icon (icon_theme, "folder", x, 0, NULL);
293   if (!icon_file)
294     icon_file = gtk_icon_theme_load_icon (icon_theme, "gnome-fs-regular", x, 0, NULL);
295 
296   /* create ui manager */
297   priv->action_group = g_simple_action_group_new ();
298   g_action_map_add_action_entries (G_ACTION_MAP (priv->action_group), action_entries, G_N_ELEMENTS (action_entries),
299                                 GTK_WIDGET (composition));
300 
301   priv->ui_manager = gtk_builder_new ();
302   gtk_builder_set_translation_domain(priv->ui_manager, GETTEXT_PACKAGE);
303 
304   xfce_resource_push_path (XFCE_RESOURCE_DATA, DATADIR);
305   gchar *popup_ui = xfce_resource_lookup (XFCE_RESOURCE_DATA, "xfburn/xfburn-popup-menus.ui");
306   gtk_builder_add_from_file (priv->ui_manager, popup_ui, NULL);
307 
308   hbox_toolbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
309   gtk_box_pack_start (GTK_BOX (composition), hbox_toolbar, FALSE, TRUE, 0);
310   gtk_widget_show (hbox_toolbar);
311 
312   /* toolbar */
313 /*  model_toolbar = exo_toolbars_model_new ();
314   exo_toolbars_model_set_actions (model_toolbar, (gchar **) toolbar_actions, G_N_ELEMENTS (toolbar_actions));
315   toolbar_position = exo_toolbars_model_add_toolbar (model_toolbar, -1, "content-toolbar");
316   exo_toolbars_model_set_style (model_toolbar, GTK_TOOLBAR_BOTH, toolbar_position);
317 
318   exo_toolbars_model_add_item (model_toolbar, toolbar_position, -1, "add-file", EXO_TOOLBARS_ITEM_TYPE);
319   exo_toolbars_model_add_item (model_toolbar, toolbar_position, -1, "create-dir", EXO_TOOLBARS_ITEM_TYPE);
320   exo_toolbars_model_add_separator (model_toolbar, toolbar_position, -1);
321   exo_toolbars_model_add_item (model_toolbar, toolbar_position, -1, "remove-file", EXO_TOOLBARS_ITEM_TYPE);
322   exo_toolbars_model_add_item (model_toolbar, toolbar_position, -1, "clear", EXO_TOOLBARS_ITEM_TYPE);
323   //exo_toolbars_model_add_separator (model_toolbar, toolbar_position, -1);
324   //exo_toolbars_model_add_item (model_toolbar, toolbar_position, -1, "import-session", EXO_TOOLBARS_ITEM_TYPE);
325 
326   priv->toolbar = exo_toolbars_view_new_with_model (priv->ui_manager, model_toolbar);
327 */
328   priv->toolbar = gtk_toolbar_new ();
329   gtk_toolbar_set_style(GTK_TOOLBAR (priv->toolbar), GTK_TOOLBAR_BOTH);
330   gtk_widget_insert_action_group (priv->toolbar, "win", G_ACTION_GROUP (priv->action_group));
331 
332   xfburn_add_button_to_toolbar (GTK_TOOLBAR (priv->toolbar),
333     "list-add", _("Add"), "win.add-file", _("Add the selected file(s) to the composition"));
334   xfburn_add_button_to_toolbar (GTK_TOOLBAR (priv->toolbar),
335     "folder-new", _("Create directory"), "win.create-dir", _("Add a new directory to the composition"));
336 
337   gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), gtk_separator_tool_item_new(), -1);
338 
339   xfburn_add_button_to_toolbar (GTK_TOOLBAR (priv->toolbar),
340     "list-remove", _("Remove"), "win.remove-file", _("Remove the selected file(s) from the composition"));
341   xfburn_add_button_to_toolbar (GTK_TOOLBAR (priv->toolbar),
342     "edit-clear", _("Clear"), "win.clear", _("Clear the content of the composition"));
343 
344   gtk_box_pack_start (GTK_BOX (hbox_toolbar), priv->toolbar, TRUE, TRUE, 0);
345   gtk_widget_show_all (priv->toolbar);
346 
347   /* volume name */
348   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
349   gtk_container_set_border_width (GTK_CONTAINER (hbox), 10);
350   gtk_box_pack_start (GTK_BOX (hbox_toolbar), hbox, FALSE, FALSE, 0);
351   gtk_widget_show (hbox);
352 
353   label = gtk_label_new (_("Volume name :"));
354   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
355   gtk_widget_show (label);
356 
357   priv->entry_volume_name = gtk_entry_new ();
358 
359   set_default_name (composition);
360 
361   gtk_box_pack_start (GTK_BOX (hbox), priv->entry_volume_name, FALSE, FALSE, 0);
362   gtk_widget_show (priv->entry_volume_name);
363 
364   /* content treeview */
365   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
366   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
367   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
368   gtk_widget_show (scrolled_window);
369   gtk_box_pack_start (GTK_BOX (composition), scrolled_window, TRUE, TRUE, 0);
370 
371   priv->content = exo_tree_view_new ();
372   model = gtk_tree_store_new (DATA_COMPOSITION_N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
373                               G_TYPE_UINT64, G_TYPE_STRING, G_TYPE_UINT);
374   priv->model = model;
375 
376   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model), DATA_COMPOSITION_COLUMN_CONTENT,
377                                    directory_tree_sortfunc, NULL, NULL);
378   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), DATA_COMPOSITION_COLUMN_CONTENT, GTK_SORT_ASCENDING);
379   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->content), GTK_TREE_MODEL (model));
380   // gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (priv->content), TRUE);
381   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->content));
382   gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
383   gtk_widget_show (priv->content);
384   gtk_container_add (GTK_CONTAINER (scrolled_window), priv->content);
385 
386   column_file = gtk_tree_view_column_new ();
387   gtk_tree_view_column_set_title (column_file, _("Contents"));
388 
389   cell_icon = gtk_cell_renderer_pixbuf_new ();
390   gtk_tree_view_column_pack_start (column_file, cell_icon, FALSE);
391   gtk_tree_view_column_set_attributes (column_file, cell_icon, "pixbuf", DATA_COMPOSITION_COLUMN_ICON, NULL);
392   g_object_set (cell_icon, "xalign", 0.0, "ypad", 0, NULL);
393 
394   cell_file = gtk_cell_renderer_text_new ();
395   gtk_tree_view_column_pack_start (column_file, cell_file, TRUE);
396   gtk_tree_view_column_set_attributes (column_file, cell_file, "text", DATA_COMPOSITION_COLUMN_CONTENT, NULL);
397   g_signal_connect (G_OBJECT (cell_file), "edited", G_CALLBACK (cb_cell_file_edited), composition);
398   g_object_set (G_OBJECT (cell_file), "editable", TRUE, NULL);
399 
400   gtk_tree_view_append_column (GTK_TREE_VIEW (priv->content), column_file);
401 
402   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->content), -1, _("Size"),
403                                                gtk_cell_renderer_text_new (), "text", DATA_COMPOSITION_COLUMN_HUMANSIZE,
404                                                NULL);
405   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->content), -1, _("Local Path"),
406                                                gtk_cell_renderer_text_new (), "text", DATA_COMPOSITION_COLUMN_PATH, NULL);
407 
408   /* Contents column */
409   gtk_tree_view_column_set_resizable (gtk_tree_view_get_column (GTK_TREE_VIEW (priv->content), 0), 1);
410   gtk_tree_view_column_set_min_width (gtk_tree_view_get_column (GTK_TREE_VIEW (priv->content), 0), 200);
411   /* Size (HUMANSIZE) column */
412   gtk_tree_view_column_set_resizable (gtk_tree_view_get_column (GTK_TREE_VIEW (priv->content), 1), 1);
413   gtk_tree_view_column_set_min_width (gtk_tree_view_get_column (GTK_TREE_VIEW (priv->content), 1), 60);
414   /* Local Path (PATH) column */
415   gtk_tree_view_column_set_resizable (gtk_tree_view_get_column (GTK_TREE_VIEW (priv->content), 2), 1);
416 
417 
418   g_signal_connect (G_OBJECT (priv->content), "row-activated", G_CALLBACK (cb_treeview_row_activated), composition);
419   g_signal_connect (G_OBJECT (priv->content), "button-press-event",
420                     G_CALLBACK (cb_treeview_button_pressed), composition);
421   g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (cb_selection_changed), composition);
422 
423   /* adding progress window */
424   priv->progress = GTK_WIDGET (xfburn_adding_progress_new ());
425   g_signal_connect (G_OBJECT (priv->progress), "adding-done", G_CALLBACK (cb_adding_done), composition);
426   gtk_window_set_transient_for (GTK_WINDOW (priv->progress), GTK_WINDOW (xfburn_main_window_get_instance ()));
427   /* FIXME: progress should have a busy cursor */
428 
429   /* disc usage */
430   priv->disc_usage = xfburn_data_disc_usage_new ();
431   gtk_box_pack_start (GTK_BOX (composition), priv->disc_usage, FALSE, FALSE, 5);
432   gtk_widget_show (priv->disc_usage);
433   g_signal_connect (G_OBJECT (priv->disc_usage), "begin-burn", G_CALLBACK (cb_begin_burn), composition);
434 
435   /* set up DnD */
436   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (priv->content), GDK_BUTTON1_MASK, gte_src,
437                                           G_N_ELEMENTS (gte_src), GDK_ACTION_COPY | GDK_ACTION_MOVE);
438   g_signal_connect (G_OBJECT (priv->content), "drag-data-get", G_CALLBACK (cb_content_drag_data_get),
439                     composition);
440   gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (priv->content), gte_dest, G_N_ELEMENTS (gte_dest),
441                                         GDK_ACTION_COPY | GDK_ACTION_MOVE);
442   g_signal_connect (G_OBJECT (priv->content), "drag-data-received", G_CALLBACK (cb_content_drag_data_rcv),
443                     composition);
444 
445   action = g_action_map_lookup_action (G_ACTION_MAP (priv->action_group), "remove-file");
446   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
447 }
448 
449 static void
xfburn_data_composition_finalize(GObject * object)450 xfburn_data_composition_finalize (GObject * object)
451 {
452   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (object);
453 
454   g_free (priv->filename);
455 
456   /* free static members */
457   instances--;
458   if (instances == 0) {
459     if (icon_directory) {
460       g_object_unref (icon_directory);
461       icon_directory = NULL;
462     }
463     if (icon_file) {
464       g_object_unref (icon_file);
465       icon_file = NULL;
466     }
467   }
468 
469   g_object_unref (priv->model);
470 
471   G_OBJECT_CLASS (parent_class)->finalize (object);
472 }
473 
474 /*************/
475 /* internals */
476 /*************/
477 static void
show_custom_controls(XfburnComposition * composition)478 show_custom_controls (XfburnComposition *composition)
479 {
480   DBG ("show");
481 }
482 
483 static void
hide_custom_controls(XfburnComposition * composition)484 hide_custom_controls (XfburnComposition *composition)
485 {
486   DBG ("hide");
487 }
488 
489 static void
cb_begin_burn(XfburnDataDiscUsage * du,XfburnDataComposition * dc)490 cb_begin_burn (XfburnDataDiscUsage * du, XfburnDataComposition * dc)
491 {
492   XfburnMainWindow *mainwin = xfburn_main_window_get_instance ();
493   GtkWidget *dialog = NULL;
494   IsoImage *image = NULL;
495 
496   if (!iso_init()) {
497     g_critical ("Could not initialize libisofs!");
498     return;
499   }
500 
501   image = generate_iso_image (XFBURN_DATA_COMPOSITION (dc));
502 
503   switch (xfburn_disc_usage_get_disc_type (XFBURN_DISC_USAGE (du))) {
504   case CD_DISC:
505     dialog = xfburn_burn_data_cd_composition_dialog_new (image, has_default_name (dc));
506     break;
507   case DVD_DISC:
508     dialog = xfburn_burn_data_dvd_composition_dialog_new (image);
509     break;
510   }
511 
512   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (mainwin));
513   gtk_dialog_run (GTK_DIALOG (dialog));
514   gtk_widget_destroy (dialog);
515 }
516 
517 static void
cb_treeview_row_activated(GtkTreeView * treeview,GtkTreePath * path,GtkTreeViewColumn * column,XfburnDataComposition * composition)518 cb_treeview_row_activated (GtkTreeView * treeview, GtkTreePath * path, GtkTreeViewColumn * column,
519                          XfburnDataComposition * composition)
520 {
521   gtk_tree_view_expand_row (treeview, path, FALSE);
522 }
523 
524 static void
cb_selection_changed(GtkTreeSelection * selection,XfburnDataComposition * dc)525 cb_selection_changed (GtkTreeSelection *selection, XfburnDataComposition * dc)
526 {
527   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
528   gint n_selected_rows;
529   GAction* action[] = {
530     g_action_map_lookup_action (G_ACTION_MAP (priv->action_group), "add-file"),
531     g_action_map_lookup_action (G_ACTION_MAP (priv->action_group), "create-dir"),
532     g_action_map_lookup_action (G_ACTION_MAP (priv->action_group), "remove-file"),
533   };
534 
535   n_selected_rows = gtk_tree_selection_count_selected_rows (selection);
536   if (n_selected_rows == 0) {
537     g_simple_action_set_enabled (G_SIMPLE_ACTION (action[0]), TRUE);
538     g_simple_action_set_enabled (G_SIMPLE_ACTION (action[1]), TRUE);
539     g_simple_action_set_enabled (G_SIMPLE_ACTION (action[2]), FALSE);
540   } else if (n_selected_rows == 1) {
541     g_simple_action_set_enabled (G_SIMPLE_ACTION (action[0]), TRUE);
542     g_simple_action_set_enabled (G_SIMPLE_ACTION (action[1]), TRUE);
543     g_simple_action_set_enabled (G_SIMPLE_ACTION (action[2]), TRUE);
544   } else {
545     g_simple_action_set_enabled (G_SIMPLE_ACTION (action[0]), FALSE);
546     g_simple_action_set_enabled (G_SIMPLE_ACTION (action[1]), FALSE);
547     g_simple_action_set_enabled (G_SIMPLE_ACTION (action[2]), TRUE);
548   }
549 }
550 
551 static gboolean
cb_treeview_button_pressed(GtkTreeView * treeview,GdkEventButton * event,XfburnDataComposition * dc)552 cb_treeview_button_pressed (GtkTreeView * treeview, GdkEventButton * event, XfburnDataComposition * dc)
553 {
554   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
555 
556   if ((event->button == 1) && (event->type == GDK_BUTTON_PRESS)) {
557     GtkTreePath *path;
558 
559     if (gtk_tree_view_get_path_at_pos (treeview, event->x, event->y, &path, NULL, NULL, NULL)) {
560       gtk_tree_path_free (path);
561     } else {
562       GtkTreeSelection *selection;
563 
564       selection = gtk_tree_view_get_selection (treeview);
565       gtk_tree_selection_unselect_all (selection);
566     }
567 
568     return FALSE;
569   }
570 
571   if ((event->button == 3) && (event->type == GDK_BUTTON_PRESS)) {
572     GtkTreeSelection *selection;
573     GtkTreePath *path;
574     GMenuModel *model;
575     GtkWidget *menu_popup;
576     GtkWidget *menuitem_remove;
577     GtkWidget *menuitem_rename;
578 
579     selection = gtk_tree_view_get_selection (treeview);
580 
581     if (gtk_tree_view_get_path_at_pos (treeview, event->x, event->y, &path, NULL, NULL, NULL)) {
582       gtk_tree_selection_unselect_all (selection);
583       gtk_tree_selection_select_path (selection, path);
584       gtk_tree_path_free (path);
585     }
586 
587     model = G_MENU_MODEL (gtk_builder_get_object (priv->ui_manager, "data-popup-menu"));
588     menu_popup = gtk_menu_new_from_model (model);
589     gtk_widget_insert_action_group(GTK_WIDGET(menu_popup), "win", G_ACTION_GROUP(priv->action_group));
590 
591     GList *childs = gtk_container_get_children (GTK_CONTAINER (menu_popup));
592     menuitem_remove = GTK_WIDGET (childs->next->next->data);
593     menuitem_rename = GTK_WIDGET (childs->next->data);
594 
595     if (gtk_tree_selection_count_selected_rows (selection) >= 1)
596       gtk_widget_set_sensitive (menuitem_remove, TRUE);
597     else
598       gtk_widget_set_sensitive (menuitem_remove, FALSE);
599     if (gtk_tree_selection_count_selected_rows (selection) == 1)
600       gtk_widget_set_sensitive (menuitem_rename, TRUE);
601     else
602       gtk_widget_set_sensitive (menuitem_rename, FALSE);
603 
604     GdkRectangle r = {event->x, event->y, 1, 1};
605     gtk_menu_popup_at_rect (GTK_MENU (menu_popup),
606         gtk_widget_get_parent_window (GTK_WIDGET (treeview)),
607         &r, GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
608     return TRUE;
609   }
610 
611   return FALSE;
612 }
613 
614 static gint
directory_tree_sortfunc(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)615 directory_tree_sortfunc (GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer user_data)
616 {
617   /* adapted from gnomebaker */
618   gchar *aname, *bname;
619   DataCompositionEntryType atype = -1, btype = -1;
620   gint result = 0;
621 
622   gtk_tree_model_get (model, a, DATA_COMPOSITION_COLUMN_CONTENT, &aname, DATA_COMPOSITION_COLUMN_TYPE, &atype, -1);
623   gtk_tree_model_get (model, b, DATA_COMPOSITION_COLUMN_CONTENT, &bname, DATA_COMPOSITION_COLUMN_TYPE, &btype, -1);
624 
625   if ( (atype == DATA_COMPOSITION_TYPE_DIRECTORY) && (btype != DATA_COMPOSITION_TYPE_DIRECTORY) )
626     result = -1;
627   else if ( (atype != DATA_COMPOSITION_TYPE_DIRECTORY) && (btype == DATA_COMPOSITION_TYPE_DIRECTORY) )
628     result = 1;
629   else
630     result = g_ascii_strcasecmp (aname, bname);
631 
632   g_free (aname);
633   g_free (bname);
634 
635   return result;
636 }
637 
638 static gboolean
file_exists_on_same_level(GtkTreeModel * model,GtkTreePath * path,gboolean skip_path,const gchar * filename)639 file_exists_on_same_level (GtkTreeModel * model, GtkTreePath * path, gboolean skip_path, const gchar *filename)
640 {
641   GtkTreePath *current_path = NULL;
642   GtkTreeIter current_iter;
643 
644   current_path = gtk_tree_path_copy (path);
645   for (;gtk_tree_path_prev (current_path););
646 
647   if (gtk_tree_model_get_iter (model, &current_iter, current_path)) {
648     do {
649       gchar *current_filename = NULL;
650 
651       if (skip_path && gtk_tree_path_compare (path, current_path) == 0) {
652         gtk_tree_path_next (current_path);
653         continue;
654       }
655 
656       gtk_tree_model_get (model, &current_iter, DATA_COMPOSITION_COLUMN_CONTENT, &current_filename, -1);
657       if (strcmp (current_filename, filename) == 0) {
658         g_free (current_filename);
659         gtk_tree_path_free (current_path);
660         return TRUE;
661       }
662 
663       g_free (current_filename);
664       gtk_tree_path_next (current_path);
665     } while (gtk_tree_model_iter_next (model, &current_iter));
666   }
667 
668   gtk_tree_path_free (current_path);
669   return FALSE;
670 }
671 
672 static void
cb_cell_file_edited(GtkCellRenderer * renderer,gchar * path,gchar * newtext,XfburnDataComposition * dc)673 cb_cell_file_edited (GtkCellRenderer * renderer, gchar * path, gchar * newtext, XfburnDataComposition * dc)
674 {
675   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
676 
677   GtkTreeIter iter;
678   GtkTreeModel *model;
679   GtkTreePath *real_path;
680 
681   if (strlen (newtext) == 0) {
682     xfce_dialog_show_error (NULL, NULL, _("You must give a name to the file."));
683     return;
684   }
685 
686   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->content));
687   real_path = gtk_tree_path_new_from_string (path);
688 
689   if (gtk_tree_model_get_iter (model, &iter, real_path)) {
690     if (file_exists_on_same_level (model, real_path, TRUE, newtext)) {
691       xfce_dialog_show_error (NULL, NULL, _("A file with the same name is already present in the composition."));
692     }
693     else {
694       gtk_tree_store_set (GTK_TREE_STORE (model), &iter, DATA_COMPOSITION_COLUMN_CONTENT, newtext, -1);
695     }
696   }
697 
698   gtk_tree_path_free (real_path);
699 }
700 
701 static void
cb_adding_done(XfburnAddingProgress * progress,XfburnDataComposition * dc)702 cb_adding_done (XfburnAddingProgress *progress, XfburnDataComposition *dc)
703 {
704   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
705 
706   gtk_widget_hide (priv->progress);
707 
708   if (priv->selected_files) {
709     g_free (priv->selected_files);
710     priv->selected_files = NULL;
711   }
712 
713   if (priv->path_where_insert) {
714     gtk_tree_path_free (priv->path_where_insert);
715     priv->path_where_insert = NULL;
716   }
717 
718   if (priv->full_paths_to_add) {
719     g_list_free_full (priv->full_paths_to_add, (GDestroyNotify) g_free);
720     priv->full_paths_to_add = NULL;
721   }
722 
723   g_free (priv->thread_params);
724   xfburn_default_cursor (priv->content);
725 }
726 
727 static void
action_rename_selection(GSimpleAction * action,GVariant * param,XfburnDataComposition * dc)728 action_rename_selection (GSimpleAction *action, GVariant *param, XfburnDataComposition *dc)
729 {
730   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
731 
732   GtkTreeSelection *selection;
733   GtkTreeModel *model;
734   GList *list;
735   GtkTreePath *path;
736   GtkTreeViewColumn *column;
737 
738   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->content));
739   list = gtk_tree_selection_get_selected_rows (selection, &model);
740 
741   path = (GtkTreePath *) list->data;
742   column = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->content), DATA_COMPOSITION_COLUMN_CONTENT - 1);
743   /* -1 because of COLUMN_ICON */
744 
745   gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->content), path, column, TRUE);
746 
747   gtk_tree_path_free (path);
748   g_list_free (list);
749 }
750 
751 static void
action_create_directory(GSimpleAction * action,GVariant * param,XfburnDataComposition * dc)752 action_create_directory (GSimpleAction *action, GVariant *param, XfburnDataComposition *dc)
753 {
754   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
755 
756   GtkTreeModel *model;
757   GtkTreeSelection *selection;
758   GList *selected_paths = NULL;
759   GtkTreePath *path_where_insert = NULL;
760   GtkTreeIter iter_where_insert, iter_directory;
761   DataCompositionEntryType type = -1;
762   gchar *humansize = NULL;
763 
764   GtkTreePath *inserted_path = NULL;
765   gchar *directory_text = NULL;
766 
767   GtkTreeViewColumn *column;
768   GtkTreePath *path = NULL;
769 
770   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->content));
771   selected_paths = gtk_tree_selection_get_selected_rows (selection, NULL);
772   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->content));
773 
774   if (selected_paths) {
775     path_where_insert = (GtkTreePath *) (selected_paths->data);
776 
777     gtk_tree_model_get_iter (model, &iter_where_insert, path_where_insert);
778     gtk_tree_model_get (model, &iter_where_insert, DATA_COMPOSITION_COLUMN_TYPE, &type, -1);
779   }
780 
781   if (type == DATA_COMPOSITION_TYPE_DIRECTORY) {
782     gtk_tree_store_append (GTK_TREE_STORE (model), &iter_directory, &iter_where_insert);
783     gtk_tree_view_expand_row (GTK_TREE_VIEW (priv->content), path_where_insert, FALSE);
784   } else if (type == DATA_COMPOSITION_TYPE_FILE) {
785     GtkTreeIter parent;
786 
787     if (gtk_tree_model_iter_parent (model, &parent, &iter_where_insert))
788       gtk_tree_store_append (GTK_TREE_STORE (model), &iter_directory, &parent);
789     else
790       gtk_tree_store_append (GTK_TREE_STORE (model), &iter_directory, NULL);
791   } else {
792     gtk_tree_store_append (GTK_TREE_STORE (model), &iter_directory, NULL);
793   }
794 
795   humansize = xfburn_humanreadable_filesize (4);
796 
797   inserted_path = gtk_tree_model_get_path (model, &iter_directory);
798   if (file_exists_on_same_level (model, inserted_path, TRUE, _("New directory")))
799     directory_text = g_strdup_printf ("%s %d", _("New directory"), ++(priv->n_new_directory));
800   else
801     directory_text = g_strdup (_("New directory"));
802   gtk_tree_path_free (inserted_path);
803 
804   gtk_tree_store_set (GTK_TREE_STORE (model), &iter_directory,
805                       DATA_COMPOSITION_COLUMN_ICON, icon_directory,
806                       DATA_COMPOSITION_COLUMN_CONTENT, directory_text,
807                       DATA_COMPOSITION_COLUMN_HUMANSIZE, humansize,
808                       DATA_COMPOSITION_COLUMN_SIZE, (guint64) 4,
809                       DATA_COMPOSITION_COLUMN_TYPE, DATA_COMPOSITION_TYPE_DIRECTORY, -1);
810   g_free (directory_text);
811   g_free (humansize);
812 
813   xfburn_disc_usage_add_size (XFBURN_DISC_USAGE (priv->disc_usage), 4);
814 
815   gtk_widget_realize (priv->content);
816 
817   /* put the cell renderer in edition mode */
818   column = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->content), DATA_COMPOSITION_COLUMN_CONTENT - 1);
819   /* -1 because of COLUMN_ICON */
820   path = gtk_tree_model_get_path (model, &iter_directory);
821 
822   gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->content), path, column, TRUE);
823   gtk_tree_path_free (path);
824 }
825 
826 static void
remove_row_reference(GtkTreeRowReference * reference,XfburnDataCompositionPrivate * priv)827 remove_row_reference (GtkTreeRowReference *reference, XfburnDataCompositionPrivate *priv)
828 {
829   GtkTreePath *path = NULL;
830   GtkTreeModel *model;
831 
832   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->content));
833 
834   path = gtk_tree_row_reference_get_path (reference);
835   if (path) {
836     GtkTreeIter iter;
837 
838     if (gtk_tree_model_get_iter (model, &iter, path)) {
839       GtkTreeIter parent, iter_temp;
840       guint64 size = 0;
841 
842       gtk_tree_model_get (model, &iter, DATA_COMPOSITION_COLUMN_SIZE, &size, -1);
843       xfburn_disc_usage_sub_size (XFBURN_DISC_USAGE (priv->disc_usage), size);
844 
845       iter_temp = iter;
846       while (gtk_tree_model_iter_parent (model, &parent, &iter_temp)) {
847         guint64 old_size;
848         gchar *humansize = NULL;
849 
850         /* updates parent directories size */
851         gtk_tree_model_get (model, &parent, DATA_COMPOSITION_COLUMN_SIZE, &old_size, -1);
852 
853         humansize = xfburn_humanreadable_filesize (old_size - size);
854         gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
855                             DATA_COMPOSITION_COLUMN_HUMANSIZE, humansize,
856                             DATA_COMPOSITION_COLUMN_SIZE, old_size - size, -1);
857 
858         iter_temp = parent;
859 
860         g_free (humansize);
861       }
862 
863       gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
864     }
865 
866     gtk_tree_path_free (path);
867   }
868 
869   gtk_tree_row_reference_free (reference);
870 }
871 
872 static void
action_remove_selection(GSimpleAction * action,GVariant * param,XfburnDataComposition * dc)873 action_remove_selection (GSimpleAction *action, GVariant *param, XfburnDataComposition *dc)
874 {
875   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
876 
877   GtkTreeSelection *selection;
878   GtkTreeModel *model;
879   GList *list_paths = NULL, *el;
880   GList *references = NULL;
881 
882   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->content));
883   list_paths = gtk_tree_selection_get_selected_rows (selection, &model);
884 
885   el = list_paths;
886   while (el) {
887     GtkTreePath *path = NULL;
888     GtkTreeRowReference *reference = NULL;
889 
890     path = (GtkTreePath *) el->data;
891     reference = gtk_tree_row_reference_new (model, path);
892     gtk_tree_path_free (path);
893 
894     if (reference)
895       references = g_list_prepend (references, reference);
896 
897     el = g_list_next (el);
898   }
899   g_list_free (list_paths);
900 
901   g_list_foreach (references, (GFunc) remove_row_reference, priv);
902   g_list_free (references);
903 }
904 
905 static void
add_cb(GtkWidget * widget,gpointer data)906 add_cb (GtkWidget * widget, gpointer data)
907 {
908     XfburnDataComposition * dc = XFBURN_DATA_COMPOSITION(data);
909     XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
910     gchar *selected_files = NULL;
911 
912     GSList *list = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (priv->add_filechooser));
913     GString  * str = g_string_new(NULL);
914     GSList * curr;
915 
916     priv->last_directory = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER(priv->add_filechooser));
917 
918     for (curr = list; curr!=NULL; curr = curr->next) {
919         g_string_append(str, curr->data);
920         g_string_append_c(str, '\n');;
921     }
922 
923     g_slist_free_full (list, g_free);
924     selected_files = str->str;
925     g_string_free (str, FALSE);
926     DBG("selected  files: %s ", selected_files);
927 
928     gtk_widget_destroy (priv->add_window);
929 
930     add_files (selected_files, dc);
931 }
932 
933 static void
select_files(XfburnDataComposition * dc)934 select_files (XfburnDataComposition * dc)
935 {
936     XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
937 
938     GtkWidget * window;
939     GtkWidget * add_button;
940     GtkWidget * vbox;
941     GtkWidget * bbox;
942 
943     priv->add_window = window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
944     gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG);
945     gtk_window_set_title (GTK_WINDOW(window), _("File(s) to add to composition"));
946     gtk_window_set_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER); // GTK_WINDOW(xfburn_main_window_get_instance()),
947     gtk_container_set_border_width (GTK_CONTAINER(window), 10);
948     gtk_window_set_default_size(GTK_WINDOW(window), 600, 400);
949 
950     priv->add_filechooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
951 
952     gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(priv->add_filechooser), TRUE);
953 
954     if(xfburn_main_has_initial_dir ()) {
955       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(priv->add_filechooser), xfburn_main_get_initial_dir ());
956     }
957 
958     if (priv->last_directory)
959       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(priv->add_filechooser), priv->last_directory);
960 
961     vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
962     gtk_container_add(GTK_CONTAINER(window), vbox);
963 
964     gtk_box_pack_start(GTK_BOX(vbox), priv->add_filechooser, TRUE, TRUE, 3);
965 
966     add_button = gtk_button_new_with_label (_("Add"));
967     g_signal_connect (add_button, "clicked", G_CALLBACK(add_cb), dc);
968     g_signal_connect (priv->add_filechooser, "file-activated", G_CALLBACK(add_cb), dc);
969 
970     bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
971     gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
972     gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 3);
973 
974     gtk_box_pack_end(GTK_BOX(bbox), add_button, FALSE, FALSE, 3);
975 
976     g_signal_connect(window, "destroy",
977                      G_CALLBACK(gtk_widget_destroyed), &window);
978 
979     gtk_widget_show_all (window);
980 }
981 
982 static void
action_add_or_select(GSimpleAction * action,GVariant * param,XfburnDataComposition * dc)983 action_add_or_select (GSimpleAction *action, GVariant *param, XfburnDataComposition *dc)
984 {
985   if (xfburn_settings_get_boolean("show-filebrowser", FALSE)) {
986     XfburnFileBrowser *browser = xfburn_main_window_get_file_browser (xfburn_main_window_get_instance ());
987 
988     add_files (xfburn_file_browser_get_selection (browser), dc);
989   } else {
990     select_files(dc);
991   }
992 }
993 
994 static void
add_files(gchar * selected_files,XfburnDataComposition * dc)995 add_files(gchar * selected_files, XfburnDataComposition *dc)
996 {
997   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
998 
999   xfburn_busy_cursor (priv->content);
1000   if (selected_files) {
1001     GtkTreeSelection *selection;
1002     GList *selected_paths = NULL;
1003     ThreadAddFilesActionParams *params;
1004 
1005     xfburn_adding_progress_show (XFBURN_ADDING_PROGRESS (priv->progress));
1006 
1007     params = g_new (ThreadAddFilesActionParams, 1);
1008     params->dc = dc;
1009     params->type = -1;
1010     priv->path_where_insert = NULL;
1011 
1012     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->content));
1013     selected_paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1014     params->model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->content));
1015 
1016     if (selected_paths) {
1017       priv->path_where_insert = gtk_tree_path_copy ((GtkTreePath *) (selected_paths->data));
1018 
1019       gtk_tree_model_get_iter (params->model, &params->iter_where_insert, priv->path_where_insert);
1020       gtk_tree_model_get (params->model, &params->iter_where_insert, DATA_COMPOSITION_COLUMN_TYPE, &params->type, -1);
1021     }
1022 
1023     priv->selected_files = selected_files;
1024 
1025     priv->thread_params = params;
1026     g_thread_new ("data_add_files", (GThreadFunc) thread_add_files_action, params);
1027 
1028     g_list_free_full (selected_paths, (GDestroyNotify) gtk_tree_path_free);
1029   }
1030 }
1031 
1032 static void
set_default_name(XfburnDataComposition * dc)1033 set_default_name (XfburnDataComposition * dc)
1034 {
1035   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
1036 
1037   char timestr[80];
1038   struct tm *today;
1039   time_t tm;
1040   /* FIXME: put i into the class? */
1041   static int i = 0;
1042 
1043   tm = time (NULL);
1044   today = localtime (&tm);
1045 
1046   if (priv->default_vol_name)
1047     g_free (priv->default_vol_name);
1048 
1049   if (tm && strftime (timestr, 80, "%Y-%m-%d", today))
1050     /* Note to translators: first %s is the date in "i18n" format (year-month-day), %d is a running number of compositions */
1051     priv->default_vol_name = g_strdup_printf (_("Data %s~%d"), timestr, ++i);
1052   else
1053     priv->default_vol_name = g_strdup_printf ("%s %d", _(DATA_COMPOSITION_DEFAULT_NAME), ++i);
1054 
1055   gtk_entry_set_text (GTK_ENTRY (priv->entry_volume_name), priv->default_vol_name);
1056 }
1057 
1058 static gboolean
has_default_name(XfburnDataComposition * dc)1059 has_default_name (XfburnDataComposition * dc)
1060 {
1061   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
1062 
1063   const gchar *name;
1064 
1065   name = gtk_entry_get_text (GTK_ENTRY (priv->entry_volume_name));
1066 
1067   return strcmp (name, priv->default_vol_name) == 0;
1068 }
1069 
1070 static void
action_clear(GSimpleAction * action,GVariant * param,XfburnDataComposition * dc)1071 action_clear (GSimpleAction *action, GVariant *param, XfburnDataComposition *dc)
1072 {
1073   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
1074 
1075   GtkTreeModel *model;
1076 
1077   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->content));
1078   gtk_tree_store_clear (GTK_TREE_STORE (model));
1079 
1080   set_default_name (dc);
1081 
1082   xfburn_disc_usage_set_size (XFBURN_DISC_USAGE (priv->disc_usage), 0);
1083 }
1084 
1085 static void
cb_content_drag_data_get(GtkWidget * widget,GdkDragContext * dc,GtkSelectionData * data,guint info,guint timestamp,XfburnDataComposition * content)1086 cb_content_drag_data_get (GtkWidget * widget, GdkDragContext * dc,
1087                           GtkSelectionData * data, guint info, guint timestamp, XfburnDataComposition * content)
1088 {
1089   if (info == DATA_COMPOSITION_DND_TARGET_INSIDE) {
1090     GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1091     GtkTreeModel *model;
1092     GList *selected_rows = NULL, *row = NULL;
1093     GList *references = NULL;
1094 
1095     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1096 
1097     row = selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
1098 
1099     while (row) {
1100       GtkTreeRowReference *reference = NULL;
1101       GtkTreePath *temp;
1102 
1103       temp = (GtkTreePath *) row->data;
1104       reference = gtk_tree_row_reference_new (model, temp);
1105       gtk_tree_path_free (temp);
1106 
1107       references = g_list_prepend (references, reference);
1108 
1109       row = g_list_next (row);
1110     }
1111 
1112     g_list_free (selected_rows);
1113 
1114     gtk_selection_data_set (data, gdk_atom_intern ("XFBURN_TREE_PATHS", FALSE), 8, (const guchar *) &references,
1115                             sizeof (GList **));
1116   }
1117 }
1118 
1119 static void
set_modified(XfburnDataCompositionPrivate * priv)1120 set_modified (XfburnDataCompositionPrivate *priv)
1121 {
1122   if (!(priv->modified)) {
1123     /*
1124     XfburnMainWindow *mainwin;
1125     GtkUIManager *ui_manager;
1126     GtkActionGroup *action_group;
1127 
1128     mainwin = xfburn_main_window_get_instance ();
1129     ui_manager = xfburn_main_window_get_ui_manager (mainwin);
1130 
1131     action_group = (GtkActionGroup *) gtk_ui_manager_get_action_groups (ui_manager)->data;
1132 
1133     action = gtk_action_group_get_action (action_group, "save-composition");
1134     gtk_action_set_sensitive (GTK_ACTION (action), TRUE);
1135     */
1136     priv->modified = TRUE;
1137   }
1138 }
1139 
1140 static gboolean
thread_add_file_to_list_with_name(const gchar * name,XfburnDataComposition * dc,GtkTreeModel * model,const gchar * path,GtkTreeIter * iter,GtkTreeIter * insertion,GtkTreeViewDropPosition position)1141 thread_add_file_to_list_with_name (const gchar *name, XfburnDataComposition * dc,
1142                                    GtkTreeModel * model, const gchar * path,
1143                                    GtkTreeIter * iter, GtkTreeIter * insertion, GtkTreeViewDropPosition position)
1144 {
1145   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
1146 
1147   struct stat s;
1148 
1149   if ((g_lstat (path, &s) == 0)) {
1150     gchar *humansize = NULL;
1151     GtkTreeIter *parent = NULL;
1152     GtkTreePath *tree_path = NULL;
1153     int parent_type = 0;
1154 
1155     if (!(S_ISDIR (s.st_mode) ||S_ISREG (s.st_mode) || S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode) || S_ISLNK (s.st_mode))) {
1156       return FALSE;
1157     }
1158 
1159     xfburn_adding_progress_pulse (XFBURN_ADDING_PROGRESS (priv->progress));
1160 
1161     /* ensure that we can only drop on top of folders, not files */
1162     if (insertion) {
1163       gdk_threads_enter ();
1164       gtk_tree_model_get (model, insertion, DATA_COMPOSITION_COLUMN_TYPE, &parent_type, -1);
1165       gdk_threads_leave ();
1166 
1167       if (parent_type == DATA_COMPOSITION_TYPE_FILE) {
1168         DBG ("Parent is file, and we're dropping into %d", position);
1169         if (position == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
1170           position = GTK_TREE_VIEW_DROP_AFTER;
1171         else if (position == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
1172           position = GTK_TREE_VIEW_DROP_BEFORE;
1173       }
1174     }
1175 
1176     /* find parent */
1177     switch (position){
1178       case GTK_TREE_VIEW_DROP_BEFORE:
1179       case GTK_TREE_VIEW_DROP_AFTER:
1180       if (insertion) {
1181           GtkTreeIter iter_parent;
1182 
1183           gdk_threads_enter ();
1184           if (gtk_tree_model_iter_parent (model, &iter_parent, insertion)) {
1185             parent = g_new0 (GtkTreeIter, 1);
1186             memcpy (parent, &iter_parent, sizeof (GtkTreeIter));
1187           }
1188           gdk_threads_leave ();
1189         }
1190         break;
1191       case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
1192       case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
1193         parent = g_new0 (GtkTreeIter, 1);
1194         memcpy (parent, insertion, sizeof (GtkTreeIter));
1195         break;
1196     }
1197 
1198     /* check if the filename is valid */
1199     gdk_threads_enter ();
1200     if (parent) {
1201       tree_path = gtk_tree_model_get_path (model, parent);
1202       gtk_tree_path_down (tree_path);
1203     } else {
1204       tree_path = gtk_tree_path_new_first ();
1205     }
1206 
1207     if (file_exists_on_same_level (model, tree_path, FALSE, name)) {
1208       xfce_dialog_show_error (NULL, NULL, _("A file with the same name is already present in the composition."));
1209 
1210       gtk_tree_path_free (tree_path);
1211       gdk_threads_leave ();
1212       g_free (parent);
1213       return FALSE;
1214     }
1215     gtk_tree_path_free (tree_path);
1216     gdk_threads_leave ();
1217 
1218     /* new directory */
1219     if (S_ISDIR (s.st_mode) && !S_ISLNK (s.st_mode)) {
1220       GDir *dir = NULL;
1221       GError *error = NULL;
1222       const gchar *filename = NULL;
1223       guint64 total_size = 4;
1224 
1225       dir = g_dir_open (path, 0, &error);
1226       if (!dir) {
1227         g_warning ("unable to open directory : %s", error->message);
1228 
1229         g_error_free (error);
1230         g_free (parent);
1231 
1232         return FALSE;
1233       }
1234 
1235       gdk_threads_enter ();
1236       gtk_tree_store_append (GTK_TREE_STORE (model), iter, parent);
1237 
1238       gtk_tree_store_set (GTK_TREE_STORE (model), iter,
1239                           DATA_COMPOSITION_COLUMN_ICON, icon_directory,
1240                           DATA_COMPOSITION_COLUMN_CONTENT, name,
1241                           DATA_COMPOSITION_COLUMN_TYPE, DATA_COMPOSITION_TYPE_DIRECTORY,
1242                           DATA_COMPOSITION_COLUMN_PATH, path,
1243                           DATA_COMPOSITION_COLUMN_SIZE, (guint64) 4, -1);
1244       xfburn_disc_usage_add_size (XFBURN_DISC_USAGE (priv->disc_usage), (guint64) 4);
1245       gdk_threads_leave ();
1246 
1247       while ((filename = g_dir_read_name (dir))) {
1248         GtkTreeIter new_iter;
1249         gchar *new_path = NULL;
1250 
1251         new_path = g_build_filename (path, filename, NULL);
1252         if (new_path) {
1253           guint64 size;
1254 
1255           if (thread_add_file_to_list (dc, model, new_path, &new_iter, iter, GTK_TREE_VIEW_DROP_INTO_OR_AFTER)) {
1256             gdk_threads_enter ();
1257             gtk_tree_model_get (model, &new_iter, DATA_COMPOSITION_COLUMN_SIZE, &size, -1);
1258             gdk_threads_leave ();
1259             total_size += size;
1260           }
1261 
1262           g_free (new_path);
1263         }
1264       }
1265 
1266       humansize = xfburn_humanreadable_filesize (total_size);
1267       gdk_threads_enter ();
1268       gtk_tree_store_set (GTK_TREE_STORE (model), iter,
1269                           DATA_COMPOSITION_COLUMN_HUMANSIZE, humansize, DATA_COMPOSITION_COLUMN_SIZE, total_size, -1);
1270       gdk_threads_leave ();
1271 
1272       g_dir_close (dir);
1273     }
1274     /* new file */
1275     else if (S_ISREG (s.st_mode) || S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode) || S_ISLNK (s.st_mode)) {
1276       GdkScreen *screen;
1277       GtkIconTheme *icon_theme;
1278       GdkPixbuf *mime_icon_pixbuf = NULL;
1279       gint x,y;
1280       GFile *file = NULL;
1281       GFileInfo *info = NULL;
1282       GIcon *mime_icon = NULL;
1283       GtkIconInfo *icon_info = NULL;
1284 
1285       if (s.st_size > MAXIMUM_ISO_FILE_SIZE) {
1286         gdk_threads_enter ();
1287         xfce_dialog_show_error (NULL, NULL, _("%s cannot be added to the composition, because it exceeds the maximum allowed file size for iso9660."), path);
1288         gdk_threads_leave ();
1289 
1290         return FALSE;
1291       } else if (s.st_size > MAXIMUM_ISO_LEVEL_2_FILE_SIZE && !priv->large_files) {
1292         priv->large_files = TRUE;
1293         gdk_threads_enter ();
1294         xfce_dialog_show_warning (NULL, NULL, _("%s is larger than what iso9660 level 2 allows. This can be a problem for old systems or software."), path);
1295         gdk_threads_leave ();
1296       }
1297 
1298       gdk_threads_enter ();
1299       screen = gtk_widget_get_screen (GTK_WIDGET (dc));
1300       icon_theme = gtk_icon_theme_get_for_screen (screen);
1301       gtk_icon_size_lookup (GTK_ICON_SIZE_SMALL_TOOLBAR, &x, &y);
1302 
1303       file = g_file_new_for_path(path);
1304       info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
1305       mime_icon = g_content_type_get_icon (g_file_info_get_content_type (info));
1306       if (mime_icon != NULL) {
1307         icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme, mime_icon, x, GTK_ICON_LOOKUP_USE_BUILTIN);
1308         if (icon_info != NULL) {
1309           mime_icon_pixbuf = gtk_icon_info_load_icon (icon_info, NULL);
1310           g_object_unref (G_OBJECT (icon_info));
1311         }
1312       }
1313 
1314       gtk_tree_store_append (GTK_TREE_STORE (model), iter, parent);
1315 
1316       humansize = xfburn_humanreadable_filesize (s.st_size);
1317 
1318       gtk_tree_store_set (GTK_TREE_STORE (model), iter,
1319                           DATA_COMPOSITION_COLUMN_ICON, (G_IS_OBJECT (mime_icon_pixbuf) ? mime_icon_pixbuf : icon_file),
1320                           DATA_COMPOSITION_COLUMN_CONTENT, name,
1321                           DATA_COMPOSITION_COLUMN_HUMANSIZE, humansize,
1322                           DATA_COMPOSITION_COLUMN_SIZE, (guint64) s.st_size,
1323                           DATA_COMPOSITION_COLUMN_PATH, path,
1324                           DATA_COMPOSITION_COLUMN_TYPE, DATA_COMPOSITION_TYPE_FILE, -1);
1325 
1326       xfburn_disc_usage_add_size (XFBURN_DISC_USAGE (priv->disc_usage), s.st_size);
1327 
1328       if (G_LIKELY (G_IS_OBJECT (mime_icon)))
1329         g_object_unref (mime_icon);
1330       if (G_LIKELY (G_IS_OBJECT (file)))
1331         g_object_unref(file);
1332       gdk_threads_leave ();
1333     }
1334     g_free (humansize);
1335     g_free (parent);
1336 
1337     set_modified (priv);
1338     return TRUE;
1339   }
1340 
1341   return FALSE;
1342 }
1343 
1344 /* thread entry point */
1345 static gpointer
thread_add_files_cli(ThreadAddFilesCLIParams * params)1346 thread_add_files_cli (ThreadAddFilesCLIParams *params)
1347 {
1348   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (params->dc);
1349   GtkTreeIter iter;
1350 
1351   GtkTreeModel *model;
1352   GSList * list_iter;
1353 
1354   gdk_threads_enter ();
1355   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->content));
1356   gdk_threads_leave ();
1357 
1358   for (list_iter = params->filelist; list_iter != NULL; list_iter = g_slist_next (list_iter)) {
1359     gchar * full_path = (gchar *) list_iter->data;
1360 
1361     g_message ("Adding %s to the data composition... (might take a while)", full_path);
1362     thread_add_file_to_list (params->dc, model, full_path, &iter, NULL, GTK_TREE_VIEW_DROP_AFTER);
1363 
1364     g_free (full_path);
1365   }
1366   g_slist_free (params->filelist);
1367   xfburn_adding_progress_done (XFBURN_ADDING_PROGRESS (priv->progress));
1368   return NULL;
1369 }
1370 
1371 static gboolean
show_add_home_question_dialog(void)1372 show_add_home_question_dialog (void)
1373 {
1374   gboolean ok = TRUE;
1375 
1376   gdk_threads_enter ();
1377   DBG ("Adding home directory");
1378   ok = xfburn_ask_yes_no (GTK_MESSAGE_WARNING, ((const gchar *) _("Adding home directory")),
1379                           _("You are about to add your home directory to the composition. " \
1380                             "This is likely to take a very long time, and also to be too big to fit on one disc.\n\n" \
1381                             "Are you sure you want to proceed?")
1382                          );
1383 
1384   gdk_threads_leave ();
1385 
1386   return ok;
1387 }
1388 
1389 /* thread entry point */
1390 static gpointer
thread_add_files_action(ThreadAddFilesActionParams * params)1391 thread_add_files_action (ThreadAddFilesActionParams *params)
1392 {
1393   XfburnDataComposition *dc = params->dc;
1394   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
1395   GtkTreeModel *model = params->model;
1396   GtkTreeIter iter_where_insert = params->iter_where_insert;
1397   GtkTreePath *path_where_insert = priv->path_where_insert;
1398   gchar ** files = NULL;
1399   int i;
1400 
1401 
1402   files = g_strsplit (priv->selected_files, "\n", -1);
1403 
1404   if (files)
1405     for (i=0; files[i] != NULL && files[i][0] != '\0'; i++) {
1406       GtkTreeIter iter;
1407       gchar *full_path = NULL;
1408 
1409       if (g_str_has_prefix (files[i], "file://"))
1410         full_path = g_build_filename (&files[i][7], NULL);
1411       else if (g_str_has_prefix (files[i], "file:"))
1412         full_path = g_build_filename (&files[i][5], NULL);
1413       else
1414         full_path = g_build_filename (files[i], NULL);
1415 
1416       if (full_path[strlen (full_path) - 1] == '\r')
1417         full_path[strlen (full_path) - 1] = '\0';
1418 
1419       if (strcmp (full_path, g_getenv ("HOME")) == 0) {
1420         if (!show_add_home_question_dialog ()) {
1421           g_free (full_path);
1422           break;
1423         }
1424       }
1425 
1426       /* add files to the disc content */
1427       if (params->type == DATA_COMPOSITION_TYPE_DIRECTORY) {
1428         guint64 old_size, size;
1429         gchar *humansize = NULL;
1430 
1431         thread_add_file_to_list (dc, model, full_path, &iter, &iter_where_insert, GTK_TREE_VIEW_DROP_INTO_OR_AFTER);
1432         gdk_threads_enter ();
1433         gtk_tree_view_expand_row (GTK_TREE_VIEW (priv->content), path_where_insert, FALSE);
1434 
1435         /* update parent directory size */
1436         gtk_tree_model_get (model, &iter_where_insert, DATA_COMPOSITION_COLUMN_SIZE, &old_size, -1);
1437         gtk_tree_model_get (model, &iter, DATA_COMPOSITION_COLUMN_SIZE, &size, -1);
1438         gdk_threads_leave ();
1439 
1440         humansize = xfburn_humanreadable_filesize (old_size + size);
1441 
1442         gdk_threads_enter ();
1443         gtk_tree_store_set (GTK_TREE_STORE (model), &iter_where_insert,
1444                             DATA_COMPOSITION_COLUMN_HUMANSIZE, humansize,
1445                             DATA_COMPOSITION_COLUMN_SIZE, old_size + size, -1);
1446         gdk_threads_leave ();
1447 
1448         g_free (humansize);
1449       } else if (params->type == DATA_COMPOSITION_TYPE_FILE) {
1450         GtkTreeIter parent;
1451         gboolean has_parent;
1452 
1453         gdk_threads_enter ();
1454         has_parent = gtk_tree_model_iter_parent (model, &parent, &iter_where_insert);
1455         gdk_threads_leave ();
1456 
1457         if (has_parent)
1458           thread_add_file_to_list (dc, model, full_path, &iter, &parent, GTK_TREE_VIEW_DROP_INTO_OR_AFTER);
1459         else
1460           thread_add_file_to_list (dc, model, full_path, &iter, NULL, GTK_TREE_VIEW_DROP_AFTER);
1461       } else {
1462         thread_add_file_to_list (dc, model, full_path, &iter, NULL, GTK_TREE_VIEW_DROP_AFTER);
1463       }
1464 
1465       g_free (full_path);
1466     }
1467   /* end if files */
1468 
1469   g_strfreev (files);
1470 
1471   xfburn_adding_progress_done (XFBURN_ADDING_PROGRESS (priv->progress));
1472   return NULL;
1473 }
1474 
1475 static gboolean
thread_add_file_to_list(XfburnDataComposition * dc,GtkTreeModel * model,const gchar * path,GtkTreeIter * iter,GtkTreeIter * insertion,GtkTreeViewDropPosition position)1476 thread_add_file_to_list (XfburnDataComposition * dc, GtkTreeModel * model,
1477                          const gchar * path, GtkTreeIter * iter, GtkTreeIter * insertion, GtkTreeViewDropPosition position)
1478 {
1479   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
1480   struct stat s;
1481   gboolean ret = FALSE;
1482 
1483   if (xfburn_adding_progress_is_aborted (XFBURN_ADDING_PROGRESS (priv->progress))) {
1484     DBG ("Adding aborted");
1485     xfburn_adding_progress_done (XFBURN_ADDING_PROGRESS (priv->progress));
1486     /* FIXME: does this properly release the resources allocated in this thread? */
1487     g_thread_exit (NULL);
1488   }
1489 
1490   if ((stat (path, &s) == 0)) {
1491     gchar *basename = NULL;
1492 
1493     basename = g_path_get_basename (path);
1494 
1495     ret = thread_add_file_to_list_with_name (basename, dc, model, path, iter, insertion, position);
1496 
1497     g_free (basename);
1498   }
1499 
1500   return ret;
1501 }
1502 
1503 static gboolean
copy_entry_to(XfburnDataComposition * dc,GtkTreeIter * src,GtkTreeIter * dest,GtkTreeViewDropPosition position)1504 copy_entry_to (XfburnDataComposition *dc, GtkTreeIter *src, GtkTreeIter *dest, GtkTreeViewDropPosition position)
1505 {
1506   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
1507 
1508   gboolean ret = FALSE;
1509 
1510   GtkTreeModel *model;
1511   GtkTreeIter iter_new;
1512 
1513   GdkPixbuf *icon = NULL;
1514   gchar *name = NULL;
1515   gchar *humansize = NULL;
1516   guint64 size = 0;
1517   gchar *path = NULL;
1518   DataCompositionEntryType type;
1519 
1520   GtkTreePath *path_level = NULL;
1521 
1522   guint n_children = 0;
1523   guint i;
1524   GtkTreePath *path_src = NULL;
1525 
1526   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->content));
1527 
1528   gtk_tree_model_get (model, src, DATA_COMPOSITION_COLUMN_ICON, &icon, DATA_COMPOSITION_COLUMN_CONTENT, &name,
1529                       DATA_COMPOSITION_COLUMN_HUMANSIZE, &humansize, DATA_COMPOSITION_COLUMN_SIZE, &size,
1530                       DATA_COMPOSITION_COLUMN_PATH, &path, DATA_COMPOSITION_COLUMN_TYPE, &type, -1);
1531 
1532   switch (position) {
1533     case GTK_TREE_VIEW_DROP_BEFORE:
1534     case GTK_TREE_VIEW_DROP_AFTER:
1535       gtk_tree_store_insert_before (GTK_TREE_STORE (model), &iter_new, NULL, dest);
1536       break;
1537     case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
1538     case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
1539       if (dest) {
1540         path_level = gtk_tree_model_get_path (model, dest);
1541         gtk_tree_path_down (path_level);
1542       } else {
1543         path_level = gtk_tree_path_new_first ();
1544       }
1545 
1546       if (file_exists_on_same_level (model, path_level, FALSE, name)) {
1547 	xfce_dialog_show_warning(NULL, NULL, _("A file named \"%s\" already exists in this directory, the file hasn't been added."), name);
1548         goto cleanup;
1549       }
1550 
1551       gtk_tree_path_free (path_level);
1552 
1553       gtk_tree_store_append (GTK_TREE_STORE (model), &iter_new, dest);
1554       break;
1555   }
1556 
1557   gtk_tree_store_set (GTK_TREE_STORE (model), &iter_new, DATA_COMPOSITION_COLUMN_ICON, icon,
1558                       DATA_COMPOSITION_COLUMN_CONTENT, name, DATA_COMPOSITION_COLUMN_HUMANSIZE, humansize,
1559                       DATA_COMPOSITION_COLUMN_SIZE, size, DATA_COMPOSITION_COLUMN_PATH, path,
1560                       DATA_COMPOSITION_COLUMN_TYPE, type, -1);
1561 
1562   /* copy children */
1563   n_children = gtk_tree_model_iter_n_children (model, src);
1564 
1565   for (i = 0; i < n_children; i++) {
1566     GtkTreeIter iter_child;
1567 
1568     if (gtk_tree_model_iter_nth_child (model, &iter_child, src, i))
1569       copy_entry_to (dc, &iter_child, &iter_new, GTK_TREE_VIEW_DROP_INTO_OR_AFTER);
1570   }
1571 
1572   path_src = gtk_tree_model_get_path (model, src);
1573   if (n_children > 0 && gtk_tree_view_row_expanded (GTK_TREE_VIEW (priv->content), path_src)) {
1574     GtkTreePath *path_new = NULL;
1575 
1576     path_new = gtk_tree_model_get_path (model, &iter_new);
1577     gtk_tree_view_expand_row (GTK_TREE_VIEW (priv->content), path_new, FALSE);
1578 
1579     gtk_tree_path_free (path_new);
1580   }
1581   gtk_tree_path_free (path_src);
1582 
1583   ret =  TRUE;
1584 
1585 cleanup:
1586   if (G_LIKELY (G_IS_OBJECT (icon)))
1587     g_object_unref (icon);
1588   g_free (name);
1589   g_free (humansize);
1590   g_free (path);
1591 
1592   return ret;
1593 }
1594 
1595 static void
cb_content_drag_data_rcv(GtkWidget * widget,GdkDragContext * dc,guint x,guint y,GtkSelectionData * sd,guint info,guint t,XfburnDataComposition * composition)1596 cb_content_drag_data_rcv (GtkWidget * widget, GdkDragContext * dc, guint x, guint y, GtkSelectionData * sd,
1597                           guint info, guint t, XfburnDataComposition * composition)
1598 {
1599   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (composition);
1600 
1601   GtkTreeModel *model;
1602   GtkTreePath *path_where_insert = NULL;
1603   GtkTreeViewDropPosition position;
1604   GtkTreeIter iter_where_insert;
1605 
1606   g_return_if_fail (sd);
1607   g_return_if_fail (gtk_selection_data_get_data (sd));
1608 
1609   model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1610 
1611   gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y, &path_where_insert, &position);
1612 
1613   xfburn_busy_cursor (priv->content);
1614 
1615   /* move a selection inside of the composition window */
1616   if (gtk_selection_data_get_target(sd) == gdk_atom_intern ("XFBURN_TREE_PATHS", FALSE)) {
1617     GList *row = NULL, *selected_rows = NULL;
1618     GtkTreeIter *iter = NULL;
1619     DataCompositionEntryType type_dest = -1;
1620 
1621     xfburn_adding_progress_show (XFBURN_ADDING_PROGRESS (priv->progress));
1622 
1623     row = selected_rows = *((GList **) gtk_selection_data_get_data(sd));
1624 
1625     if (path_where_insert) {
1626       gtk_tree_model_get_iter (model, &iter_where_insert, path_where_insert);
1627       iter = &iter_where_insert;
1628 
1629       gtk_tree_model_get (model, &iter_where_insert, DATA_COMPOSITION_COLUMN_TYPE, &type_dest, -1);
1630 
1631       if (type_dest == DATA_COMPOSITION_TYPE_FILE) {
1632         if (position == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
1633           position = GTK_TREE_VIEW_DROP_AFTER;
1634         else if (position == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
1635           position = GTK_TREE_VIEW_DROP_BEFORE;
1636       }
1637     } else {
1638       position = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
1639     }
1640 
1641     /* copy selection */
1642     while (row) {
1643       GtkTreePath *path_src = NULL;
1644       GtkTreeIter iter_src;
1645       GtkTreeRowReference *reference = NULL;
1646       DataCompositionEntryType type;
1647       guint64 size = 0;
1648 
1649       reference = (GtkTreeRowReference *) row->data;
1650 
1651       path_src = gtk_tree_row_reference_get_path (reference);
1652       if (!path_src) {
1653         gtk_tree_row_reference_free (reference);
1654 
1655         row = g_list_next (row);
1656         continue;
1657       }
1658 
1659       if (path_where_insert && (position == GTK_TREE_VIEW_DROP_AFTER || position == GTK_TREE_VIEW_DROP_BEFORE)
1660           && (gtk_tree_path_get_depth (path_where_insert) == gtk_tree_path_get_depth (path_src))) {
1661           gtk_tree_path_free (path_src);
1662           gtk_tree_row_reference_free (reference);
1663 
1664           row = g_list_next (row);
1665           continue;
1666       }
1667 
1668       if (path_where_insert && type == DATA_COMPOSITION_TYPE_DIRECTORY
1669           && gtk_tree_path_is_descendant (path_where_insert, path_src)) {
1670 
1671         gtk_tree_path_free (path_src);
1672         gtk_tree_path_free (path_where_insert);
1673         gtk_tree_row_reference_free (reference);
1674 
1675         gtk_drag_finish (dc, FALSE, FALSE, t);
1676         return;
1677       }
1678 
1679       gtk_tree_model_get_iter (model, &iter_src, path_src);
1680       gtk_tree_model_get (model, &iter_src, DATA_COMPOSITION_COLUMN_TYPE, &type,
1681                           DATA_COMPOSITION_COLUMN_SIZE, &size, -1);
1682 
1683       /* copy entry */
1684       if (copy_entry_to (composition, &iter_src, iter, position)) {
1685         GtkTreePath *path_parent = gtk_tree_path_copy (path_src);
1686 
1687         /* update new parent size */
1688         if (iter && (position == GTK_TREE_VIEW_DROP_INTO_OR_AFTER
1689                    || position == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1690           guint64 old_size = 0;
1691           gchar *parent_humansize = NULL;
1692 
1693           gtk_tree_model_get (model, iter, DATA_COMPOSITION_COLUMN_SIZE, &old_size, -1);
1694 
1695           parent_humansize = xfburn_humanreadable_filesize (old_size + size);
1696           gtk_tree_store_set (GTK_TREE_STORE (model), iter, DATA_COMPOSITION_COLUMN_HUMANSIZE, parent_humansize,
1697                               DATA_COMPOSITION_COLUMN_SIZE, old_size + size, -1);
1698 
1699           g_free (parent_humansize);
1700         }
1701 
1702         if (gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE) {
1703           /* remove source entry */
1704           if (gtk_tree_path_up (path_parent) && path_where_insert &&
1705               !gtk_tree_path_is_descendant (path_where_insert, path_parent)) {
1706             /* update parent size and humansize */
1707             GtkTreeIter iter_parent;
1708             guint64 old_size;
1709             gchar *parent_humansize = NULL;
1710 
1711             gtk_tree_model_iter_parent (model, &iter_parent, &iter_src);
1712             gtk_tree_model_get (model, &iter_parent, DATA_COMPOSITION_COLUMN_SIZE, &old_size, -1);
1713 
1714             parent_humansize = xfburn_humanreadable_filesize (old_size - size);
1715             gtk_tree_store_set (GTK_TREE_STORE (model), &iter_parent,
1716                                 DATA_COMPOSITION_COLUMN_HUMANSIZE, parent_humansize,
1717                                 DATA_COMPOSITION_COLUMN_SIZE, old_size - size, -1);
1718             g_free (parent_humansize);
1719           }
1720 
1721           gtk_tree_store_remove (GTK_TREE_STORE (model), &iter_src);
1722         } else {
1723           xfburn_disc_usage_add_size (XFBURN_DISC_USAGE (priv->disc_usage), size);
1724         }
1725 
1726         gtk_tree_path_free (path_parent);
1727       }
1728 
1729       gtk_tree_path_free (path_src);
1730       gtk_tree_row_reference_free (reference);
1731 
1732       row = g_list_next (row);
1733     }
1734 
1735     g_list_free (selected_rows);
1736     gtk_drag_finish (dc, TRUE, FALSE, t);
1737 
1738     if (path_where_insert)
1739       gtk_tree_path_free (path_where_insert);
1740     gtk_widget_hide (priv->progress);
1741     xfburn_default_cursor (priv->content);
1742   }
1743   /* drag from the file selector, or nautilus */
1744   else if (gtk_selection_data_get_target(sd) == gdk_atom_intern ("text/plain;charset=utf-8", FALSE)) {
1745     ThreadAddFilesDragParams *params;
1746     gchar **files = NULL;
1747     gchar *full_paths;
1748     int i;
1749 
1750     xfburn_adding_progress_show (XFBURN_ADDING_PROGRESS (priv->progress));
1751 
1752     full_paths = (gchar *) gtk_selection_data_get_text (sd);
1753 
1754     files = g_strsplit ((gchar *) full_paths, "\n", -1);
1755 
1756     if (files) {
1757 
1758       for (i=0; files[i] != NULL && files[i][0] != '\0'; i++) {
1759         gchar *full_path;
1760 
1761         if (g_str_has_prefix (files[i], "file://"))
1762           full_path = g_build_filename (&files[i][7], NULL);
1763         else if (g_str_has_prefix (files[i], "file:"))
1764           full_path = g_build_filename (&files[i][5], NULL);
1765         else
1766           full_path = g_build_filename (files[i], NULL);
1767 
1768         if (full_path[strlen (full_path) - 1] == '\r')
1769           full_path[strlen (full_path) - 1] = '\0';
1770 
1771         DBG ("Adding path '%s'", full_path);
1772 
1773         /* remember path to add it later in another thread */
1774         priv->full_paths_to_add = g_list_append (priv->full_paths_to_add, full_path);
1775       }
1776       g_strfreev (files);
1777       g_free (full_paths);
1778 
1779       priv->full_paths_to_add = g_list_reverse (priv->full_paths_to_add);
1780       /* FIXME: path_where_insert is always NULL here */
1781       priv->path_where_insert = path_where_insert;
1782 
1783       params = g_new (ThreadAddFilesDragParams, 1);
1784       params->composition = composition;
1785       params->position = position;
1786       params->widget = widget;
1787 
1788       /* append a dummy row so that gtk doesn't freak out */
1789       gtk_tree_store_append (GTK_TREE_STORE (model), &params->iter_dummy, NULL);
1790 
1791       priv->thread_params = params;
1792       g_thread_new ("data_add_dnd_plain", (GThreadFunc) thread_add_files_drag, params);
1793     }
1794 
1795     gtk_drag_finish (dc, TRUE, FALSE, t);
1796   }
1797   else if (gtk_selection_data_get_target(sd) == gdk_atom_intern ("text/uri-list", FALSE)) {
1798     GList *vfs_paths = NULL;
1799     GList *vfs_path;
1800     GList *lp;
1801     gchar *full_path;
1802     gchar **uris;
1803     gsize   n;
1804 
1805     uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data(sd));
1806 
1807     for (n = 0; uris != NULL && uris[n] != NULL; ++n)
1808       vfs_paths = g_list_append (vfs_paths, g_file_new_for_uri (uris[n]));
1809 
1810     g_strfreev (uris);
1811 
1812     if (G_LIKELY (vfs_paths != NULL)) {
1813       ThreadAddFilesDragParams *params;
1814       priv->full_paths_to_add = NULL;
1815       for (vfs_path = vfs_paths; vfs_path != NULL; vfs_path = g_list_next (vfs_path)) {
1816 	GFile *path = vfs_path->data;
1817 	if (path == NULL)
1818           continue;
1819 	/* unable to handle non-local files */
1820 	if (G_UNLIKELY (!g_file_has_uri_scheme (path, "file"))) {
1821             g_object_unref (path);
1822 	    continue;
1823         }
1824         full_path = g_file_get_path (path);
1825         /* if there is no local path, use the URI (which always works) */
1826         if (full_path == NULL)
1827             full_path = g_file_get_uri (path);
1828         /* release the location */
1829         g_debug ("adding uri path: %s", full_path);
1830         priv->full_paths_to_add = g_list_prepend (priv->full_paths_to_add, full_path);
1831       }
1832 
1833       for (lp = vfs_paths; lp != NULL; lp = lp->next)
1834         g_object_unref (lp->data);
1835       g_list_free (vfs_paths);
1836 
1837       priv->full_paths_to_add = g_list_reverse (priv->full_paths_to_add);
1838       /* FIXME: path_where_insert is always NULL here */
1839       priv->path_where_insert = path_where_insert;
1840 
1841       params = g_new (ThreadAddFilesDragParams, 1);
1842       params->composition = composition;
1843       params->position = position;
1844       params->widget = widget;
1845 
1846       /* append a dummy row so that gtk doesn't freak out */
1847       gtk_tree_store_append (GTK_TREE_STORE (model), &params->iter_dummy, NULL);
1848 
1849       priv->thread_params = params;
1850       g_thread_new ("data_add_dnd_uri", (GThreadFunc) thread_add_files_drag, params);
1851 
1852       gtk_drag_finish (dc, TRUE, FALSE, t);
1853     } else {
1854       g_warning("There were no files in the uri list!");
1855       gtk_drag_finish (dc, FALSE, FALSE, t);
1856       xfburn_default_cursor (priv->content);
1857     }
1858   }
1859   else {
1860     g_warning ("Trying to receive an unsupported drag target, this should not happen.");
1861     gtk_drag_finish (dc, FALSE, FALSE, t);
1862     xfburn_default_cursor (priv->content);
1863   }
1864 }
1865 
1866 /* thread entry point */
1867 static gpointer
thread_add_files_drag(ThreadAddFilesDragParams * params)1868 thread_add_files_drag (ThreadAddFilesDragParams *params)
1869 {
1870   XfburnDataComposition *composition = params->composition;
1871   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (composition);
1872 
1873   GtkTreeViewDropPosition position = params->position;
1874   GtkWidget *widget = params->widget;
1875 
1876   GtkTreeModel *model;
1877   GtkTreeIter iter_where_insert;
1878   GtkTreeIter *iter_insert = (priv->path_where_insert) ? &iter_where_insert : NULL;
1879   gboolean expand = (position == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || position == GTK_TREE_VIEW_DROP_INTO_OR_AFTER);
1880   gboolean success = FALSE;
1881   GList *files = priv->full_paths_to_add;
1882 
1883   gdk_threads_enter ();
1884   model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1885 
1886   /* remove the dummy row again */
1887   gtk_tree_store_remove (GTK_TREE_STORE (model), &params->iter_dummy);
1888   gdk_threads_leave ();
1889 
1890   for (; files; files = g_list_next (files)) {
1891     gchar *full_path = (gchar *) files->data;
1892     GtkTreeIter iter;
1893 
1894     if (priv->path_where_insert) {
1895       gdk_threads_enter ();
1896       gtk_tree_model_get_iter (model, &iter_where_insert, priv->path_where_insert);
1897       gdk_threads_leave ();
1898     }
1899 
1900     success = thread_add_file_to_list (composition, model, full_path, &iter, iter_insert, position);
1901 
1902     if (success && expand && priv->path_where_insert) {
1903       gdk_threads_enter ();
1904       gtk_tree_view_expand_row (GTK_TREE_VIEW (widget), priv->path_where_insert, FALSE);
1905       gdk_threads_leave ();
1906     }
1907 
1908   }
1909   xfburn_adding_progress_done (XFBURN_ADDING_PROGRESS (priv->progress));
1910   return NULL;
1911 }
1912 
1913 static void
fill_image_with_composition(GtkTreeModel * model,IsoImage * image,IsoDir * parent,GtkTreeIter * iter,GSList ** errors)1914 fill_image_with_composition (GtkTreeModel *model, IsoImage *image, IsoDir * parent, GtkTreeIter *iter, GSList **errors)
1915 {
1916   do {
1917       DataCompositionEntryType type;
1918       gchar *name = NULL;
1919       gchar *src = NULL;
1920       IsoNode *node = NULL;
1921       IsoDir *dir = NULL;
1922       int r;
1923       gchar *basename;
1924 
1925       gtk_tree_model_get (model, iter, DATA_COMPOSITION_COLUMN_TYPE, &type,
1926 			  DATA_COMPOSITION_COLUMN_CONTENT, &name, DATA_COMPOSITION_COLUMN_PATH, &src, -1);
1927 
1928       if (type == DATA_COMPOSITION_TYPE_DIRECTORY && src == NULL) {
1929         /* a directory which the user added for this composition */
1930         r = iso_tree_add_new_dir (parent, name, &dir);
1931         node = (IsoNode *) dir;
1932 
1933         /* if the new directory is part of the root of the iso,
1934          * then its owner will be set to root:root. Not sure what a better
1935          * default could be, so I'll just leave it like that. */
1936       } else {
1937         /* something existing on the filesystem, creating a node
1938          * will copy its attributes */
1939         r = iso_tree_add_node (image, parent, src, &node);
1940       }
1941 
1942       if (r < 0) {
1943         char * msg;
1944 
1945         if (r == ISO_NULL_POINTER)
1946           g_error (_("%s: null pointer"), src);
1947         else if (r == ISO_OUT_OF_MEM)
1948           g_error (_("%s: out of memory"), src);
1949         else if (r == ISO_NODE_NAME_NOT_UNIQUE)
1950           msg = g_strdup_printf (_("%s: node name not unique"), src);
1951         else
1952           msg = g_strdup_printf (_("%s: %s (code %X)"), src, iso_error_to_msg(r), r);
1953 
1954         g_warning ("%s", msg);
1955         *errors = g_slist_prepend (*errors, msg);
1956 
1957         g_free (name);
1958         g_free (src);
1959         continue;
1960       }
1961 
1962       if (src != NULL && *src != '\0') {
1963         basename = g_path_get_basename (src);
1964 
1965         /* check if the file has been renamed */
1966         if (strcmp (basename, name) != 0) {
1967           /* rename the iso_node */
1968           r = iso_node_set_name (node, name);
1969 
1970           if (r == 0) {
1971             /* The first string is the renamed name, the second one the original name */
1972             xfce_dialog_show_warning(NULL, NULL, _("Duplicate filename '%s' for '%s'"), name, src);
1973 
1974             g_free (basename);
1975             g_free (name);
1976             g_free (src);
1977 
1978             continue;
1979           }
1980         }
1981 
1982         g_free (basename);
1983       }
1984 
1985       g_free (name);
1986       g_free (src);
1987 
1988       if (type == DATA_COMPOSITION_TYPE_DIRECTORY && gtk_tree_model_iter_has_child (model, iter)) {
1989 	GtkTreeIter child;
1990 
1991         if (iso_node_get_type(node) != LIBISO_DIR)
1992             g_error ("Expected %s to be a directory, but it isn't...\n", src);
1993 
1994         dir = (IsoDir *)node;
1995 
1996 	gtk_tree_model_iter_children (model, &child, iter);
1997 	fill_image_with_composition (model, image, dir, &child, errors);
1998       }
1999   } while (gtk_tree_model_iter_next (model, iter));
2000 }
2001 
2002 static
concat_free(char * msg,char ** combined_msg)2003 void concat_free (char * msg, char ** combined_msg)
2004 {
2005   *combined_msg = g_strconcat (*combined_msg, msg, "\n", NULL);
2006   free (msg);
2007 }
2008 
2009 static IsoImage *
generate_iso_image(XfburnDataComposition * dc)2010 generate_iso_image (XfburnDataComposition * dc)
2011 {
2012   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
2013   IsoImage *image = NULL;
2014   GtkTreeModel *model;
2015   GtkTreeIter iter;
2016   GSList *errors = NULL;
2017 
2018   iso_image_new (gtk_entry_get_text (GTK_ENTRY (priv->entry_volume_name)), &image);
2019   iso_image_set_application_id (image, "Xfburn");
2020   iso_image_set_data_preparer_id (image, "Xfburn");
2021 
2022   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->content));
2023   if (gtk_tree_model_get_iter_first (model, &iter)) {
2024     fill_image_with_composition (model, image, iso_image_get_root (image), &iter, &errors);
2025 
2026     if (errors != NULL) {
2027       gchar * combined_msg = "";
2028       GtkWidget * dialog, * label, * textview, * scrolled;
2029       GtkTextBuffer * buffer;
2030       XfburnMainWindow *mainwin = xfburn_main_window_get_instance ();
2031       gchar * title;
2032 
2033       errors = g_slist_reverse (errors);
2034 
2035       g_slist_foreach (errors, (GFunc) concat_free, &combined_msg);
2036 
2037       title = _("Error(s) occured while adding files");
2038       dialog = gtk_dialog_new_with_buttons (title,
2039                                             GTK_WINDOW (mainwin),
2040                                             GTK_DIALOG_DESTROY_WITH_PARENT,
2041                                             _("OK"),
2042                                             GTK_RESPONSE_OK,
2043                                             NULL);
2044 
2045       label = gtk_label_new (NULL);
2046       title = g_strdup_printf ("<b>%s</b>", title);
2047       gtk_label_set_markup(GTK_LABEL (label), title);
2048       g_free (title);
2049 
2050 	  gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label);
2051 
2052       textview = gtk_text_view_new ();
2053       buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
2054       gtk_text_buffer_set_text (buffer, combined_msg, -1);
2055       gtk_text_view_set_editable (GTK_TEXT_VIEW(textview), FALSE);
2056 
2057       scrolled = gtk_scrolled_window_new (NULL, NULL);
2058       gtk_container_add (GTK_CONTAINER (scrolled), textview);
2059       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2060       gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area(GTK_DIALOG(dialog))), scrolled);
2061 
2062       gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 200);
2063 
2064       gtk_widget_show (label);
2065       gtk_widget_show (textview);
2066       gtk_widget_show (scrolled);
2067 
2068       gtk_dialog_run (GTK_DIALOG (dialog));
2069 
2070       gtk_widget_destroy (dialog);
2071       g_free (combined_msg);
2072     }
2073   }
2074 
2075   return image;
2076 }
2077 
2078 /****************/
2079 /* loading code */
2080 /****************/
2081 typedef struct
2082 {
2083   gboolean started;
2084   XfburnDataComposition *dc;
2085   GQueue *queue_iter;
2086 } LoadParserStruct;
2087 
2088 /*
2089 static gint
2090 _find_attribute (const gchar ** attribute_names, const gchar * attr)
2091 {
2092   gint i;
2093 
2094   for (i = 0; attribute_names[i]; i++) {
2095     if (!strcmp (attribute_names[i], attr))
2096       return i;
2097   }
2098 
2099   return -1;
2100 }
2101 */
2102 
2103 static void
load_composition_start(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer data,GError ** error)2104 load_composition_start (GMarkupParseContext * context, const gchar * element_name,
2105                         const gchar ** attribute_names, const gchar ** attribute_values,
2106                         gpointer data, GError ** error)
2107 {
2108   g_error ("This method needs to get fixed, and does not work right now!");
2109 /*
2110   LoadParserStruct * parserinfo = (LoadParserStruct *) data;
2111   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (parserinfo->dc);
2112 
2113 
2114   if (!(parserinfo->started) && !strcmp (element_name, "xfburn-composition"))
2115     parserinfo->started = TRUE;
2116   else if (!(parserinfo->started))
2117     return;
2118 
2119   if (!strcmp (element_name, "file")) {
2120     int i, j;
2121 
2122     if ((i = _find_attribute (attribute_names, "name")) != -1 &&
2123         (j = _find_attribute (attribute_names, "source")) != -1) {
2124       //GtkTreeIter iter;
2125       GtkTreeIter *parent;
2126       GtkTreeModel *model;
2127 
2128       model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->content));
2129       parent = g_queue_peek_head (parserinfo->queue_iter);
2130 
2131       add_file_to_list_with_name (attribute_values[i], parserinfo->dc, model, attribute_values[j], &iter,
2132                                   parent, GTK_TREE_VIEW_DROP_INTO_OR_AFTER);
2133     }
2134   } else if (!strcmp (element_name, "directory")) {
2135     int i, j;
2136 
2137     if ((i = _find_attribute (attribute_names, "name")) != -1 &&
2138         (j = _find_attribute (attribute_names, "source")) != -1) {
2139       //GtkTreeIter iter;
2140       GtkTreeModel *model;
2141 
2142       model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->content));
2143 
2144       //add_directory_to_list (attribute_values[i], parserinfo->dc, model, attribute_values[j], &iter, parent);
2145     }
2146   }
2147   */
2148 }
2149 
2150 static void
load_composition_end(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)2151 load_composition_end (GMarkupParseContext * context, const gchar * element_name, gpointer user_data, GError ** error)
2152 {
2153   LoadParserStruct *parserinfo = (LoadParserStruct *) user_data;
2154 
2155   if (!parserinfo->started)
2156     return;
2157 
2158   if (!strcmp (element_name, "xfburn-composition"))
2159     parserinfo->started = FALSE;
2160 
2161   if (!strcmp (element_name, "directory"))
2162     parserinfo->queue_iter = g_queue_pop_head (parserinfo->queue_iter);
2163 }
2164 
2165 static void
load_from_file(XfburnComposition * composition,const gchar * filename)2166 load_from_file (XfburnComposition * composition, const gchar * filename)
2167 {
2168   gchar *file_contents = NULL;
2169   GMarkupParseContext *gpcontext = NULL;
2170   struct stat st;
2171   LoadParserStruct parserinfo;
2172   GMarkupParser gmparser = {
2173     load_composition_start, load_composition_end, NULL, NULL, NULL
2174   };
2175   GError *err = NULL;
2176 #ifdef HAVE_MMAP
2177   gint fd = -1;
2178   void *maddr = NULL;
2179 #endif
2180   g_return_if_fail (filename != NULL);
2181   if (stat (filename, &st) < 0) {
2182     g_warning ("Unable to open %s", filename);
2183     goto cleanup;
2184   }
2185 
2186 #ifdef HAVE_MMAP
2187   fd = open (filename, O_RDONLY, 0);
2188   if (fd < 0)
2189     goto cleanup;
2190   maddr = mmap (NULL, st.st_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0);
2191   if (maddr)
2192     file_contents = maddr;
2193 #endif
2194   if (!file_contents && !g_file_get_contents (filename, &file_contents, NULL, &err)) {
2195     if (err) {
2196       g_warning ("Unable to read file '%s' (%d): %s", filename, err->code, err->message);
2197       g_error_free (err);
2198     }
2199     goto cleanup;
2200   }
2201 
2202   parserinfo.started = FALSE;
2203   parserinfo.dc = XFBURN_DATA_COMPOSITION (composition);
2204   parserinfo.queue_iter = g_queue_new ();
2205   gpcontext = g_markup_parse_context_new (&gmparser, 0, &parserinfo, NULL);
2206   if (!g_markup_parse_context_parse (gpcontext, file_contents, st.st_size, &err)) {
2207     g_warning ("Error parsing composition (%d): %s", err->code, err->message);
2208     g_error_free (err);
2209     goto cleanup;
2210   }
2211 
2212   if (g_markup_parse_context_end_parse (gpcontext, NULL)) {
2213     DBG ("parsed");
2214   }
2215 
2216   g_queue_free (parserinfo.queue_iter);
2217 
2218 cleanup:
2219   if (gpcontext)
2220     g_markup_parse_context_free (gpcontext);
2221 #ifdef HAVE_MMAP
2222   if (maddr) {
2223     munmap (maddr, st.st_size);
2224     file_contents = NULL;
2225   }
2226   if (fd > -1)
2227     close (fd);
2228 #endif
2229   if (file_contents)
2230     g_free (file_contents);
2231 }
2232 
2233 
2234 /***************/
2235 /* saving code */
2236 /***************/
2237 typedef struct
2238 {
2239   FILE *file_content;
2240   gint last_depth;
2241 } CompositionSaveInfo;
2242 
2243 static gboolean
foreach_save(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,CompositionSaveInfo * info)2244 foreach_save (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, CompositionSaveInfo *info)
2245 {
2246   gchar *space = NULL;
2247   gint i;
2248   gchar *name = NULL;
2249   gchar *source_path = NULL;
2250   DataCompositionEntryType type;
2251 
2252   space = g_strnfill (gtk_tree_path_get_depth (path), '\t');
2253 
2254   for (i = info->last_depth; i > gtk_tree_path_get_depth (path); i--) {
2255     gchar *space2 = NULL;
2256 
2257     space2 = g_strnfill (i - 1, '\t');
2258     fprintf (info->file_content, "%s</directory>\n", space2);
2259 
2260     g_free (space2);
2261   }
2262 
2263   gtk_tree_model_get (model, iter, DATA_COMPOSITION_COLUMN_CONTENT, &name,
2264                       DATA_COMPOSITION_COLUMN_PATH, &source_path,
2265                       DATA_COMPOSITION_COLUMN_TYPE, &type, -1);
2266 
2267   fprintf (info->file_content, "%s", space);
2268   switch (type) {
2269   case DATA_COMPOSITION_TYPE_FILE:
2270     fprintf (info->file_content, "<file name=\"%s\" source=\"%s\" />\n", name, source_path);
2271     break;
2272   case DATA_COMPOSITION_TYPE_DIRECTORY:
2273     fprintf (info->file_content, "<directory name=\"%s\" source=\"%s\"", name, source_path);
2274 
2275     if (gtk_tree_model_iter_has_child (model, iter))
2276       fprintf (info->file_content, ">\n");
2277     else
2278       fprintf (info->file_content, "/>\n");
2279 
2280     break;
2281   }
2282 
2283 
2284   info->last_depth = gtk_tree_path_get_depth (path);
2285 
2286   g_free (space);
2287   g_free (name);
2288   g_free (source_path);
2289 
2290   return FALSE;
2291 }
2292 
2293 static void
save_to_file(XfburnComposition * composition)2294 save_to_file (XfburnComposition * composition)
2295 {
2296   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (composition);
2297   FILE *file_content;
2298   GtkTreeModel *model;
2299   CompositionSaveInfo info;
2300   gint i;
2301 
2302   if (!(priv->filename)) {
2303     priv->filename = g_strdup ("/tmp/gna");
2304 
2305     g_signal_emit_by_name (G_OBJECT (composition), "name-changed", priv->filename);
2306   }
2307 
2308   file_content = fopen (priv->filename, "w+");
2309   fprintf (file_content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
2310   fprintf (file_content, "<xfburn-composition version=\"0.1\">\n");
2311 
2312   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->content));
2313   info.file_content = file_content;
2314   info.last_depth = 0;
2315   gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) foreach_save, &info);
2316 
2317   for (i = info.last_depth; i > 1; i--) {
2318     gchar *space2 = NULL;
2319 
2320     space2 = g_strnfill (i - 1, '\t');
2321     fprintf (info.file_content, "%s</directory>\n", space2);
2322 
2323     g_free (space2);
2324   }
2325 
2326   fprintf (file_content, "</xfburn-composition>\n");
2327   fclose (file_content);
2328 }
2329 
2330 /******************/
2331 /* public methods */
2332 /******************/
2333 GtkWidget *
xfburn_data_composition_new(void)2334 xfburn_data_composition_new (void)
2335 {
2336   return g_object_new (xfburn_data_composition_get_type (), NULL);
2337 }
2338 
2339 void
xfburn_data_composition_add_files(XfburnDataComposition * dc,GSList * filelist)2340 xfburn_data_composition_add_files (XfburnDataComposition *dc, GSList * filelist)
2341 {
2342   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (dc);
2343   ThreadAddFilesCLIParams *params;
2344 
2345   if (filelist != NULL) {
2346     params = g_new (ThreadAddFilesCLIParams, 1);
2347 
2348     params->filelist = filelist;
2349     params->dc = dc;
2350 
2351     xfburn_adding_progress_show (XFBURN_ADDING_PROGRESS (priv->progress));
2352     xfburn_busy_cursor (priv->content);
2353 
2354     priv->thread_params = params;
2355     g_thread_new ("data_add_files_cli", (GThreadFunc) thread_add_files_cli, params);
2356   }
2357 }
2358 
2359 void
xfburn_data_composition_hide_toolbar(XfburnDataComposition * composition)2360 xfburn_data_composition_hide_toolbar (XfburnDataComposition * composition)
2361 {
2362   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (composition);
2363 
2364   gtk_widget_hide (priv->toolbar);
2365 }
2366 
2367 void
xfburn_data_composition_show_toolbar(XfburnDataComposition * composition)2368 xfburn_data_composition_show_toolbar (XfburnDataComposition * composition)
2369 {
2370   XfburnDataCompositionPrivate *priv = XFBURN_DATA_COMPOSITION_GET_PRIVATE (composition);
2371 
2372   gtk_widget_show (priv->toolbar);
2373 }
2374 
2375