1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2005-2009 Free Software Foundation, Inc.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program 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.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 #include <string.h>
24 #include <glib/gi18n.h>
25 #include <glib.h>
26 #include <glib/gprintf.h>
27 #include <gtk/gtk.h>
28 #include "gio-utils.h"
29 #include "glib-utils.h"
30 #include "gth-file-source.h"
31 #include "gth-location-chooser.h"
32 #include "gth-location-chooser-dialog.h"
33 #include "gth-main.h"
34 #include "gtk-utils.h"
35 #include "pixbuf-utils.h"
36 
37 
38 #define MIN_WIDTH 200
39 
40 
41 typedef enum {
42 	ITEM_TYPE_NONE,
43 	ITEM_TYPE_SEPARATOR,
44 	ITEM_TYPE_LOCATION,
45 	ITEM_TYPE_ENTRY_POINT,
46 	ITEM_TYPE_CHOOSE_LOCATION
47 } ItemType;
48 
49 enum {
50 	ICON_COLUMN,
51 	NAME_COLUMN,
52 	URI_COLUMN,
53 	TYPE_COLUMN,
54 	ELLIPSIZE_COLUMN,
55 	N_COLUMNS
56 };
57 
58 enum {
59 	PROP_0,
60 	PROP_SHOW_ENTRY_POINTS,
61 	PROP_SHOW_OTHER,
62 	PROP_SHOW_ROOT,
63 	PROP_RELIEF
64 };
65 
66 enum {
67 	CHANGED,
68 	LAST_SIGNAL
69 };
70 
71 
72 struct _GthLocationChooserPrivate {
73 	GtkWidget      *combo;
74 	GtkTreeStore   *model;
75 	GFile          *location;
76 	GthFileSource  *file_source;
77 	gulong          entry_points_changed_id;
78 	guint           update_entry_list_id;
79 	guint           update_location_list_id;
80 	gboolean        show_entry_points;
81 	gboolean        show_root;
82 	gboolean        show_other;
83 	GtkReliefStyle  relief;
84 	gboolean        reload;
85 };
86 
87 
88 static guint gth_location_chooser_signals[LAST_SIGNAL] = { 0 };
89 
90 
G_DEFINE_TYPE_WITH_CODE(GthLocationChooser,gth_location_chooser,GTK_TYPE_BOX,G_ADD_PRIVATE (GthLocationChooser))91 G_DEFINE_TYPE_WITH_CODE (GthLocationChooser,
92 			 gth_location_chooser,
93 			 GTK_TYPE_BOX,
94 			 G_ADD_PRIVATE (GthLocationChooser))
95 
96 
97 static void
98 gth_location_chooser_set_property (GObject      *object,
99 				   guint         property_id,
100 				   const GValue *value,
101 				   GParamSpec   *pspec)
102 {
103 	GthLocationChooser *self;
104 
105 	self = GTH_LOCATION_CHOOSER (object);
106 
107 	switch (property_id) {
108 	case PROP_SHOW_ENTRY_POINTS:
109 		gth_location_chooser_set_show_entry_points (self, g_value_get_boolean (value));
110 		break;
111 	case PROP_SHOW_ROOT:
112 		self->priv->show_root = g_value_get_boolean (value);
113 		break;
114 	case PROP_SHOW_OTHER:
115 		self->priv->show_other = g_value_get_boolean (value);
116 		break;
117 	case PROP_RELIEF:
118 		gth_location_chooser_set_relief (self, g_value_get_enum (value));
119 		break;
120 	default:
121 		break;
122 	}
123 }
124 
125 
126 static void
gth_location_chooser_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)127 gth_location_chooser_get_property (GObject    *object,
128 				   guint       property_id,
129 				   GValue     *value,
130 				   GParamSpec *pspec)
131 {
132 	GthLocationChooser *self;
133 
134 	self = GTH_LOCATION_CHOOSER (object);
135 
136 	switch (property_id) {
137 	case PROP_SHOW_ENTRY_POINTS:
138 		g_value_set_boolean (value, self->priv->show_entry_points);
139 		break;
140 	case PROP_SHOW_ROOT:
141 		g_value_set_boolean (value, self->priv->show_root);
142 		break;
143 	case PROP_SHOW_OTHER:
144 		g_value_set_boolean (value, self->priv->show_other);
145 		break;
146 	case PROP_RELIEF:
147 		g_value_set_enum (value, self->priv->relief);
148 		break;
149 	default:
150 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
151 		break;
152 	}
153 }
154 
155 
156 static void
gth_location_chooser_finalize(GObject * object)157 gth_location_chooser_finalize (GObject *object)
158 {
159 	GthLocationChooser *self;
160 
161 	self = GTH_LOCATION_CHOOSER (object);
162 
163 	if (self->priv->update_location_list_id != 0)
164 		g_source_remove (self->priv->update_location_list_id);
165 	if (self->priv->update_entry_list_id != 0)
166 		g_source_remove (self->priv->update_entry_list_id);
167 	if (self->priv->entry_points_changed_id != 0)
168 		g_signal_handler_disconnect (gth_main_get_default_monitor (),
169 					     self->priv->entry_points_changed_id);
170 	if (self->priv->file_source != NULL)
171 		g_object_unref (self->priv->file_source);
172 	if (self->priv->location != NULL)
173 		g_object_unref (self->priv->location);
174 
175 	G_OBJECT_CLASS (gth_location_chooser_parent_class)->finalize (object);
176 }
177 
178 
179 static gboolean
get_nth_separator_pos(GthLocationChooser * self,int pos,int * idx)180 get_nth_separator_pos (GthLocationChooser *self,
181 		       int                 pos,
182 		       int                *idx)
183 {
184 	GtkTreeIter iter;
185 	gboolean    n_found = 0;
186 
187 	if (idx != NULL)
188 		*idx = 0;
189 
190 	if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->priv->model), &iter))
191 		return FALSE;
192 
193 	do {
194 		int item_type = ITEM_TYPE_NONE;
195 
196 		gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
197 				    &iter,
198 				    TYPE_COLUMN, &item_type,
199 				    -1);
200 		if (item_type == ITEM_TYPE_SEPARATOR) {
201 			n_found++;
202 			if (n_found == pos)
203 				break;
204 		}
205 
206 		if (idx != NULL)
207 			*idx = *idx + 1;
208 	}
209 	while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->priv->model), &iter));
210 
211 	return n_found == pos;
212 }
213 
214 
215 static gboolean
get_iter_from_current_file_entries(GthLocationChooser * self,GFile * file,GtkTreeIter * iter)216 get_iter_from_current_file_entries (GthLocationChooser *self,
217 				    GFile              *file,
218 				    GtkTreeIter        *iter)
219 {
220 	gboolean  found = FALSE;
221 	char     *uri;
222 
223 	if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->priv->model), iter))
224 		return FALSE;
225 
226 	uri = g_file_get_uri (file);
227 	do {
228 		int   item_type = ITEM_TYPE_NONE;
229 		char *list_uri;
230 
231 		gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
232 				    iter,
233 				    TYPE_COLUMN, &item_type,
234 				    URI_COLUMN, &list_uri,
235 				    -1);
236 		if (item_type == ITEM_TYPE_SEPARATOR)
237 			break;
238 		if (_g_str_equal (uri, list_uri)) {
239 			found = TRUE;
240 			g_free (list_uri);
241 			break;
242 		}
243 		g_free (list_uri);
244 	}
245 	while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->priv->model), iter));
246 
247 	g_free (uri);
248 
249 	return found;
250 }
251 
252 
253 static void
combo_changed_cb(GtkComboBox * widget,gpointer user_data)254 combo_changed_cb (GtkComboBox *widget,
255 		  gpointer     user_data)
256 {
257 	GthLocationChooser *self = user_data;
258 	GtkTreeIter         iter;
259 	char               *uri = NULL;
260 	int                 item_type = ITEM_TYPE_NONE;
261 
262 	if (! gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self->priv->combo), &iter))
263 		return;
264 
265 	gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
266 			    &iter,
267 			    TYPE_COLUMN, &item_type,
268 			    URI_COLUMN, &uri,
269 			    -1);
270 
271 	if (item_type == ITEM_TYPE_CHOOSE_LOCATION) {
272 		GtkWidget *dialog;
273 
274 		dialog = gth_location_chooser_dialog_new (_("Location"), _gtk_widget_get_toplevel_if_window (GTK_WIDGET (widget)));
275 		if (self->priv->location != NULL)
276 			gth_location_chooser_dialog_set_folder (GTH_LOCATION_CHOOSER_DIALOG (dialog), self->priv->location);
277 
278 		switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
279 		case GTK_RESPONSE_OK:
280 			gth_location_chooser_set_current (self, gth_location_chooser_dialog_get_folder (GTH_LOCATION_CHOOSER_DIALOG (dialog)));
281 			break;
282 
283 		default:
284 			/* reset the previous value. */
285 			gth_location_chooser_set_current (self, self->priv->location);
286 			break;
287 		}
288 
289 		gtk_widget_destroy (dialog);
290 	}
291 	else if (uri != NULL) {
292 		GFile *file;
293 
294 		file = g_file_new_for_uri (uri);
295 		gth_location_chooser_set_current (self, file);
296 
297 		g_object_unref (file);
298 	}
299 }
300 
301 
302 static void
add_file_source_entries(GthLocationChooser * self,GFile * file,const char * name,GIcon * icon,int position,gboolean update_active_iter,int iter_type)303 add_file_source_entries (GthLocationChooser *self,
304 			 GFile              *file,
305 			 const char         *name,
306 			 GIcon              *icon,
307 			 int                 position,
308 			 gboolean            update_active_iter,
309 			 int                 iter_type)
310 {
311 	GtkTreeIter  iter;
312 	char        *uri;
313 
314 	uri = g_file_get_uri (file);
315 
316 	gtk_tree_store_insert (self->priv->model, &iter, NULL, position);
317 	gtk_tree_store_set (self->priv->model, &iter,
318 			    TYPE_COLUMN, iter_type,
319 			    ICON_COLUMN, icon,
320 			    NAME_COLUMN, name,
321 			    URI_COLUMN, uri,
322 			    ELLIPSIZE_COLUMN, PANGO_ELLIPSIZE_END,
323 			    -1);
324 
325 	g_free (uri);
326 
327 	if (update_active_iter && g_file_equal (self->priv->location, file)) {
328 		g_signal_handlers_block_by_func (self->priv->combo, combo_changed_cb, self);
329 		gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self->priv->combo), &iter);
330 		g_signal_handlers_unblock_by_func (self->priv->combo, combo_changed_cb, self);
331 	}
332 }
333 
334 
335 static void
clear_items_from_separator(GthLocationChooser * self,int nth_separator,gboolean stop_at_next_separator)336 clear_items_from_separator (GthLocationChooser *self,
337 			    int                 nth_separator,
338 			    gboolean            stop_at_next_separator)
339 {
340 	int first_position;
341 	int i;
342 
343 	if (! get_nth_separator_pos (self, nth_separator, &first_position))
344 		return;
345 
346 	for (i = first_position + 1; TRUE; i++) {
347 		GtkTreePath *path;
348 		GtkTreeIter  iter;
349 
350 		path = gtk_tree_path_new_from_indices (first_position + 1, -1);
351 		if (gtk_tree_model_get_iter (GTK_TREE_MODEL (self->priv->model), &iter, path)) {
352 			if (stop_at_next_separator) {
353 				int item_type = ITEM_TYPE_NONE;
354 
355 				gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
356 						    &iter,
357 						    TYPE_COLUMN, &item_type,
358 						    -1);
359 				if (item_type == ITEM_TYPE_SEPARATOR)
360 					break;
361 			}
362 			gtk_tree_store_remove (self->priv->model, &iter);
363 		}
364 		else
365 			break;
366 
367 		gtk_tree_path_free (path);
368 	}
369 }
370 
371 
372 static void
delete_section_by_type(GthLocationChooser * self,ItemType type_to_delete)373 delete_section_by_type (GthLocationChooser *self,
374 			ItemType            type_to_delete)
375 {
376 	GtkTreeIter iter;
377 	gboolean    valid;
378 	gboolean    prev_matched;
379 
380 	if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->priv->model), &iter))
381 		return;
382 
383 	prev_matched = FALSE;
384 	do {
385 		int item_type = ITEM_TYPE_NONE;
386 
387 		gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
388 				    &iter,
389 				    TYPE_COLUMN, &item_type,
390 				    -1);
391 
392 		if (item_type == type_to_delete) {
393 			valid = gtk_tree_store_remove (self->priv->model, &iter);
394 			prev_matched = TRUE;
395 		}
396 		else {
397 			if (prev_matched && (item_type == ITEM_TYPE_SEPARATOR))
398 				valid = gtk_tree_store_remove (self->priv->model, &iter);
399 			else
400 				valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (self->priv->model), &iter);
401 			prev_matched = FALSE;
402 		}
403 	}
404 	while (valid);
405 }
406 
407 
408 static gboolean
get_section_end_by_type(GthLocationChooser * self,ItemType item_to_search,int * p_pos)409 get_section_end_by_type (GthLocationChooser *self,
410 			 ItemType            item_to_search,
411 			 int                *p_pos)
412 {
413 	GtkTreeIter iter;
414 	int         pos;
415 	gboolean    valid;
416 	gboolean    prev_matched;
417 
418 	if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->priv->model), &iter))
419 		return FALSE;
420 
421 	pos = 0;
422 	valid = FALSE;
423 	prev_matched = FALSE;
424 	do {
425 		int item_type = ITEM_TYPE_NONE;
426 
427 		gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
428 				    &iter,
429 				    TYPE_COLUMN, &item_type,
430 				    -1);
431 
432 		if (item_type == item_to_search) {
433 			*p_pos = pos;
434 			valid = TRUE;
435 			prev_matched = TRUE;
436 		}
437 		else {
438 			if ((item_type == ITEM_TYPE_SEPARATOR) && prev_matched)
439 				*p_pos = pos;
440 			prev_matched = FALSE;
441 		}
442 
443 		pos++;
444 	}
445 	while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->priv->model), &iter));
446 
447 	return valid;
448 }
449 
450 
451 static void
update_entry_point_list(GthLocationChooser * self)452 update_entry_point_list (GthLocationChooser *self)
453 {
454 	int    position;
455 	GList *entry_points;
456 	GList *scan;
457 
458 	self->priv->update_entry_list_id = 0;
459 
460 	delete_section_by_type (self, ITEM_TYPE_ENTRY_POINT);
461 
462 	if (get_section_end_by_type (self, ITEM_TYPE_LOCATION, &position))
463 		position = position + 1;
464 	else
465 		position = 0;
466 
467 	entry_points = gth_main_get_all_entry_points ();
468 	for (scan = entry_points; scan; scan = scan->next) {
469 		GthFileData *file_data = scan->data;
470 
471 		add_file_source_entries (self,
472 					 file_data->file,
473 					 g_file_info_get_display_name (file_data->info),
474 					 g_file_info_get_symbolic_icon (file_data->info),
475 					 position++,
476 					 FALSE,
477 					 ITEM_TYPE_ENTRY_POINT);
478 	}
479 
480 	if (self->priv->show_other) {
481 		GtkTreeIter iter;
482 
483 		gtk_tree_store_insert (self->priv->model, &iter, NULL, position);
484 		gtk_tree_store_set (self->priv->model, &iter,
485 				    TYPE_COLUMN, ITEM_TYPE_SEPARATOR,
486 				    -1);
487 	}
488 
489 	_g_object_list_unref (entry_points);
490 }
491 
492 
493 static gboolean
row_separator_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)494 row_separator_func (GtkTreeModel *model,
495 		    GtkTreeIter  *iter,
496 		    gpointer      data)
497 {
498 	int item_type = ITEM_TYPE_NONE;
499 
500 	gtk_tree_model_get (model,
501 			    iter,
502 			    TYPE_COLUMN, &item_type,
503 			    -1);
504 
505 	return (item_type == ITEM_TYPE_SEPARATOR);
506 }
507 
508 
509 static void
entry_points_changed_cb(GthMonitor * monitor,GthLocationChooser * self)510 entry_points_changed_cb (GthMonitor         *monitor,
511 			 GthLocationChooser *self)
512 {
513 	if (! self->priv->show_entry_points)
514 		return;
515 	if (self->priv->update_entry_list_id != 0)
516 		return;
517 	self->priv->update_entry_list_id = call_when_idle ((DataFunc) update_entry_point_list, self);
518 }
519 
520 
521 static void
update_location_list(gpointer user_data)522 update_location_list (gpointer user_data)
523 {
524 	GthLocationChooser *self = user_data;
525 	GtkTreeIter         iter;
526 
527 	self->priv->update_location_list_id = 0;
528 
529 	if (self->priv->location == NULL)
530 		return;
531 
532 	if (! self->priv->reload && get_iter_from_current_file_entries (self, self->priv->location, &iter)) {
533 		g_signal_handlers_block_by_func (self->priv->combo, combo_changed_cb, self);
534 		gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self->priv->combo), &iter);
535 		g_signal_handlers_unblock_by_func (self->priv->combo, combo_changed_cb, self);
536 	}
537 	else {
538 		GList *list;
539 		GList *scan;
540 		int    position = 0;
541 
542 		delete_section_by_type (self, ITEM_TYPE_LOCATION);
543 
544 		list = gth_file_source_get_current_list (self->priv->file_source, self->priv->location);
545 		for (scan = list; scan; scan = scan->next) {
546 			GFile     *file = scan->data;
547 			GFileInfo *info;
548 
549 			info = _g_file_get_info_for_display (file);
550 			if (info == NULL)
551 				continue;
552 
553 			add_file_source_entries (self,
554 						 file,
555 						 g_file_info_get_display_name (info),
556 						 g_file_info_get_symbolic_icon (info),
557 						 position++,
558 						 TRUE,
559 						 ITEM_TYPE_LOCATION);
560 
561 			g_object_unref (info);
562 		}
563 
564 		if (self->priv->show_root) {
565 			GIcon *icon;
566 
567 			icon = g_themed_icon_new ("computer-symbolic");
568 			gtk_tree_store_insert (self->priv->model, &iter, NULL, position++);
569 			gtk_tree_store_set (self->priv->model, &iter,
570 					    TYPE_COLUMN, ITEM_TYPE_LOCATION,
571 					    ICON_COLUMN, icon,
572 					    NAME_COLUMN, _("Locations"),
573 					    URI_COLUMN, "gthumb-vfs:///",
574 					    -1);
575 
576 			_g_object_unref (icon);
577 		}
578 
579 		if (self->priv->show_other || self->priv->show_entry_points) {
580 			GtkTreeIter iter;
581 
582 			gtk_tree_store_insert (self->priv->model, &iter, NULL, position);
583 			gtk_tree_store_set (self->priv->model, &iter,
584 					    TYPE_COLUMN, ITEM_TYPE_SEPARATOR,
585 					    -1);
586 		}
587 
588 		_g_object_list_unref (list);
589 	}
590 
591 	self->priv->reload = FALSE;
592 }
593 
594 
595 static void
current_location_changed(GthLocationChooser * self)596 current_location_changed (GthLocationChooser *self)
597 {
598 	if (self->priv->update_location_list_id != 0)
599 		g_source_remove (self->priv->update_location_list_id);
600 	self->priv->update_location_list_id = call_when_idle ((DataFunc) update_location_list, self);
601 }
602 
603 
604 static void
gth_location_chooser_realize(GtkWidget * widget)605 gth_location_chooser_realize (GtkWidget *widget)
606 {
607 	GthLocationChooser *self = GTH_LOCATION_CHOOSER (widget);
608 
609 	GTK_WIDGET_CLASS (gth_location_chooser_parent_class)->realize (widget);
610 
611 	if (self->priv->show_other) {
612 		GtkTreeIter iter;
613 
614 		gtk_tree_store_append (self->priv->model, &iter, NULL);
615 		gtk_tree_store_set (self->priv->model, &iter,
616 				    TYPE_COLUMN, ITEM_TYPE_CHOOSE_LOCATION,
617 				    NAME_COLUMN, _("Other…"),
618 				    ELLIPSIZE_COLUMN, FALSE,
619 				    -1);
620 	}
621 	entry_points_changed_cb (NULL, self);
622 	current_location_changed (self);
623 }
624 
625 
626 static void
gth_location_chooser_class_init(GthLocationChooserClass * klass)627 gth_location_chooser_class_init (GthLocationChooserClass *klass)
628 {
629 	GObjectClass   *object_class;
630 	GtkWidgetClass *widget_class;
631 
632 	object_class = (GObjectClass*) klass;
633 	object_class->set_property = gth_location_chooser_set_property;
634 	object_class->get_property = gth_location_chooser_get_property;
635 	object_class->finalize = gth_location_chooser_finalize;
636 
637 	widget_class = (GtkWidgetClass *) klass;
638 	widget_class->realize = gth_location_chooser_realize;
639 
640 	/* properties */
641 
642 	g_object_class_install_property (object_class,
643 					 PROP_SHOW_ENTRY_POINTS,
644 					 g_param_spec_boolean ("show-entry-points",
645 							       "Show entry points",
646 							       "Whether to show the entry points in the list",
647 							       TRUE,
648 							       G_PARAM_READWRITE));
649 	g_object_class_install_property (object_class,
650 					 PROP_SHOW_ROOT,
651 					 g_param_spec_boolean ("show-root",
652 							       "Show the VFS root",
653 							       "Whether to show the VFS root in the list",
654 							       FALSE,
655 							       G_PARAM_READWRITE));
656 	g_object_class_install_property (object_class,
657 					 PROP_SHOW_OTHER,
658 					 g_param_spec_boolean ("show-other",
659 							       "Show the Other... entry",
660 							       "Whether to show a special entry to choose a location from a dialog",
661 							       FALSE,
662 							       G_PARAM_READWRITE));
663 	g_object_class_install_property (object_class,
664 					 PROP_RELIEF,
665 					 g_param_spec_enum ("relief",
666 							    "Border relief",
667 							    "The border relief style",
668 							    GTK_TYPE_RELIEF_STYLE,
669 							    GTK_RELIEF_NORMAL,
670 							    G_PARAM_READWRITE));
671 
672 	/* signals */
673 
674 	gth_location_chooser_signals[CHANGED] =
675 		g_signal_new ("changed",
676 			      G_TYPE_FROM_CLASS (klass),
677 			      G_SIGNAL_RUN_LAST,
678 			      G_STRUCT_OFFSET (GthLocationChooserClass, changed),
679 			      NULL, NULL,
680 			      g_cclosure_marshal_VOID__VOID,
681 			      G_TYPE_NONE,
682 			      0);
683 }
684 
685 
686 static void
gth_location_chooser_init(GthLocationChooser * self)687 gth_location_chooser_init (GthLocationChooser *self)
688 {
689 	GtkCellRenderer *renderer;
690 
691 	gtk_widget_set_can_focus (GTK_WIDGET (self), FALSE);
692 	gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_HORIZONTAL);
693 
694 	self->priv = gth_location_chooser_get_instance_private (self);
695 	self->priv->entry_points_changed_id = 0;
696 	self->priv->show_entry_points = TRUE;
697 	self->priv->show_root = FALSE;
698 	self->priv->show_other = TRUE;
699 	self->priv->relief = GTK_RELIEF_NORMAL;
700 	self->priv->reload = FALSE;
701 
702 	self->priv->model = gtk_tree_store_new (N_COLUMNS,
703 						G_TYPE_ICON,
704 						G_TYPE_STRING,
705 						G_TYPE_STRING,
706 						G_TYPE_INT,
707 						PANGO_TYPE_ELLIPSIZE_MODE);
708 	self->priv->combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (self->priv->model));
709 	g_object_unref (self->priv->model);
710 	g_signal_connect (self->priv->combo,
711 			  "changed",
712 			  G_CALLBACK (combo_changed_cb),
713 			  self);
714 	gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (self->priv->combo),
715 					      row_separator_func,
716 					      self,
717 					      NULL);
718 	gtk_widget_set_size_request (self->priv->combo, MIN_WIDTH, -1);
719 
720 	/* icon column */
721 
722 	renderer = gtk_cell_renderer_pixbuf_new ();
723 	g_object_set (renderer,
724 		      "follow-state", TRUE,
725 		      NULL);
726 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self->priv->combo),
727 				    renderer,
728 				    FALSE);
729 	gtk_cell_layout_set_attributes  (GTK_CELL_LAYOUT (self->priv->combo),
730 					 renderer,
731 					 "gicon", ICON_COLUMN,
732 					 NULL);
733 
734 	/* path column */
735 
736 	renderer = gtk_cell_renderer_text_new ();
737 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self->priv->combo),
738 				    renderer,
739 				    TRUE);
740 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (self->priv->combo),
741 					renderer,
742 					"text", NAME_COLUMN,
743 					"ellipsize", ELLIPSIZE_COLUMN,
744 					NULL);
745 
746 	/**/
747 
748 	gtk_widget_show (self->priv->combo);
749 	gtk_box_pack_start (GTK_BOX (self), self->priv->combo, TRUE, TRUE, 0);
750 }
751 
752 
753 GtkWidget *
gth_location_chooser_new(void)754 gth_location_chooser_new (void)
755 {
756 	return GTK_WIDGET (g_object_new (GTH_TYPE_LOCATION_CHOOSER, NULL));
757 }
758 
759 
760 /* -- gth_location_chooser_set_relief -- */
761 
762 
763 void
gth_location_chooser_set_relief(GthLocationChooser * self,GtkReliefStyle value)764 gth_location_chooser_set_relief (GthLocationChooser *self,
765 				 GtkReliefStyle      value)
766 {
767 	if (self->priv->relief == value)
768 		return;
769 
770 	self->priv->relief = value;
771 	g_object_notify (G_OBJECT (self), "relief");
772 }
773 
774 
775 GtkReliefStyle
gth_location_chooser_get_relief(GthLocationChooser * self)776 gth_location_chooser_get_relief (GthLocationChooser *self)
777 {
778 	return self->priv->relief;
779 }
780 
781 
782 void
gth_location_chooser_set_show_entry_points(GthLocationChooser * self,gboolean value)783 gth_location_chooser_set_show_entry_points (GthLocationChooser *self,
784 					    gboolean            value)
785 {
786 	self->priv->show_entry_points = value;
787 
788 	if (self->priv->show_entry_points) {
789 		if (self->priv->entry_points_changed_id == 0)
790 			self->priv->entry_points_changed_id =
791 					g_signal_connect (gth_main_get_default_monitor (),
792 							  "entry-points-changed",
793 							  G_CALLBACK (entry_points_changed_cb),
794 							  self);
795 		entry_points_changed_cb (NULL, self);
796 	}
797 	else {
798 		if (self->priv->entry_points_changed_id != 0) {
799 			g_source_remove (self->priv->entry_points_changed_id);
800 			self->priv->entry_points_changed_id = 0;
801 		}
802 		clear_items_from_separator (self, 1, TRUE);
803 	}
804 
805 	g_object_notify (G_OBJECT (self), "show-entry-points");
806 }
807 
808 
809 gboolean
gth_location_chooser_get_show_entry_points(GthLocationChooser * self)810 gth_location_chooser_get_show_entry_points (GthLocationChooser *self)
811 {
812 	return self->priv->show_entry_points;
813 }
814 
815 
816 void
gth_location_chooser_set_current(GthLocationChooser * self,GFile * file)817 gth_location_chooser_set_current (GthLocationChooser *self,
818 				  GFile              *file)
819 {
820 	if (file == NULL)
821 		return;
822 
823 	if (file != self->priv->location) {
824 		if (self->priv->file_source != NULL)
825 			g_object_unref (self->priv->file_source);
826 		self->priv->file_source = gth_main_get_file_source (file);
827 
828 		if (self->priv->file_source == NULL)
829 			return;
830 
831 		if (self->priv->location != NULL) {
832 			g_object_unref (self->priv->location);
833 			self->priv->location = NULL;
834 		}
835 
836 		if (file != NULL)
837 			self->priv->location = g_file_dup (file);
838 	}
839 
840 	if (gtk_widget_get_realized (GTK_WIDGET (self)))
841 		current_location_changed (self);
842 
843 	g_signal_emit (G_OBJECT (self), gth_location_chooser_signals[CHANGED], 0);
844 }
845 
846 
847 GFile *
gth_location_chooser_get_current(GthLocationChooser * self)848 gth_location_chooser_get_current (GthLocationChooser *self)
849 {
850 	return self->priv->location;
851 }
852 
853 void
gth_location_chooser_reload(GthLocationChooser * self)854 gth_location_chooser_reload (GthLocationChooser *self)
855 {
856 	if (self->priv->location == NULL)
857 		return;
858 
859 	self->priv->reload = TRUE;
860 
861 	if (gtk_widget_get_realized (GTK_WIDGET (self)))
862 		current_location_changed (self);
863 }
864