1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) James Liggett 2010 <jrliggett@cox.net>
6 * anjuta is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * anjuta is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "anjuta-file-list.h"
38 static GtkTargetEntry dnd_target_entries
[] =
47 struct _AnjutaFileListPriv
51 GtkListStore
*list_model
;
52 GtkWidget
*add_button
;
53 GtkWidget
*copy_button
;
54 GtkWidget
*remove_button
;
56 /* The placeholder iter tells the user that a file can be entered into the
57 * view, or dragged into it. */
58 GtkTreeIter placeholder
;
61 G_DEFINE_TYPE (AnjutaFileList
, anjuta_file_list
, GTK_TYPE_VBOX
);
64 anjuta_file_list_append_placeholder (AnjutaFileList
*self
)
68 gtk_list_store_append (self
->priv
->list_model
, &iter
);
70 gtk_list_store_set (self
->priv
->list_model
, &iter
, COL_PATH
, NULL
, -1);
72 self
->priv
->placeholder
= iter
;
77 path_cell_data_func (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
78 GtkTreeModel
*model
, GtkTreeIter
*iter
,
79 GtkTreeView
*list_view
)
84 gtk_tree_model_get (model
, iter
, COL_PATH
, &path
, -1);
85 style
= gtk_widget_get_style (GTK_WIDGET (list_view
));
87 /* NULL path means this is the placeholder */
90 g_object_set (G_OBJECT (renderer
),
91 "foreground-gdk", &(style
->text
[GTK_STATE_NORMAL
]),
92 "style", PANGO_STYLE_NORMAL
,
98 g_object_set (G_OBJECT (renderer
),
99 "foreground-gdk", &(style
->text
[GTK_STATE_INSENSITIVE
]),
100 "style", PANGO_STYLE_ITALIC
,
101 "text", _("Drop a file or enter a path here"),
109 on_path_renderer_editing_started (GtkCellRenderer
*renderer
,
110 GtkCellEditable
*editable
,
111 const gchar
*tree_path
,
112 GtkTreeModel
*list_model
)
117 /* Don't show placeholder text in the edit widget */
118 gtk_tree_model_get_iter_from_string (list_model
, &iter
, tree_path
);
120 gtk_tree_model_get (list_model
, &iter
, COL_PATH
, &path
, -1);
124 if (GTK_IS_ENTRY (editable
))
125 gtk_entry_set_text (GTK_ENTRY (editable
), "");
130 on_path_renderer_edited (GtkCellRendererText
*renderer
, gchar
*path
,
131 gchar
*new_text
, AnjutaFileList
*self
)
136 gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (self
->priv
->list_model
),
138 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->list_model
), &iter
,
139 COL_PATH
, ¤t_path
, -1);
141 /* Interpret empty new_text as a cancellation of the edit */
142 if (g_utf8_strlen (new_text
, -1) > 0)
144 /* If the placeholder is being edited, a new one has to be created */
146 anjuta_file_list_append_placeholder (self
);
148 gtk_list_store_set (self
->priv
->list_model
, &iter
, COL_PATH
, new_text
,
154 on_list_view_drag_data_received (GtkWidget
*list_view
, GdkDragContext
*context
,
155 gint x
, gint y
, GtkSelectionData
*data
,
156 gint target_type
, gint time
,
157 AnjutaFileList
*self
)
169 if ((data
!= NULL
) &&
170 (gtk_selection_data_get_length (data
) >= 0))
172 if (target_type
== 0)
174 uri_list
= gtk_selection_data_get_uris (data
);
177 if (self
->priv
->relative_path
)
178 parent_file
= g_file_new_for_path (self
->priv
->relative_path
);
180 for (i
= 0; uri_list
[i
]; i
++)
182 file
= g_file_new_for_uri (uri_list
[i
]);
186 path
= g_file_get_relative_path (parent_file
, file
);
188 g_object_unref (parent_file
);
191 path
= g_file_get_path (file
);
195 gtk_list_store_insert_before (self
->priv
->list_model
,
197 &(self
->priv
->placeholder
));
198 gtk_list_store_set (self
->priv
->list_model
, &iter
,
205 g_object_unref (file
);
210 g_strfreev (uri_list
);
214 /* Do not delete source data */
215 gtk_drag_finish (context
, success
, FALSE
, time
);
219 on_list_view_drag_drop (GtkWidget
*widget
, GdkDragContext
*context
,
220 gint x
, gint y
, guint time
, gpointer user_data
)
224 target_type
= gtk_drag_dest_find_target (widget
, context
, NULL
);
226 if (target_type
!= GDK_NONE
)
227 gtk_drag_get_data (widget
, context
, target_type
, time
);
229 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
235 on_list_view_item_selected (GtkTreeSelection
*selection
, GtkTreeModel
*model
,
236 GtkTreePath
*tree_path
,
237 gboolean path_currently_selected
,
238 AnjutaFileList
*self
)
247 if (!path_currently_selected
)
249 gtk_tree_model_get_iter (model
, &iter
, tree_path
);
250 gtk_tree_model_get (model
, &iter
, COL_PATH
, &path
, -1);
260 gtk_widget_set_sensitive (self
->priv
->copy_button
, sensitive
);
261 gtk_widget_set_sensitive (self
->priv
->remove_button
, sensitive
);
267 on_add_button_clicked (GtkButton
*button
, AnjutaFileList
*self
)
269 GtkWidget
*file_dialog
;
271 GSList
*current_path
;
274 file_dialog
= gtk_file_chooser_dialog_new (_("Select Files"),
275 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button
))),
276 GTK_FILE_CHOOSER_ACTION_OPEN
,
283 gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (file_dialog
),
286 if (gtk_dialog_run (GTK_DIALOG (file_dialog
)) == GTK_RESPONSE_ACCEPT
)
288 paths
= gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (file_dialog
));
289 current_path
= paths
;
293 gtk_list_store_insert_before (self
->priv
->list_model
, &iter
,
294 &(self
->priv
->placeholder
));
295 gtk_list_store_set (self
->priv
->list_model
, &iter
,
296 COL_PATH
, current_path
->data
,
299 g_free (current_path
->data
);
301 current_path
= g_slist_next (current_path
);
304 g_slist_free (paths
);
309 gtk_widget_destroy (file_dialog
);
313 on_copy_button_clicked (GtkButton
*button
, GtkTreeSelection
*selection
)
315 GtkTreeModel
*list_model
;
316 GtkTreeIter selected_iter
;
317 GtkTreeIter new_iter
;
320 if (gtk_tree_selection_get_selected (selection
, &list_model
,
323 gtk_tree_model_get (list_model
, &selected_iter
, COL_PATH
, &path
, -1);
324 gtk_list_store_insert_after (GTK_LIST_STORE (list_model
), &new_iter
,
327 gtk_list_store_set (GTK_LIST_STORE (list_model
), &new_iter
, COL_PATH
,
335 on_remove_button_clicked (GtkButton
*button
, GtkTreeSelection
*selection
)
338 GtkTreeModel
*list_model
;
340 if (gtk_tree_selection_get_selected (selection
, &list_model
, &iter
))
341 gtk_list_store_remove (GTK_LIST_STORE (list_model
), &iter
);
345 anjuta_file_list_init (AnjutaFileList
*self
)
347 GtkWidget
*scrolled_window
;
348 GtkWidget
*button_box
;
349 GtkWidget
*clear_button
;
350 GtkTreeSelection
*selection
;
351 GtkTreeViewColumn
*column
;
352 GtkCellRenderer
*renderer
;
354 self
->priv
= g_new0 (AnjutaFileListPriv
, 1);
355 self
->priv
->list_view
= gtk_tree_view_new ();
356 self
->priv
->list_model
= gtk_list_store_new (NUM_COLS
, G_TYPE_STRING
);
357 self
->priv
->add_button
= gtk_button_new_from_stock (GTK_STOCK_ADD
);
358 self
->priv
->copy_button
= gtk_button_new_from_stock (GTK_STOCK_COPY
);
359 self
->priv
->remove_button
= gtk_button_new_from_stock (GTK_STOCK_REMOVE
);
361 button_box
= gtk_hbutton_box_new ();
362 scrolled_window
= gtk_scrolled_window_new (NULL
, NULL
);
363 clear_button
= gtk_button_new_from_stock (GTK_STOCK_CLEAR
);
365 gtk_widget_set_sensitive (self
->priv
->copy_button
, FALSE
);
366 gtk_widget_set_sensitive (self
->priv
->remove_button
, FALSE
);
368 g_signal_connect_swapped (G_OBJECT (clear_button
), "clicked",
369 G_CALLBACK (anjuta_file_list_clear
),
373 gtk_box_set_spacing (GTK_BOX (self
), 2);
374 gtk_box_set_homogeneous (GTK_BOX (self
), FALSE
);
376 gtk_tree_view_set_model (GTK_TREE_VIEW (self
->priv
->list_view
),
377 GTK_TREE_MODEL (self
->priv
->list_model
));
378 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self
->priv
->list_view
),
382 column
= gtk_tree_view_column_new ();
383 renderer
= gtk_cell_renderer_text_new ();
385 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
386 gtk_tree_view_column_set_cell_data_func (column
, renderer
,
387 (GtkTreeCellDataFunc
) path_cell_data_func
,
388 self
->priv
->list_view
,
390 g_object_set (G_OBJECT (renderer
), "editable", TRUE
, NULL
);
391 gtk_tree_view_append_column (GTK_TREE_VIEW (self
->priv
->list_view
),
394 g_signal_connect (G_OBJECT (renderer
), "editing-started",
395 G_CALLBACK (on_path_renderer_editing_started
),
396 self
->priv
->list_model
);
398 g_signal_connect (G_OBJECT (renderer
), "edited",
399 G_CALLBACK (on_path_renderer_edited
),
403 gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (self
->priv
->list_view
),
405 G_N_ELEMENTS (dnd_target_entries
),
408 g_signal_connect (G_OBJECT (self
->priv
->list_view
), "drag-drop",
409 G_CALLBACK (on_list_view_drag_drop
),
412 g_signal_connect (G_OBJECT (self
->priv
->list_view
), "drag-data-received",
413 G_CALLBACK (on_list_view_drag_data_received
),
416 /* Selection handling */
417 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (self
->priv
->list_view
));
419 gtk_tree_selection_set_select_function (selection
,
420 (GtkTreeSelectionFunc
) on_list_view_item_selected
,
423 g_signal_connect (G_OBJECT (self
->priv
->add_button
), "clicked",
424 G_CALLBACK (on_add_button_clicked
),
427 g_signal_connect (G_OBJECT (self
->priv
->copy_button
), "clicked",
428 G_CALLBACK (on_copy_button_clicked
),
431 g_signal_connect (G_OBJECT (self
->priv
->remove_button
), "clicked",
432 G_CALLBACK (on_remove_button_clicked
),
435 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window
),
436 GTK_POLICY_AUTOMATIC
,
437 GTK_POLICY_AUTOMATIC
);
438 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window
),
440 gtk_container_add (GTK_CONTAINER (scrolled_window
), self
->priv
->list_view
);
441 gtk_box_pack_start (GTK_BOX (self
), scrolled_window
, TRUE
, TRUE
, 0);
444 gtk_box_set_spacing (GTK_BOX (button_box
), 5);
445 gtk_button_box_set_layout (GTK_BUTTON_BOX (button_box
),
446 GTK_BUTTONBOX_START
);
448 gtk_container_add (GTK_CONTAINER (button_box
), self
->priv
->add_button
);
449 gtk_container_add (GTK_CONTAINER (button_box
), self
->priv
->copy_button
);
450 gtk_container_add (GTK_CONTAINER (button_box
), self
->priv
->remove_button
);
451 gtk_container_add (GTK_CONTAINER (button_box
), clear_button
);
452 gtk_box_pack_start (GTK_BOX (self
), button_box
, FALSE
, FALSE
, 0);
454 anjuta_file_list_append_placeholder (self
);
456 gtk_widget_show_all (GTK_WIDGET (self
));
458 /* Don't show the Add button by default */
459 gtk_widget_set_visible (self
->priv
->add_button
, FALSE
);
463 anjuta_file_list_finalize (GObject
*object
)
465 AnjutaFileList
*self
;
467 self
= ANJUTA_FILE_LIST (object
);
469 g_free (self
->priv
->relative_path
);
472 G_OBJECT_CLASS (anjuta_file_list_parent_class
)->finalize (object
);
476 anjuta_file_list_set_property (GObject
*object
, guint prop_id
,
477 const GValue
*value
, GParamSpec
*pspec
)
479 AnjutaFileList
*self
;
481 g_return_if_fail (ANJUTA_IS_FILE_LIST (object
));
483 self
= ANJUTA_FILE_LIST (object
);
487 case PROP_RELATIVE_PATH
:
488 g_free (self
->priv
->relative_path
);
490 self
->priv
->relative_path
= g_value_dup_string (value
);
492 case PROP_SHOW_ADD_BUTTON
:
493 gtk_widget_set_visible (self
->priv
->add_button
,
494 g_value_get_boolean (value
));
497 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
503 anjuta_file_list_get_property (GObject
*object
, guint prop_id
, GValue
*value
,
506 AnjutaFileList
*self
;
508 g_return_if_fail (ANJUTA_IS_FILE_LIST (object
));
510 self
= ANJUTA_FILE_LIST (object
);
514 case PROP_RELATIVE_PATH
:
515 g_value_set_string (value
, self
->priv
->relative_path
);
517 case PROP_SHOW_ADD_BUTTON
:
518 g_value_set_boolean (value
,
519 gtk_widget_get_visible (self
->priv
->add_button
));
522 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
528 anjuta_file_list_class_init (AnjutaFileListClass
*klass
)
530 GObjectClass
* object_class
= G_OBJECT_CLASS (klass
);
532 object_class
->finalize
= anjuta_file_list_finalize
;
533 object_class
->set_property
= anjuta_file_list_set_property
;
534 object_class
->get_property
= anjuta_file_list_get_property
;
536 g_object_class_install_property (object_class
,
538 g_param_spec_string ("relative-path",
540 _("Path that all files in the list should be relative to"),
544 g_object_class_install_property (object_class
,
545 PROP_SHOW_ADD_BUTTON
,
546 g_param_spec_boolean ("show-add-button",
547 _("Show Add button"),
548 _("Display an Add button"),
555 anjuta_file_list_new (void)
557 return g_object_new (ANJUTA_TYPE_FILE_LIST
, NULL
);
561 list_model_foreach (GtkTreeModel
*list_model
, GtkTreePath
*tree_path
,
562 GtkTreeIter
*iter
, GList
**list
)
566 gtk_tree_model_get (list_model
, iter
, COL_PATH
, &path
, -1);
568 /* Make sure not to add the placeholder to the list */
570 *list
= g_list_append (*list
, path
);
576 anjuta_file_list_get_paths (AnjutaFileList
*self
)
582 gtk_tree_model_foreach (GTK_TREE_MODEL (self
->priv
->list_model
),
583 (GtkTreeModelForeachFunc
) list_model_foreach
,
590 anjuta_file_list_set_relative_path (AnjutaFileList
*self
, const gchar
*path
)
592 g_object_set (G_OBJECT (self
), "relative-path", path
, NULL
);
596 anjuta_file_list_clear (AnjutaFileList
*self
)
598 gtk_list_store_clear (self
->priv
->list_model
);
600 anjuta_file_list_append_placeholder (self
);