Avoid linking programs with plugin libraries
[anjuta.git] / libanjuta / anjuta-file-list.c
blob17a5b0ed9b0338f5c23ac7b1c539d1a2d56ccc21
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta
4 * Copyright (C) James Liggett 2010 <jrliggett@cox.net>
5 *
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"
22 enum
24 PROP_0,
26 PROP_RELATIVE_PATH,
27 PROP_SHOW_ADD_BUTTON
30 enum
32 COL_PATH,
34 NUM_COLS
37 /* DND Targets */
38 static GtkTargetEntry dnd_target_entries[] =
41 "text/uri-list",
47 struct _AnjutaFileListPriv
49 gchar *relative_path;
50 GtkWidget *list_view;
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_BOX);
63 static void
64 anjuta_file_list_append_placeholder (AnjutaFileList *self)
66 GtkTreeIter iter;
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;
76 static void
77 path_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
78 GtkTreeModel *model, GtkTreeIter *iter,
79 GtkTreeView *list_view)
81 gchar *path;
82 GtkStyleContext *context;
83 GdkRGBA fg_color;
85 gtk_tree_model_get (model, iter, COL_PATH, &path, -1);
86 context = gtk_widget_get_style_context (GTK_WIDGET (list_view));
88 /* NULL path means this is the placeholder */
89 if (path)
91 gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &fg_color);
92 g_object_set (G_OBJECT (renderer),
93 "foreground-rgba", &fg_color,
94 "style", PANGO_STYLE_NORMAL,
95 "text", path,
96 NULL);
98 else
100 gtk_style_context_get_color (context, GTK_STATE_FLAG_INSENSITIVE, &fg_color);
101 g_object_set (G_OBJECT (renderer),
102 "foreground-rgba", &fg_color,
103 "style", PANGO_STYLE_ITALIC,
104 "text", _("Drop a file or enter a path here"),
105 NULL);
108 g_free (path);
111 static void
112 on_path_renderer_editing_started (GtkCellRenderer *renderer,
113 GtkCellEditable *editable,
114 const gchar *tree_path,
115 GtkTreeModel *list_model)
117 GtkTreeIter iter;
118 gchar *path;
120 /* Don't show placeholder text in the edit widget */
121 gtk_tree_model_get_iter_from_string (list_model, &iter, tree_path);
123 gtk_tree_model_get (list_model, &iter, COL_PATH, &path, -1);
125 if (!path)
127 if (GTK_IS_ENTRY (editable))
128 gtk_entry_set_text (GTK_ENTRY (editable), "");
132 static void
133 on_path_renderer_edited (GtkCellRendererText *renderer, gchar *path,
134 gchar *new_text, AnjutaFileList *self)
136 GtkTreeIter iter;
137 gchar *current_path;
139 gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (self->priv->list_model),
140 &iter, path);
141 gtk_tree_model_get (GTK_TREE_MODEL (self->priv->list_model), &iter,
142 COL_PATH, &current_path, -1);
144 /* Interpret empty new_text as a cancellation of the edit */
145 if (g_utf8_strlen (new_text, -1) > 0)
147 /* If the placeholder is being edited, a new one has to be created */
148 if (!current_path)
149 anjuta_file_list_append_placeholder (self);
151 gtk_list_store_set (self->priv->list_model, &iter, COL_PATH, new_text,
152 -1);
156 static void
157 on_list_view_drag_data_received (GtkWidget *list_view, GdkDragContext *context,
158 gint x, gint y, GtkSelectionData *data,
159 gint target_type, gint time,
160 AnjutaFileList *self)
162 gboolean success;
163 gchar **uri_list;
164 gint i;
165 GFile *parent_file;
166 GFile *file;
167 gchar *path;
168 GtkTreeIter iter;
170 success = FALSE;
172 if ((data != NULL) &&
173 (gtk_selection_data_get_length (data) >= 0))
175 if (target_type == 0)
177 uri_list = gtk_selection_data_get_uris (data);
178 parent_file = NULL;
180 if (self->priv->relative_path)
181 parent_file = g_file_new_for_path (self->priv->relative_path);
183 for (i = 0; uri_list[i]; i++)
185 file = g_file_new_for_uri (uri_list[i]);
187 if (parent_file)
189 path = g_file_get_relative_path (parent_file, file);
191 g_object_unref (parent_file);
193 else
194 path = g_file_get_path (file);
196 if (path)
198 gtk_list_store_insert_before (self->priv->list_model,
199 &iter,
200 &(self->priv->placeholder));
201 gtk_list_store_set (self->priv->list_model, &iter,
202 COL_PATH, path,
203 -1);
205 g_free (path);
208 g_object_unref (file);
211 success = TRUE;
213 g_strfreev (uri_list);
217 /* Do not delete source data */
218 gtk_drag_finish (context, success, FALSE, time);
221 static gboolean
222 on_list_view_drag_drop (GtkWidget *widget, GdkDragContext *context,
223 gint x, gint y, guint time, gpointer user_data)
225 GdkAtom target_type;
227 target_type = gtk_drag_dest_find_target (widget, context, NULL);
229 if (target_type != GDK_NONE)
230 gtk_drag_get_data (widget, context, target_type, time);
231 else
232 gtk_drag_finish (context, FALSE, FALSE, time);
234 return TRUE;
237 static gboolean
238 on_list_view_item_selected (GtkTreeSelection *selection, GtkTreeModel *model,
239 GtkTreePath *tree_path,
240 gboolean path_currently_selected,
241 AnjutaFileList *self)
244 gboolean sensitive;
245 GtkTreeIter iter;
246 gchar *path;
248 sensitive = FALSE;
250 if (!path_currently_selected)
252 gtk_tree_model_get_iter (model, &iter, tree_path);
253 gtk_tree_model_get (model, &iter, COL_PATH, &path, -1);
255 if (path)
257 sensitive = TRUE;
259 g_free (path);
263 gtk_widget_set_sensitive (self->priv->copy_button, sensitive);
264 gtk_widget_set_sensitive (self->priv->remove_button, sensitive);
266 return TRUE;
269 static void
270 on_add_button_clicked (GtkButton *button, AnjutaFileList *self)
272 GtkWidget *file_dialog;
273 GSList *paths;
274 GSList *current_path;
275 GtkTreeIter iter;
277 file_dialog = gtk_file_chooser_dialog_new (_("Select Files"),
278 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))),
279 GTK_FILE_CHOOSER_ACTION_OPEN,
280 GTK_STOCK_CANCEL,
281 GTK_RESPONSE_CANCEL,
282 GTK_STOCK_OPEN,
283 GTK_RESPONSE_ACCEPT,
284 NULL);
286 gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (file_dialog),
287 TRUE);
289 if (gtk_dialog_run (GTK_DIALOG (file_dialog)) == GTK_RESPONSE_ACCEPT)
291 paths = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (file_dialog));
292 current_path = paths;
294 while (current_path)
296 gtk_list_store_insert_before (self->priv->list_model, &iter,
297 &(self->priv->placeholder));
298 gtk_list_store_set (self->priv->list_model, &iter,
299 COL_PATH, current_path->data,
300 -1);
302 g_free (current_path->data);
304 current_path = g_slist_next (current_path);
307 g_slist_free (paths);
312 gtk_widget_destroy (file_dialog);
315 static void
316 on_copy_button_clicked (GtkButton *button, GtkTreeSelection *selection)
318 GtkTreeModel *list_model;
319 GtkTreeIter selected_iter;
320 GtkTreeIter new_iter;
321 gchar *path;
323 if (gtk_tree_selection_get_selected (selection, &list_model,
324 &selected_iter))
326 gtk_tree_model_get (list_model, &selected_iter, COL_PATH, &path, -1);
327 gtk_list_store_insert_after (GTK_LIST_STORE (list_model), &new_iter,
328 &selected_iter);
330 gtk_list_store_set (GTK_LIST_STORE (list_model), &new_iter, COL_PATH,
331 path, -1);
333 g_free (path);
337 static void
338 on_remove_button_clicked (GtkButton *button, GtkTreeSelection *selection)
340 GtkTreeIter iter;
341 GtkTreeModel *list_model;
343 if (gtk_tree_selection_get_selected (selection, &list_model, &iter))
344 gtk_list_store_remove (GTK_LIST_STORE (list_model), &iter);
347 static void
348 anjuta_file_list_init (AnjutaFileList *self)
350 GtkWidget *scrolled_window;
351 GtkWidget *button_box;
352 GtkWidget *clear_button;
353 GtkTreeSelection *selection;
354 GtkTreeViewColumn *column;
355 GtkCellRenderer *renderer;
358 /* Set properties */
359 g_object_set (self, "orientation", GTK_ORIENTATION_VERTICAL, NULL);
361 self->priv = g_new0 (AnjutaFileListPriv, 1);
362 self->priv->list_view = gtk_tree_view_new ();
363 self->priv->list_model = gtk_list_store_new (NUM_COLS, G_TYPE_STRING);
364 self->priv->add_button = gtk_button_new_from_stock (GTK_STOCK_ADD);
365 self->priv->copy_button = gtk_button_new_from_stock (GTK_STOCK_COPY);
366 self->priv->remove_button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
368 button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
369 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
370 clear_button = gtk_button_new_from_stock (GTK_STOCK_CLEAR);
372 gtk_widget_set_sensitive (self->priv->copy_button, FALSE);
373 gtk_widget_set_sensitive (self->priv->remove_button, FALSE);
375 g_signal_connect_swapped (G_OBJECT (clear_button), "clicked",
376 G_CALLBACK (anjuta_file_list_clear),
377 self);
379 /* File list view */
380 gtk_box_set_spacing (GTK_BOX (self), 2);
381 gtk_box_set_homogeneous (GTK_BOX (self), FALSE);
383 gtk_tree_view_set_model (GTK_TREE_VIEW (self->priv->list_view),
384 GTK_TREE_MODEL (self->priv->list_model));
385 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self->priv->list_view),
386 FALSE);
388 /* Path column */
389 column = gtk_tree_view_column_new ();
390 renderer = gtk_cell_renderer_text_new ();
392 gtk_tree_view_column_pack_start (column, renderer, TRUE);
393 gtk_tree_view_column_set_cell_data_func (column, renderer,
394 (GtkTreeCellDataFunc) path_cell_data_func,
395 self->priv->list_view,
396 NULL);
397 g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
398 gtk_tree_view_append_column (GTK_TREE_VIEW (self->priv->list_view),
399 column);
401 g_signal_connect (G_OBJECT (renderer), "editing-started",
402 G_CALLBACK (on_path_renderer_editing_started),
403 self->priv->list_model);
405 g_signal_connect (G_OBJECT (renderer), "edited",
406 G_CALLBACK (on_path_renderer_edited),
407 self);
409 /* DND */
410 gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (self->priv->list_view),
411 dnd_target_entries,
412 G_N_ELEMENTS (dnd_target_entries),
413 GDK_ACTION_COPY);
415 g_signal_connect (G_OBJECT (self->priv->list_view), "drag-drop",
416 G_CALLBACK (on_list_view_drag_drop),
417 NULL);
419 g_signal_connect (G_OBJECT (self->priv->list_view), "drag-data-received",
420 G_CALLBACK (on_list_view_drag_data_received),
421 self);
423 /* Selection handling */
424 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->list_view));
426 gtk_tree_selection_set_select_function (selection,
427 (GtkTreeSelectionFunc) on_list_view_item_selected,
428 self, NULL);
430 g_signal_connect (G_OBJECT (self->priv->add_button), "clicked",
431 G_CALLBACK (on_add_button_clicked),
432 self);
434 g_signal_connect (G_OBJECT (self->priv->copy_button), "clicked",
435 G_CALLBACK (on_copy_button_clicked),
436 selection);
438 g_signal_connect (G_OBJECT (self->priv->remove_button), "clicked",
439 G_CALLBACK (on_remove_button_clicked),
440 selection);
442 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
443 GTK_POLICY_AUTOMATIC,
444 GTK_POLICY_AUTOMATIC);
445 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
446 GTK_SHADOW_IN);
447 gtk_container_add (GTK_CONTAINER (scrolled_window), self->priv->list_view);
448 gtk_box_pack_start (GTK_BOX (self), scrolled_window, TRUE, TRUE, 0);
450 /* Button box */
451 gtk_box_set_spacing (GTK_BOX (button_box), 5);
452 gtk_button_box_set_layout (GTK_BUTTON_BOX (button_box),
453 GTK_BUTTONBOX_START);
455 gtk_container_add (GTK_CONTAINER (button_box), self->priv->add_button);
456 gtk_container_add (GTK_CONTAINER (button_box), self->priv->copy_button);
457 gtk_container_add (GTK_CONTAINER (button_box), self->priv->remove_button);
458 gtk_container_add (GTK_CONTAINER (button_box), clear_button);
459 gtk_box_pack_start (GTK_BOX (self), button_box, FALSE, FALSE, 0);
461 anjuta_file_list_append_placeholder (self);
463 gtk_widget_show_all (GTK_WIDGET (self));
465 /* Don't show the Add button by default */
466 gtk_widget_set_visible (self->priv->add_button, FALSE);
469 static void
470 anjuta_file_list_finalize (GObject *object)
472 AnjutaFileList *self;
474 self = ANJUTA_FILE_LIST (object);
476 g_free (self->priv->relative_path);
477 g_free (self->priv);
479 G_OBJECT_CLASS (anjuta_file_list_parent_class)->finalize (object);
482 static void
483 anjuta_file_list_set_property (GObject *object, guint prop_id,
484 const GValue *value, GParamSpec *pspec)
486 AnjutaFileList *self;
488 g_return_if_fail (ANJUTA_IS_FILE_LIST (object));
490 self = ANJUTA_FILE_LIST (object);
492 switch (prop_id)
494 case PROP_RELATIVE_PATH:
495 g_free (self->priv->relative_path);
497 self->priv->relative_path = g_value_dup_string (value);
498 break;
499 case PROP_SHOW_ADD_BUTTON:
500 gtk_widget_set_visible (self->priv->add_button,
501 g_value_get_boolean (value));
502 break;
503 default:
504 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
505 break;
509 static void
510 anjuta_file_list_get_property (GObject *object, guint prop_id, GValue *value,
511 GParamSpec *pspec)
513 AnjutaFileList *self;
515 g_return_if_fail (ANJUTA_IS_FILE_LIST (object));
517 self = ANJUTA_FILE_LIST (object);
519 switch (prop_id)
521 case PROP_RELATIVE_PATH:
522 g_value_set_string (value, self->priv->relative_path);
523 break;
524 case PROP_SHOW_ADD_BUTTON:
525 g_value_set_boolean (value,
526 gtk_widget_get_visible (self->priv->add_button));
527 break;
528 default:
529 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
530 break;
534 static void
535 anjuta_file_list_class_init (AnjutaFileListClass *klass)
537 GObjectClass* object_class = G_OBJECT_CLASS (klass);
539 object_class->finalize = anjuta_file_list_finalize;
540 object_class->set_property = anjuta_file_list_set_property;
541 object_class->get_property = anjuta_file_list_get_property;
543 g_object_class_install_property (object_class,
544 PROP_RELATIVE_PATH,
545 g_param_spec_string ("relative-path",
546 "relative-path",
547 _("Path that all files in the list should be relative to"),
549 G_PARAM_READWRITE));
551 g_object_class_install_property (object_class,
552 PROP_SHOW_ADD_BUTTON,
553 g_param_spec_boolean ("show-add-button",
554 _("Show Add button"),
555 _("Display an Add button"),
556 FALSE,
557 G_PARAM_READWRITE));
561 GtkWidget *
562 anjuta_file_list_new (void)
564 return g_object_new (ANJUTA_TYPE_FILE_LIST, NULL);
567 static gboolean
568 list_model_foreach (GtkTreeModel *list_model, GtkTreePath *tree_path,
569 GtkTreeIter *iter, GList **list)
571 gchar *path;
573 gtk_tree_model_get (list_model, iter, COL_PATH, &path, -1);
575 /* Make sure not to add the placeholder to the list */
576 if (path)
577 *list = g_list_append (*list, path);
579 return FALSE;
582 GList *
583 anjuta_file_list_get_paths (AnjutaFileList *self)
585 GList *list;
587 list = NULL;
589 gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->list_model),
590 (GtkTreeModelForeachFunc) list_model_foreach,
591 &list);
593 return list;
596 void
597 anjuta_file_list_set_relative_path (AnjutaFileList *self, const gchar *path)
599 g_object_set (G_OBJECT (self), "relative-path", path, NULL);
602 void
603 anjuta_file_list_clear (AnjutaFileList *self)
605 gtk_list_store_clear (self->priv->list_model);
607 anjuta_file_list_append_placeholder (self);