1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * file-manager
4 * Copyright (C) Johannes Schmid 2007 <jhs@gnome.org>
5 *
6 * file-manager is free software.
7 *
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
11 * any later version.
12 *
13 * file-manager is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with file-manager. If not, write to:
20 * The Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301, USA.
23 */
24
25 #include "file-view.h"
26 #include "file-model.h"
27 #include "file-view-marshal.h"
28
29 #include <gdk/gdkkeysyms.h>
30 #include <gtk/gtk.h>
31
32 #include <gio/gio.h>
33
34 #include <string.h>
35
36 #define HAVE_TOOLTIP_API (GTK_MAJOR_VERSION > 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 12))
37 #include <glib/gi18n.h>
38 #include <glib/gprintf.h>
39 #include <glib/gstdio.h>
40
41 #include <libanjuta/anjuta-utils.h>
42 #include <libanjuta/anjuta-debug.h>
43 #include <libanjuta/anjuta-vcs-status.h>
44
45 typedef struct _AnjutaFileViewPrivate AnjutaFileViewPrivate;
46
47 struct _AnjutaFileViewPrivate
48 {
49 FileModel* model;
50
51 GList* saved_paths;
52 GtkTreeRowReference* current_selection;
53
54 GFile* pending_selected_file;
55 };
56
57 GtkTargetEntry uri_targets[] =
58 {
59 {
60 "text/uri-list",
61 0, /* no flags */
62 0
63 }
64 };
65
66
67 #define ANJUTA_FILE_VIEW_GET_PRIVATE(o) \
68 (G_TYPE_INSTANCE_GET_PRIVATE((o), ANJUTA_TYPE_FILE_VIEW, AnjutaFileViewPrivate))
69
70 G_DEFINE_TYPE (AnjutaFileView, file_view, GTK_TYPE_TREE_VIEW);
71
72 static const gchar*
get_status_string(AnjutaVcsStatus status)73 get_status_string(AnjutaVcsStatus status)
74 {
75 switch (status)
76 {
77 case ANJUTA_VCS_STATUS_NONE:
78 return NULL;
79 case ANJUTA_VCS_STATUS_MODIFIED:
80 return _("Modified");
81 case ANJUTA_VCS_STATUS_DELETED:
82 return _("Deleted");
83 case ANJUTA_VCS_STATUS_ADDED:
84 return _("Added");
85 case ANJUTA_VCS_STATUS_CONFLICTED:
86 return _("Conflicted");
87 case ANJUTA_VCS_STATUS_UPTODATE:
88 return _("Up-to-date");
89 case ANJUTA_VCS_STATUS_LOCKED:
90 return _("Locked");
91 case ANJUTA_VCS_STATUS_MISSING:
92 return _("Missing");
93 case ANJUTA_VCS_STATUS_UNVERSIONED:
94 return _("Unversioned");
95 case ANJUTA_VCS_STATUS_IGNORED:
96 return _("Ignored");
97 default:
98 break;
99 }
100
101 return NULL;
102 }
103
104 enum
105 {
106 PROP_BASE_PATH = 1,
107 PROP_END
108 };
109
110 void
file_view_refresh(AnjutaFileView * view)111 file_view_refresh (AnjutaFileView* view)
112 {
113 AnjutaFileViewPrivate* priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
114 GtkTreePath* tree_path;
115
116 file_model_refresh (priv->model);
117
118 tree_path = gtk_tree_path_new_first ();
119 gtk_tree_view_expand_row (GTK_TREE_VIEW (view), tree_path, FALSE);
120 gtk_tree_path_free (tree_path);
121 }
122
123
124 static void
file_view_rename_edit_finish(GtkCellRendererText * renderer,gchar * path,gchar * new_text,gpointer user_data)125 file_view_rename_edit_finish (GtkCellRendererText *renderer,
126 gchar *path,
127 gchar *new_text,
128 gpointer user_data) {
129 AnjutaFileView* view = (AnjutaFileView*) user_data ;
130 GFile* file = file_view_get_selected (view);
131
132
133 gchar * newFilename ;
134 gchar * oldFilename ;
135 gchar * oldPath ;
136 gchar * fileBasename ;
137 GFile * parent = NULL ;
138 if(!g_file_has_parent(file, NULL)) {/* You try to rename "/" */
139 anjuta_util_dialog_error(NULL, _("You can't rename \"/\"!"), NULL);
140 return ;
141 }
142
143 fileBasename = g_file_get_basename(file) ;
144 parent = g_file_get_parent(file) ;
145 oldPath = g_file_get_path (parent) ;
146
147 /* Creating the new filename */
148 newFilename = g_strconcat(oldPath, G_DIR_SEPARATOR_S, new_text, NULL);
149 oldFilename = g_strconcat(oldPath, G_DIR_SEPARATOR_S, fileBasename, NULL);
150
151 if(g_rename(oldFilename, newFilename))
152 anjuta_util_dialog_error(NULL,
153 _("An error has occurred!\n\
154 Maybe your permissions are insufficient or the filename is wrong")
155 , NULL);
156
157 g_object_unref(parent) ;
158 g_free(newFilename) ;
159 g_free(oldFilename) ;
160 g_free(oldPath) ;
161 g_free(fileBasename) ;
162 }
163
164 static void
file_view_rename_edit_start(GtkCellRenderer * cell,GtkCellEditable * editable,const gchar * path,gpointer data)165 file_view_rename_edit_start (GtkCellRenderer *cell,
166 GtkCellEditable *editable,
167 const gchar *path,
168 gpointer data)
169 {
170 if (GTK_IS_ENTRY (editable)) {
171 GtkEntry *entry = GTK_ENTRY (editable);
172
173 AnjutaFileView* view = (AnjutaFileView*) data ;
174 GFile* file = file_view_get_selected (view);
175
176 gchar * fileBasename ;
177 fileBasename = g_file_get_basename(file) ;
178 gtk_entry_set_text(entry, fileBasename);
179 g_free(fileBasename) ;
180 }
181 }
182
file_view_rename(AnjutaFileView * view)183 void file_view_rename (AnjutaFileView* view)
184 {
185
186 GtkTreeSelection * selection = gtk_tree_view_get_selection(
187 GTK_TREE_VIEW(view));
188 GtkTreeIter iter ;
189 GtkTreeModel * model = NULL ;
190 gtk_tree_selection_get_selected(selection, &model, &iter);
191
192 GtkTreePath * path = gtk_tree_model_get_path(model, &iter);
193 GtkTreeViewColumn * column = gtk_tree_view_get_column(GTK_TREE_VIEW(view),
194 0);
195
196 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(view)
197 , path, column,
198 NULL, TRUE);
199 gtk_tree_path_free(path) ;
200 }
201
file_view_can_rename(AnjutaFileView * view)202 gboolean file_view_can_rename (AnjutaFileView* view)
203 {
204 /* TODO
205 * Maybe it can be use for windows (look if file is already open)
206 */
207 return FALSE;
208 }
209
210 GFile*
file_view_get_selected(AnjutaFileView * view)211 file_view_get_selected (AnjutaFileView* view)
212 {
213 AnjutaFileViewPrivate* priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
214 GtkTreeSelection* selection =
215 gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
216 GtkTreeIter selected;
217 if (gtk_tree_selection_get_selected (selection, NULL, &selected))
218 {
219 GFile* file;
220 GtkTreeIter real_iter;
221 GtkTreeModel* sort_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
222
223 gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT(sort_model),
224 &real_iter, &selected);
225
226 file = file_model_get_file (priv->model, &real_iter);
227 return file;
228 }
229 else
230 return NULL;
231 }
232
233 static void
file_view_select_iter(AnjutaFileView * view,GtkTreeIter iter)234 file_view_select_iter (AnjutaFileView* view, GtkTreeIter iter)
235 {
236 GtkTreeModelSort* model_sort;
237 GtkTreeIter sort_iter;
238 GtkTreeSelection* selection;
239 GtkTreePath* path;
240
241 model_sort = GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
242
243 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
244 gtk_tree_model_sort_convert_child_iter_to_iter (model_sort, &sort_iter, &iter);
245 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model_sort), &sort_iter);
246
247 gtk_tree_selection_select_iter (selection, &sort_iter);
248 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), path, NULL, TRUE, 0.5, 0.0);
249 }
250
251 static void
file_view_select_from_iter(AnjutaFileView * view,GtkTreeIter iter)252 file_view_select_from_iter (AnjutaFileView* view, GtkTreeIter iter)
253 {
254 AnjutaFileViewPrivate* priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
255
256 GtkTreeModelSort* model_sort;
257 gboolean valid;
258 GtkTreeIter sort_iter;
259 GFile* file;
260
261 model_sort = GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
262
263 do
264 {
265 gboolean is_dummy, is_dir;
266
267 gtk_tree_model_get (GTK_TREE_MODEL (priv->model), &iter,
268 COLUMN_FILE, &file, COLUMN_DUMMY, &is_dummy,
269 COLUMN_IS_DIR, &is_dir, -1);
270
271 if (is_dummy)
272 break;
273
274 if (g_file_equal (priv->pending_selected_file, file))
275 {
276 file_view_select_iter (view, iter);
277 break;
278 }
279
280 else if (g_file_has_prefix (priv->pending_selected_file, file))
281 {
282 if (is_dir)
283 {
284 GtkTreePath *path = NULL;
285
286 gtk_tree_model_sort_convert_child_iter_to_iter (model_sort, &sort_iter, &iter);
287 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model_sort), &sort_iter);
288
289 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), path))
290 {
291 GtkTreeIter parent = iter;
292 valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (priv->model), &iter, &parent);
293 gtk_tree_path_free (path);
294 }
295 else
296 {
297 gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, FALSE);
298 gtk_tree_path_free (path);
299 break;
300 }
301 }
302 else
303 {
304 file_view_select_iter (view, iter);
305 break;
306 }
307 }
308 else
309 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->model), &iter);
310
311 g_clear_object (&file);
312
313 } while (valid);
314
315 if (file)
316 g_object_unref (file);
317 }
318
319 static void
file_view_directory_expanded(FileModel * model,GtkTreeIter * iter,GtkTreePath * path,gpointer user_data)320 file_view_directory_expanded (FileModel* model, GtkTreeIter* iter, GtkTreePath* path,
321 gpointer user_data)
322 {
323 AnjutaFileView* view = user_data;
324 AnjutaFileViewPrivate *priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
325
326 if (priv->pending_selected_file)
327 {
328 GFile *dir;
329
330 gtk_tree_model_get (GTK_TREE_MODEL (priv->model), iter,
331 COLUMN_FILE, &dir, -1);
332
333 if (g_file_has_prefix (priv->pending_selected_file, dir))
334 file_view_select_from_iter (view, *iter);
335
336 g_object_unref (dir);
337 }
338 }
339
340 void
file_view_set_selected(AnjutaFileView * view,GFile * selected)341 file_view_set_selected (AnjutaFileView* view, GFile *selected)
342 {
343 AnjutaFileViewPrivate *priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
344
345 GtkTreeIter iter;
346 gboolean valid;
347
348 g_clear_object (&priv->pending_selected_file);
349 priv->pending_selected_file = g_object_ref (selected);
350
351 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->model), &iter);
352 if (valid)
353 file_view_select_from_iter (view, iter);
354 }
355
356 static void
file_view_drag_data_get(GtkWidget * widget,GdkDragContext * drag_context,GtkSelectionData * data,guint info,guint time)357 file_view_drag_data_get (GtkWidget* widget,
358 GdkDragContext *drag_context,
359 GtkSelectionData *data,
360 guint info,
361 guint time)
362 {
363 AnjutaFileView* view = ANJUTA_FILE_VIEW(widget);
364 GFile* selected = file_view_get_selected (view);
365
366 if (selected)
367 {
368 gchar* uris[2];
369 uris[0] = g_file_get_uri (selected);
370 uris[1] = NULL;
371 gtk_selection_data_set_uris (data, uris);
372 g_free(uris[0]);
373 }
374 }
375
376 static void
file_view_row_activated(GtkTreeView * widget,GtkTreePath * sort_path,GtkTreeViewColumn * column)377 file_view_row_activated (GtkTreeView* widget, GtkTreePath* sort_path,
378 GtkTreeViewColumn* column)
379 {
380 AnjutaFileView* view = ANJUTA_FILE_VIEW (widget);
381 AnjutaFileViewPrivate* priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
382 GtkTreeIter selected;
383 GtkTreePath* path = NULL;
384 gboolean is_dir;
385 GFile* file;
386
387 GtkTreeIter select_iter;
388 GtkTreeModel* sort_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
389 gtk_tree_model_get_iter (sort_model, &selected, sort_path);
390 gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT(sort_model),
391 &select_iter, &selected);
392 gtk_tree_model_get (GTK_TREE_MODEL(priv->model), &select_iter,
393 COLUMN_IS_DIR, &is_dir,
394 -1);
395 file = file_model_get_file (priv->model, &select_iter);
396
397 path = gtk_tree_model_get_path(sort_model, &selected);
398
399 if (is_dir)
400 {
401 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (view),
402 path))
403 {
404 gtk_tree_view_expand_row (GTK_TREE_VIEW (view),
405 path,
406 FALSE);
407 }
408 else
409 {
410 gtk_tree_view_collapse_row (GTK_TREE_VIEW (view),
411 path);
412 }
413 }
414 else
415 {
416 g_signal_emit_by_name (G_OBJECT (view),
417 "file-open",
418 file);
419 }
420 if (file)
421 g_object_unref (file);
422 if (path)
423 gtk_tree_path_free(path);
424 }
425
426 static gboolean
file_view_key_press_event(GtkWidget * widget,GdkEventKey * event)427 file_view_key_press_event (GtkWidget* widget, GdkEventKey* event)
428 {
429 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter)
430 {
431 AnjutaFileView* view = ANJUTA_FILE_VIEW (widget);
432 AnjutaFileViewPrivate* priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
433 GtkTreeIter selected;
434
435 GtkTreeSelection* selection =
436 gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
437
438 if (gtk_tree_selection_get_selected (selection, NULL, &selected))
439 {
440 GFile* file;
441 GtkTreeIter select_iter;
442 GtkTreeModel* sort_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
443 gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT(sort_model),
444 &select_iter, &selected);
445 file = file_model_get_file (priv->model, &select_iter);
446 if (file != NULL)
447 {
448 g_signal_emit_by_name (G_OBJECT (view),
449 "file-open",
450 file);
451 g_object_unref (file);
452 }
453 }
454 }
455 return
456 GTK_WIDGET_CLASS (file_view_parent_class)->key_press_event (widget,
457 event);
458 }
459
460 static void
file_view_do_popup_menu(GtkWidget * widget,GdkEventButton * event)461 file_view_do_popup_menu (GtkWidget* widget, GdkEventButton* event)
462 {
463 AnjutaFileView* view = ANJUTA_FILE_VIEW (widget);
464 AnjutaFileViewPrivate* priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
465 GtkTreeSelection* selection =
466 gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
467 GFile* file = NULL;
468 gboolean is_dir = FALSE;
469 GtkTreeIter selected;
470 gint button, event_time;
471
472 if (gtk_tree_selection_get_selected (selection, NULL, &selected))
473 {
474 GtkTreeIter select_iter;
475 GtkTreeModel* sort_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
476 gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT(sort_model),
477 &select_iter, &selected);
478 gtk_tree_model_get (GTK_TREE_MODEL(priv->model), &select_iter,
479 COLUMN_IS_DIR, &is_dir,
480 -1);
481 file = file_model_get_file (priv->model, &select_iter);
482
483 }
484 if (event)
485 {
486 button = event->button;
487 event_time = event->time;
488 }
489 else
490 {
491 button = 0;
492 event_time = gtk_get_current_event_time ();
493 }
494
495 g_signal_emit_by_name (G_OBJECT(widget), "show-popup-menu",
496 file, is_dir, button, event_time);
497 if (file)
498 g_object_unref (file);
499 }
500
501 static gboolean
file_view_button_press_event(GtkWidget * widget,GdkEventButton * event)502 file_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
503 {
504 gint retval =
505 GTK_WIDGET_CLASS (file_view_parent_class)->button_press_event (widget,
506 event);
507 /* Ignore double-clicks and triple-clicks */
508 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
509 {
510 GtkTreePath *path;
511
512 /* Select file under cursor if not selected */
513 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
514 event->x,event->y, &path, NULL, NULL, NULL))
515 {
516 GtkTreeSelection *selection;
517
518 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
519 if (!gtk_tree_selection_path_is_selected(selection, path))
520 {
521 gtk_tree_selection_unselect_all(selection);
522 gtk_tree_selection_select_path(selection, path);
523 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget),
524 path, NULL, FALSE);
525 }
526 gtk_tree_path_free (path);
527
528 file_view_do_popup_menu (widget, event);
529
530 return TRUE;
531 }
532 }
533
534 return retval;
535
536 }
537
538 static gboolean
file_view_popup_menu(GtkWidget * widget)539 file_view_popup_menu (GtkWidget* widget)
540 {
541 file_view_do_popup_menu(widget, NULL);
542 return TRUE;
543 }
544
545 static void
file_view_show_extended_data(AnjutaFileView * view,GtkTreeIter * iter)546 file_view_show_extended_data (AnjutaFileView* view, GtkTreeIter* iter)
547 {
548 AnjutaFileViewPrivate* priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
549
550 GtkTreeModel* file_model = GTK_TREE_MODEL (priv->model);
551 gboolean is_dummy, is_dir;
552 GFile* file;
553 GFileInfo* file_info;
554 time_t time;
555 gchar* display;
556 gchar time_str[128];
557 AnjutaVcsStatus status;
558
559 gtk_tree_model_get (file_model, iter, COLUMN_DUMMY, &is_dummy,
560 COLUMN_IS_DIR, &is_dir, -1);
561 if (is_dummy || is_dir)
562 return;
563
564
565 gtk_tree_model_get (file_model, iter, COLUMN_FILE, &file,
566 COLUMN_STATUS, &status, -1);
567
568 file_info = g_file_query_info (file,
569 "standard::*,time::changed",
570 G_FILE_QUERY_INFO_NONE,
571 NULL, NULL);
572 g_object_unref (file);
573 if (!file_info)
574 return;
575
576 time = g_file_info_get_attribute_uint64(file_info, "time::changed");
577 strftime(time_str, 127, "%x %X", localtime(&time));
578 if (get_status_string(status))
579 {
580 display = g_markup_printf_escaped("%s\n"
581 "<small><tt>%s</tt></small>\n"
582 "<small>%s</small>",
583 g_file_info_get_display_name(file_info),
584 time_str,
585 get_status_string(status));
586 }
587 else
588 {
589 display = g_markup_printf_escaped("%s\n"
590 "<small><tt>%s</tt></small>",
591 g_file_info_get_display_name(file_info),
592 time_str);
593 }
594 gtk_tree_store_set (GTK_TREE_STORE(file_model), iter,
595 COLUMN_DISPLAY, display,
596 -1);
597
598 g_object_unref (file_info);
599 g_free(display);
600 }
601
602 static void
file_view_selection_changed(GtkTreeSelection * selection,AnjutaFileView * view)603 file_view_selection_changed (GtkTreeSelection* selection, AnjutaFileView* view)
604 {
605 AnjutaFileViewPrivate* priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
606 GtkTreeModel* file_model = GTK_TREE_MODEL(priv->model);
607 GtkTreeIter selected;
608 GtkTreeModel* model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
609
610 if (priv->current_selection)
611 {
612 GtkTreeIter iter;
613 GtkTreePath* path = gtk_tree_row_reference_get_path (priv->current_selection);
614 if (path && gtk_tree_model_get_iter (file_model, &iter, path))
615 {
616 gchar* filename;
617 gtk_tree_model_get (file_model, &iter, COLUMN_FILENAME, &filename, -1);
618 gtk_tree_store_set (GTK_TREE_STORE (file_model), &iter,
619 COLUMN_DISPLAY, filename, -1);
620 g_free(filename);
621 gtk_tree_path_free(path);
622 }
623 gtk_tree_row_reference_free(priv->current_selection);
624 priv->current_selection = NULL;
625 }
626
627 if (gtk_tree_selection_get_selected (selection, &model, &selected))
628 {
629 GtkTreeIter real_selection;
630 GtkTreePath* path;
631 GFile* file;
632 gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT(model),
633 &real_selection, &selected);
634
635 path = gtk_tree_model_get_path(file_model, &real_selection);
636 priv->current_selection = gtk_tree_row_reference_new (file_model, path);
637 gtk_tree_path_free(path);
638
639 file_view_show_extended_data (view, &real_selection);
640
641 file = file_model_get_file(FILE_MODEL(file_model), &real_selection);
642 g_signal_emit_by_name (G_OBJECT (view), "current-file-changed",
643 file, NULL);
644 g_object_unref (file);
645 }
646 else
647 {
648 g_signal_emit_by_name (G_OBJECT (view), "current-file-changed",
649 NULL, NULL);
650 }
651
652 /* The pending selection is now either done or if the user changed the
653 * selection before it was finished we cancel it. */
654 g_clear_object (&priv->pending_selected_file);
655
656 DEBUG_PRINT ("%s", "selection_changed");
657 }
658
659 static gboolean
file_view_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard,GtkTooltip * tooltip)660 file_view_query_tooltip (GtkWidget* widget, gint x, gint y, gboolean keyboard,
661 GtkTooltip* tooltip)
662 {
663 AnjutaFileView* view = ANJUTA_FILE_VIEW (widget);
664 AnjutaFileViewPrivate* priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
665 GtkTreeModel* model_sort;
666 GtkTreeModel* file_model = GTK_TREE_MODEL (priv->model);
667 GtkTreePath* path;
668 GtkTreeIter iter;
669 GtkTreeIter real_iter;
670 gchar* filename;
671 gboolean result = TRUE;
672 GdkRectangle visible_rect, column_rect;
673 GdkScreen *screen = gdk_screen_get_default ();
674 PangoContext *context;
675 PangoLayout *layout;
676 gint width, height;
677
678 if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (view),
679 &x, &y, keyboard,
680 &model_sort,
681 &path,
682 &iter))
683 return FALSE;
684
685 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model_sort),
686 &real_iter, &iter);
687
688 filename = file_model_get_filename (FILE_MODEL (file_model), &real_iter);
689
690 context = gdk_pango_context_get_for_screen (screen);
691 layout = pango_layout_new (context);
692
693 pango_layout_set_text (layout, filename, -1);
694 pango_layout_get_pixel_size (layout, &width, &height);
695
696 gtk_tree_view_get_visible_rect (GTK_TREE_VIEW (view), &visible_rect);
697 gtk_tree_view_get_cell_area (GTK_TREE_VIEW (view), path,
698 gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0), &column_rect);
699
700 if (column_rect.x + width > visible_rect.x + visible_rect.width ||
701 column_rect.x < visible_rect.x)
702 {
703 gtk_tooltip_set_text (tooltip, filename);
704 gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (view),
705 tooltip,
706 path);
707 } else
708 {
709 result = FALSE;
710 }
711
712 g_free (filename);
713 gtk_tree_path_free (path);
714 g_object_unref (layout);
715 g_object_unref (context);
716
717 return result;
718 }
719
720 static int
file_view_sort_model(GtkTreeModel * model,GtkTreeIter * iter1,GtkTreeIter * iter2,gpointer null)721 file_view_sort_model(GtkTreeModel* model,
722 GtkTreeIter* iter1,
723 GtkTreeIter* iter2,
724 gpointer null)
725 {
726 gint sort1, sort2;
727 gchar *filename1 = NULL, *filename2 = NULL;
728 gboolean is_dir1, is_dir2;
729 gint retval = 0;
730
731 gtk_tree_model_get (model, iter1,
732 COLUMN_FILENAME, &filename1,
733 COLUMN_SORT, &sort1,
734 COLUMN_IS_DIR, &is_dir1, -1);
735 gtk_tree_model_get (model, iter2,
736 COLUMN_FILENAME, &filename2,
737 COLUMN_SORT, &sort2,
738 COLUMN_IS_DIR, &is_dir2, -1);
739
740 if (sort1 != sort2)
741 {
742 retval = sort2 - sort1;
743 }
744 else if (is_dir1 != is_dir2)
745 {
746 retval = is_dir1 ? -1 : 1;
747 }
748 else if (filename1 && filename2)
749 {
750 retval = strcasecmp(filename1, filename2);
751 }
752 g_free(filename1);
753 g_free(filename2);
754
755 return retval;
756 }
757
758 #define EMBLEM_ADDED "vcs-added.png"
759 #define EMBLEM_CONFLICT "vcs-conflict.png"
760 #define EMBLEM_DELETED "vcs-deleted.png"
761 #define EMBLEM_IGNORED "vcs-ignored.png"
762 #define EMBLEM_LOCKED "vcs-locked.png"
763 #define EMBLEM_UNVERSIONED "vcs-unversioned.png"
764 #define EMBLEM_UPTODATE "vcs-updated.png"
765 #define EMBLEM_MODIFIED "vcs-modified.png"
766
767 #define COMPOSITE_ALPHA 225
768
769 static GdkPixbuf*
get_vcs_emblem(AnjutaVcsStatus status)770 get_vcs_emblem (AnjutaVcsStatus status)
771 {
772 GdkPixbuf* emblem ;
773 switch (status)
774 {
775 case ANJUTA_VCS_STATUS_ADDED:
776 emblem = gdk_pixbuf_new_from_file (PACKAGE_PIXMAPS_DIR"/"EMBLEM_ADDED, NULL);
777 break;
778 case ANJUTA_VCS_STATUS_MODIFIED:
779 emblem = gdk_pixbuf_new_from_file (PACKAGE_PIXMAPS_DIR"/"EMBLEM_MODIFIED, NULL);
780 break;
781 case ANJUTA_VCS_STATUS_DELETED:
782 emblem = gdk_pixbuf_new_from_file (PACKAGE_PIXMAPS_DIR"/"EMBLEM_DELETED, NULL);
783 break;
784 case ANJUTA_VCS_STATUS_CONFLICTED:
785 emblem = gdk_pixbuf_new_from_file (PACKAGE_PIXMAPS_DIR"/"EMBLEM_CONFLICT, NULL);
786 break;
787 case ANJUTA_VCS_STATUS_LOCKED:
788 emblem = gdk_pixbuf_new_from_file (PACKAGE_PIXMAPS_DIR"/"EMBLEM_LOCKED, NULL);
789 break;
790 case ANJUTA_VCS_STATUS_UNVERSIONED:
791 emblem = gdk_pixbuf_new_from_file (PACKAGE_PIXMAPS_DIR"/"EMBLEM_UNVERSIONED, NULL);
792 break;
793 case ANJUTA_VCS_STATUS_UPTODATE:
794 emblem = gdk_pixbuf_new_from_file (PACKAGE_PIXMAPS_DIR"/"EMBLEM_UPTODATE, NULL);
795 break;
796 case ANJUTA_VCS_STATUS_IGNORED:
797 emblem = gdk_pixbuf_new_from_file (PACKAGE_PIXMAPS_DIR"/"EMBLEM_IGNORED, NULL);
798 break;
799 default:
800 emblem = NULL;
801 }
802 return emblem;
803 }
804
805 static void
file_view_render_pixbuf_with_emblem(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,gpointer data)806 file_view_render_pixbuf_with_emblem (GtkTreeViewColumn* tree_column,
807 GtkCellRenderer* cell,
808 GtkTreeModel* tree_model,
809 GtkTreeIter* iter,
810 gpointer data)
811 {
812 GdkPixbuf* file_icon = NULL;
813 GdkPixbuf* emblem = NULL;
814
815 AnjutaVcsStatus status;
816
817 gtk_tree_model_get (tree_model, iter,
818 COLUMN_STATUS, &status,
819 COLUMN_PIXBUF, &file_icon, -1);
820
821 if (file_icon)
822 {
823 emblem = get_vcs_emblem (status);
824 if (emblem)
825 {
826 GdkPixbuf *new_icon;
827
828 new_icon = gdk_pixbuf_copy (file_icon);
829 gdk_pixbuf_composite (emblem,
830 new_icon,
831 0, 0,
832 gdk_pixbuf_get_width (file_icon),
833 gdk_pixbuf_get_height (file_icon),
834 0, 0,
835 1, 1,
836 GDK_INTERP_BILINEAR,
837 COMPOSITE_ALPHA);
838
839 g_object_set (cell, "pixbuf", new_icon, NULL);
840
841 g_object_unref (new_icon);
842 g_object_unref (emblem);
843 }
844 else
845 {
846 g_object_set (cell, "pixbuf", file_icon, NULL);
847 }
848 g_object_unref (file_icon);
849 }
850 }
851
852 static void
file_view_init(AnjutaFileView * object)853 file_view_init (AnjutaFileView *object)
854 {
855 GtkCellRenderer* renderer_pixbuf;
856 GtkCellRenderer* renderer_display;
857 GtkTreeViewColumn* column;
858 GtkTreeSelection* selection;
859 GtkTreeModel* sort_model;
860
861 AnjutaFileViewPrivate* priv = ANJUTA_FILE_VIEW_GET_PRIVATE (object);
862
863 priv->current_selection = NULL;
864
865 priv->model = file_model_new (GTK_TREE_VIEW(object), NULL);
866 g_signal_connect_object (priv->model, "directory-expanded",
867 G_CALLBACK (file_view_directory_expanded), object, 0);
868
869 sort_model = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(priv->model));
870
871 gtk_tree_view_set_model (GTK_TREE_VIEW(object), sort_model);
872 /* set_model takes a reference so we can release the reference we got when
873 * the sort_model was created. */
874 g_object_unref (sort_model);
875 gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE(sort_model),
876 file_view_sort_model,
877 NULL,
878 NULL);
879
880 renderer_pixbuf = gtk_cell_renderer_pixbuf_new ();
881 renderer_display = gtk_cell_renderer_text_new ();
882
883 g_object_set(renderer_display,
884 "mode", GTK_CELL_RENDERER_MODE_EDITABLE,
885 "editable", TRUE, NULL);
886 g_object_connect(renderer_display, "signal::edited",
887 file_view_rename_edit_finish, object, NULL);
888 g_object_connect(renderer_display, "signal::editing-started",
889 file_view_rename_edit_start, object, NULL);
890
891 column = gtk_tree_view_column_new ();
892 gtk_tree_view_column_set_title (column, _("Filename"));
893 gtk_tree_view_column_pack_start (column, renderer_pixbuf, FALSE);
894 gtk_tree_view_column_pack_start (column, renderer_display, FALSE);
895 gtk_tree_view_column_set_cell_data_func(column,
896 renderer_pixbuf,
897 file_view_render_pixbuf_with_emblem,
898 object, NULL);
899 gtk_tree_view_column_set_attributes (column, renderer_display,
900 "markup", COLUMN_DISPLAY,
901 NULL);
902 gtk_tree_view_append_column (GTK_TREE_VIEW (object), column);
903
904 selection =
905 gtk_tree_view_get_selection (GTK_TREE_VIEW (object));
906 g_signal_connect (selection, "changed",
907 G_CALLBACK (file_view_selection_changed), object);
908
909 /* DND */
910 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW(object),
911 GDK_BUTTON1_MASK,
912 uri_targets,
913 1,
914 GDK_ACTION_MOVE);
915
916 /* Typeahead search */
917 gtk_tree_view_set_search_column (GTK_TREE_VIEW (object), COLUMN_FILENAME);
918 }
919
920 static void
file_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)921 file_view_get_property (GObject *object, guint prop_id, GValue *value,
922 GParamSpec *pspec)
923 {
924 AnjutaFileViewPrivate *priv = ANJUTA_FILE_VIEW_GET_PRIVATE (object);
925
926 switch (prop_id)
927 {
928 case PROP_BASE_PATH:
929 g_object_get_property (G_OBJECT(priv->model), "base-path", value);
930 break;
931 default:
932 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
933 break;
934 }
935 }
936
937 static void
file_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)938 file_view_set_property (GObject *object, guint prop_id, const GValue *value,
939 GParamSpec *pspec)
940 {
941 AnjutaFileViewPrivate *priv = ANJUTA_FILE_VIEW_GET_PRIVATE (object);
942
943 switch (prop_id)
944 {
945 case PROP_BASE_PATH:
946 g_object_set_property (G_OBJECT (priv->model), "base-path", value);
947 break;
948 default:
949 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
950 break;
951 }
952 }
953
954 static void
file_view_finalize(GObject * object)955 file_view_finalize (GObject *object)
956 {
957 AnjutaFileViewPrivate *priv = ANJUTA_FILE_VIEW_GET_PRIVATE (object);
958
959 g_object_unref (priv->model);
960
961 G_OBJECT_CLASS (file_view_parent_class)->finalize (object);
962 }
963
964 static void
file_view_class_init(AnjutaFileViewClass * klass)965 file_view_class_init (AnjutaFileViewClass *klass)
966 {
967 GObjectClass* object_class = G_OBJECT_CLASS (klass);
968 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
969 GtkTreeViewClass* tree_class = GTK_TREE_VIEW_CLASS(klass);
970
971 g_type_class_add_private (klass, sizeof (AnjutaFileViewPrivate));
972
973 object_class->finalize = file_view_finalize;
974 object_class->set_property = file_view_set_property;
975 object_class->get_property = file_view_get_property;
976
977 g_object_class_install_property (object_class,
978 PROP_BASE_PATH,
979 g_param_spec_object ("base-path",
980 _("Base Path"),
981 _("GFile representing the top-most path displayed"),
982 G_TYPE_FILE,
983 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
984 g_signal_new ("file-open",
985 ANJUTA_TYPE_FILE_VIEW,
986 G_SIGNAL_RUN_LAST,
987 G_STRUCT_OFFSET (AnjutaFileViewClass, file_open),
988 NULL, NULL,
989 g_cclosure_marshal_VOID__OBJECT,
990 G_TYPE_NONE,
991 1,
992 G_TYPE_OBJECT,
993 NULL);
994
995 g_signal_new ("current-file-changed",
996 ANJUTA_TYPE_FILE_VIEW,
997 G_SIGNAL_RUN_LAST,
998 G_STRUCT_OFFSET (AnjutaFileViewClass, current_uri_changed),
999 NULL, NULL,
1000 g_cclosure_marshal_VOID__OBJECT,
1001 G_TYPE_NONE,
1002 1,
1003 G_TYPE_OBJECT,
1004 NULL);
1005
1006 g_signal_new ("show-popup-menu",
1007 ANJUTA_TYPE_FILE_VIEW,
1008 G_SIGNAL_RUN_LAST,
1009 G_STRUCT_OFFSET (AnjutaFileViewClass, show_popup_menu),
1010 NULL, NULL,
1011 file_view_cclosure_marshal_VOID__OBJECT_BOOLEAN_INT_INT,
1012 G_TYPE_NONE,
1013 4,
1014 G_TYPE_OBJECT,
1015 G_TYPE_BOOLEAN,
1016 G_TYPE_INT,
1017 G_TYPE_INT,
1018 NULL);
1019
1020 tree_class->row_activated = file_view_row_activated;
1021 widget_class->key_press_event = file_view_key_press_event;
1022 widget_class->popup_menu = file_view_popup_menu;
1023 widget_class->button_press_event = file_view_button_press_event;
1024
1025 /* Tooltips */
1026 widget_class->query_tooltip = file_view_query_tooltip;
1027
1028 /* Dnd */
1029 widget_class->drag_data_get = file_view_drag_data_get;
1030 }
1031
1032 void
file_view_refresh_vcs(AnjutaFileView * view)1033 file_view_refresh_vcs (AnjutaFileView* view)
1034 {
1035 AnjutaFileViewPrivate* priv = ANJUTA_FILE_VIEW_GET_PRIVATE (view);
1036 file_model_update_vcs_status(priv->model);
1037 }
1038
1039
1040 GtkWidget*
file_view_new(void)1041 file_view_new (void)
1042 {
1043 return g_object_new (ANJUTA_TYPE_FILE_VIEW,
1044 "headers-visible", FALSE,
1045 "has-tooltip", TRUE,
1046 "enable-search", TRUE,
1047 NULL);
1048 }
1049