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