nautilus-history-view.c 9.34 KB
Newer Older
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
Elliot Lee's avatar
Elliot Lee committed
2
3
4
5

/*
 *  Nautilus
 *
Elliot Lee's avatar
Elliot Lee committed
6
 *  Copyright (C) 1999, 2000 Red Hat, Inc.
7
 *  Copyright (C) 2000, 2001 Eazel, Inc.
Elliot Lee's avatar
Elliot Lee committed
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this library; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
23
24
 *  Authors: Elliot Lee <sopwith@redhat.com>
 *           Darin Adler <darin@eazel.com>
Elliot Lee's avatar
Elliot Lee committed
25
26
 *
 */
27
 
28
#include <config.h>
Elliot Lee's avatar
Elliot Lee committed
29

30
#include <bonobo/bonobo-ui-util.h>
31
32
#include <gtk/gtkclist.h>
#include <gtk/gtkscrolledwindow.h>
33
34
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-i18n.h>
35
#include <libnautilus-extensions/nautilus-bookmark.h>
36
37
38
39
40
41
42
43
44
#include <libnautilus-extensions/nautilus-gdk-pixbuf-extensions.h>
#include <libnautilus-extensions/nautilus-gtk-macros.h>
#include <libnautilus/nautilus-view-standard-main.h>

#define NAUTILUS_TYPE_HISTORY_VIEW            (nautilus_history_view_get_type ())
#define NAUTILUS_HISTORY_VIEW(obj)            (GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_HISTORY_VIEW, NautilusHistoryView))
#define NAUTILUS_HISTORY_VIEW_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_HISTORY_VIEW, NautilusHistoryViewClass))
#define NAUTILUS_IS_HISTORY_VIEW(obj)         (GTK_CHECK_TYPE ((obj), NAUTILUS_TYPE_HISTORY_VIEW))
#define NAUTILUS_IS_HISTORY_VIEW_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_HISTORY_VIEW))
Elliot Lee's avatar
Elliot Lee committed
45
46

typedef struct {
47
48
49
50
	NautilusView parent;
	GtkCList *list;
	gboolean updating_history;
	int press_row;
51
	gboolean *external_destroyed_flag;
52
} NautilusHistoryView;
Elliot Lee's avatar
Elliot Lee committed
53

54
55
56
typedef struct {
	NautilusViewClass parent;
} NautilusHistoryViewClass;
Elliot Lee's avatar
Elliot Lee committed
57

58
59
60
61
#define HISTORY_VIEW_COLUMN_ICON	0
#define HISTORY_VIEW_COLUMN_NAME	1
#define HISTORY_VIEW_COLUMN_COUNT	2

62
63
64
65
static GtkType nautilus_history_view_get_type         (void);
static void    nautilus_history_view_initialize_class (NautilusHistoryViewClass *klass);
static void    nautilus_history_view_initialize       (NautilusHistoryView      *view);
static void    nautilus_history_view_destroy          (GtkObject                *object);
66

67
68
69
NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusHistoryView,
				   nautilus_history_view,
				   NAUTILUS_TYPE_VIEW)
70

71
static NautilusBookmark *
72
get_bookmark_from_row (GtkCList *list, int row)
73
{
74
	return NAUTILUS_BOOKMARK (gtk_clist_get_row_data (list, row));
75
76
}

77
static char *
78
get_uri_from_row (GtkCList *list, int row)
79
{
80
	return nautilus_bookmark_get_uri (get_bookmark_from_row (list, row));
81
}
82
83

static void
84
install_icon (GtkCList *list, int row, GdkPixbuf *pixbuf)
85
86
{
	GdkPixmap *pixmap;
87
	GdkBitmap *mask;
88
	NautilusBookmark *bookmark;
89

90
91
92
93
	if (pixbuf != NULL) {
		gdk_pixbuf_render_pixmap_and_mask (pixbuf, &pixmap, &mask, 
						   NAUTILUS_STANDARD_ALPHA_THRESHHOLD);
	} else {
94
		bookmark = get_bookmark_from_row (list, row);
95
		if (!nautilus_bookmark_get_pixmap_and_mask (bookmark, NAUTILUS_ICON_SIZE_SMALLER,
96
							    &pixmap, &mask)) {
97
98
99
100
			return;
		}
	}
	
101
	gtk_clist_set_pixmap (list, row, HISTORY_VIEW_COLUMN_ICON, pixmap, mask);
102

103
104
105
106
	gdk_pixmap_unref (pixmap);
	if (mask != NULL) {
		gdk_pixmap_unref (mask);
	}
107
108
}

Elliot Lee's avatar
Elliot Lee committed
109
static void
110
111
update_history (NautilusHistoryView *view,
		Nautilus_History *history)
Elliot Lee's avatar
Elliot Lee committed
112
{
113
	char *cols[HISTORY_VIEW_COLUMN_COUNT];
114
115
	int new_row;
	GtkCList *list;
116
117
	NautilusBookmark *bookmark;
	Nautilus_HistoryItem *item;
118
	GdkPixbuf *pixbuf;
119
	guint i;
120
	gboolean destroyed_flag;
121

122
123
	/* FIXME: We'll end up with old history if this happens. */
	if (view->updating_history) {
124
125
		return;
	}
126

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
	list = view->list;

	if (GTK_OBJECT_DESTROYED (list)) {
		return;
	}

	/* Set up a local boolean so we can detect that the view has
	 * been destroyed. We can't ask the view itself because once
	 * it's destroyed it's pointer is a pointer to freed storage.
	 */
	/* FIXME: We can't just keep an extra ref to the view as we
	 * normally would because of a bug in Bonobo that means a
	 * BonoboControl must not outlast its BonoboFrame
	 * (NautilusHistoryView is a BonoboControl).
	 */
	destroyed_flag = FALSE;
	view->external_destroyed_flag = &destroyed_flag;

145
	view->updating_history = TRUE;
146

147
	gtk_clist_freeze (list);
148

149
	gtk_clist_clear (list);
150

151
152
	for (i = 0; i < history->_length; i++) {
		item = &history->_buffer[i];
153
		bookmark = nautilus_bookmark_new (item->location, item->title);
154
155
156
157
158
159
160
161
162
163
164
165

		/* Through a long line of calls, nautilus_bookmark_new
		 * can end up calling through to CORBA, so a remote
		 * unref can come in at this point. In theory, other
		 * calls could result in a similar problem, so in
		 * theory we need this check after any call out, but
		 * in practice, none of the other calls used here have
		 * that problem.
		 */
		if (destroyed_flag) {
			return;
		}
166
167
		
		cols[HISTORY_VIEW_COLUMN_ICON] = NULL;
168
		cols[HISTORY_VIEW_COLUMN_NAME] = item->title;
169

170
		new_row = gtk_clist_append (list, cols);
171
		
172
		gtk_clist_set_row_data_full (list, new_row, bookmark,
173
					     (GtkDestroyNotify) gtk_object_unref);
174
175

		pixbuf = bonobo_ui_util_xml_to_pixbuf (item->icon);
176
		install_icon (list, new_row, pixbuf);
177
178
179
		if (pixbuf != NULL) {
			gdk_pixbuf_unref (pixbuf);
		}
180
		
181
		gtk_clist_columns_autosize (list);
182
		
183
184
		if (gtk_clist_row_is_visible (list, new_row) != GTK_VISIBILITY_FULL) {
			gtk_clist_moveto (list, new_row, -1, 0.5, 0.0);
185
186
187
		}
	}

188
	gtk_clist_select_row (list, 0, 0);
189
	
190
	gtk_clist_thaw (list);
191
	
192
  	view->updating_history = FALSE;
193
194

	view->external_destroyed_flag = NULL;
195
196
197
}

static void
198
199
200
button_press_callback (GtkCList *list,
		       GdkEventButton *event,
		       NautilusHistoryView *view)
201
{
202
	int row, column;
Elliot Lee's avatar
Elliot Lee committed
203

204
205
206
	if (event->button != 1) {
		return;
	}
207

208
	gtk_clist_get_selection_info (list, event->x, event->y, &row, &column);
209

210
	view->press_row = row;
211
212
213
}

static void
214
215
216
button_release_callback (GtkCList *list,
			 GdkEventButton *event,
			 NautilusHistoryView *view)
Elliot Lee's avatar
Elliot Lee committed
217
{
218
	char *uri;
219
	int row, column;
220
	
221
222
	/* FIXME: Is it really a good idea to just ignore button presses when we are updating? */
	if (view->updating_history) {
223
224
		return;
	}
225
	
226
227
228
229
230
231
232
233
	if (event->button != 1) {
		return;
	}
	
	gtk_clist_get_selection_info (list, event->x, event->y, &row, &column);
	
	/* Do nothing if row is zero. A click either in the top list
	 * item or in the history content view is ignored.
234
235
236
237
238
	 */
	if (row <= 0) {
		return;
	}
	
239
240
241
242
243
 	/* Do nothing if the row does not match the row we stashed on
	 * the mouse down. This means that dragging will not cause
	 * navigation.
	 */
	if (row != view->press_row) {
244
245
246
		return;
	}
	
247
248
249
250
	/* Navigate to the clicked location. */
	uri = get_uri_from_row (list, row);
	nautilus_view_open_location_in_this_window
		(NAUTILUS_VIEW (view), uri);
251
	g_free (uri);
Elliot Lee's avatar
Elliot Lee committed
252
253
}

254
static void
255
256
257
history_changed_callback (NautilusHistoryView *view,
			  Nautilus_History *list,
			  gpointer callback_data)
258
{
259
	g_assert (view == callback_data);
260

261
	update_history (view, list);
262
}
Elliot Lee's avatar
Elliot Lee committed
263

264
265
static void
nautilus_history_view_initialize_class (NautilusHistoryViewClass *klass)
Elliot Lee's avatar
Elliot Lee committed
266
{
267
268
269
270
271
272
	GtkObjectClass *object_class;
	
	object_class = GTK_OBJECT_CLASS (klass);
	
	object_class->destroy = nautilus_history_view_destroy;
}
Elliot Lee's avatar
Elliot Lee committed
273

274
275
276
277
278
279
280
281
282
283
284
285
static void
nautilus_history_view_initialize (NautilusHistoryView *view)
{
	GtkCList *list;
	GtkWidget *window;

  	list = GTK_CLIST (gtk_clist_new (HISTORY_VIEW_COLUMN_COUNT));
  	gtk_clist_column_titles_hide (list);
	gtk_clist_set_row_height (list, NAUTILUS_ICON_SIZE_SMALLER);
	gtk_clist_set_selection_mode (list, GTK_SELECTION_BROWSE);
	gtk_clist_columns_autosize (list);
	gtk_widget_show (GTK_WIDGET (list));
286
	
287
288
289
	window = gtk_scrolled_window_new (gtk_clist_get_hadjustment (list),
					  gtk_clist_get_vadjustment (list));
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (window),
290
291
					GTK_POLICY_AUTOMATIC,
					GTK_POLICY_AUTOMATIC);
292
293
294
295
296
	gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (list));
	gtk_widget_show (window);
	
	nautilus_view_construct (NAUTILUS_VIEW (view), window);

297
	gtk_object_ref (GTK_OBJECT (list));
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
	view->list = list;

	gtk_signal_connect (GTK_OBJECT (list),
			    "button-press-event",
			    button_press_callback,
			    view);
	gtk_signal_connect (GTK_OBJECT (list),
			    "button-release-event",
			    button_release_callback,
			    view);

	gtk_signal_connect (GTK_OBJECT (view),
			    "history_changed", 
			    history_changed_callback,
			    view);
}
314

315
316
317
318
319
320
static void
nautilus_history_view_destroy (GtkObject *object)
{
	NautilusHistoryView *view;
	
	view = NAUTILUS_HISTORY_VIEW (object);
Elliot Lee's avatar
Elliot Lee committed
321

322
323
324
325
326
327
	if (view->external_destroyed_flag != NULL) {
		*view->external_destroyed_flag = TRUE;
	}

	gtk_object_unref (GTK_OBJECT (view->list));

328
	NAUTILUS_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
Elliot Lee's avatar
Elliot Lee committed
329
330
}

331
332
int
main (int argc, char *argv[])
Elliot Lee's avatar
Elliot Lee committed
333
{
334
	/* Initialize gettext support */
335
336
337
338
339
#ifdef ENABLE_NLS
	bindtextdomain (PACKAGE, GNOMELOCALEDIR);
	textdomain (PACKAGE);
#endif

340
341
342
343
344
345
	return nautilus_view_standard_main ("nautilus_history-view", VERSION,
					    argc, argv,
					    "OAFIID:nautilus_history_view_factory:912d6634-d18f-40b6-bb83-bdfe16f1d15e",
					    "OAFIID:nautilus_history_view:a7a85bdd-2ecf-4bc1-be7c-ed328a29aacb",
					    nautilus_view_create_from_get_type_function,
					    nautilus_history_view_get_type);
Elliot Lee's avatar
Elliot Lee committed
346
}