nautilus-history-view.c 9.53 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
 *  Authors: Elliot Lee <sopwith@redhat.com>
24
 *           Darin Adler <darin@bentspoon.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
#include <eel/eel-debug.h>
32
33
#include <eel/eel-gdk-pixbuf-extensions.h>
#include <eel/eel-gtk-macros.h>
34
35
#include <gtk/gtkclist.h>
#include <gtk/gtkscrolledwindow.h>
36
37
#include <libnautilus-private/nautilus-bookmark.h>
#include <libnautilus-private/nautilus-global-preferences.h>
38
39
#include <libnautilus/nautilus-view-standard-main.h>

40
41
42
#define FACTORY_IID	"OAFIID:nautilus_history_view_factory:912d6634-d18f-40b6-bb83-bdfe16f1d15e"
#define VIEW_IID	"OAFIID:nautilus_history_view:a7a85bdd-2ecf-4bc1-be7c-ed328a29aacb"

43
44
45
46
47
#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
48
49

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

57
58
59
typedef struct {
	NautilusViewClass parent;
} NautilusHistoryViewClass;
Elliot Lee's avatar
Elliot Lee committed
60

61
62
63
64
#define HISTORY_VIEW_COLUMN_ICON	0
#define HISTORY_VIEW_COLUMN_NAME	1
#define HISTORY_VIEW_COLUMN_COUNT	2

65
static GtkType nautilus_history_view_get_type         (void);
66
67
static void    nautilus_history_view_class_init (NautilusHistoryViewClass *klass);
static void    nautilus_history_view_init       (NautilusHistoryView      *view);
68
static void    nautilus_history_view_destroy          (GtkObject                *object);
69

70
EEL_CLASS_BOILERPLATE (NautilusHistoryView,
71
72
				   nautilus_history_view,
				   NAUTILUS_TYPE_VIEW)
73

74
static NautilusBookmark *
75
get_bookmark_from_row (GtkCList *list, int row)
76
{
77
	return NAUTILUS_BOOKMARK (gtk_clist_get_row_data (list, row));
78
79
}

80
static char *
81
get_uri_from_row (GtkCList *list, int row)
82
{
83
	return nautilus_bookmark_get_uri (get_bookmark_from_row (list, row));
84
}
85
86

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

93
94
	if (pixbuf != NULL) {
		gdk_pixbuf_render_pixmap_and_mask (pixbuf, &pixmap, &mask, 
Ramiro Estrugo's avatar
Ramiro Estrugo committed
95
						   EEL_STANDARD_ALPHA_THRESHHOLD);
96
	} else {
97
		bookmark = get_bookmark_from_row (list, row);
98
		if (!nautilus_bookmark_get_pixmap_and_mask (bookmark, NAUTILUS_ICON_SIZE_SMALLER,
99
							    &pixmap, &mask)) {
100
101
102
103
			return;
		}
	}
	
104
	gtk_clist_set_pixmap (list, row, HISTORY_VIEW_COLUMN_ICON, pixmap, mask);
105

106
107
108
109
	gdk_pixmap_unref (pixmap);
	if (mask != NULL) {
		gdk_pixmap_unref (mask);
	}
110
111
}

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

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

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
	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;

148
	view->updating_history = TRUE;
149

150
	gtk_clist_freeze (list);
151

152
	gtk_clist_clear (list);
153

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

		/* 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;
		}
169
170
		
		cols[HISTORY_VIEW_COLUMN_ICON] = NULL;
171
		cols[HISTORY_VIEW_COLUMN_NAME] = item->title;
172

173
		new_row = gtk_clist_append (list, cols);
174
		
175
		gtk_clist_set_row_data_full (list, new_row, bookmark,
176
					     (GtkDestroyNotify) gtk_object_unref);
177
178

		pixbuf = bonobo_ui_util_xml_to_pixbuf (item->icon);
179
		install_icon (list, new_row, pixbuf);
180
		if (pixbuf != NULL) {
181
			g_object_unref (G_OBJECT (pixbuf));
182
		}
183
		
184
		gtk_clist_columns_autosize (list);
185
		
186
187
		if (gtk_clist_row_is_visible (list, new_row) != GTK_VISIBILITY_FULL) {
			gtk_clist_moveto (list, new_row, -1, 0.5, 0.0);
188
189
190
		}
	}

191
	gtk_clist_select_row (list, 0, 0);
192
	
193
	gtk_clist_thaw (list);
194
	
195
  	view->updating_history = FALSE;
196
197

	view->external_destroyed_flag = NULL;
198
199
200
}

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

207
208
209
	if (event->button != 1) {
		return;
	}
210

211
	gtk_clist_get_selection_info (list, event->x, event->y, &row, &column);
212

213
	view->press_row = row;
214
215
216
}

static void
217
218
219
button_release_callback (GtkCList *list,
			 GdkEventButton *event,
			 NautilusHistoryView *view)
Elliot Lee's avatar
Elliot Lee committed
220
{
221
	char *uri;
222
	int row, column;
223
	
224
225
	/* FIXME: Is it really a good idea to just ignore button presses when we are updating? */
	if (view->updating_history) {
226
227
		return;
	}
228
	
229
230
231
232
233
234
235
236
	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.
237
238
239
240
241
	 */
	if (row <= 0) {
		return;
	}
	
242
243
244
245
246
 	/* 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) {
247
248
249
		return;
	}
	
250
251
252
253
	/* Navigate to the clicked location. */
	uri = get_uri_from_row (list, row);
	nautilus_view_open_location_in_this_window
		(NAUTILUS_VIEW (view), uri);
254
	g_free (uri);
Elliot Lee's avatar
Elliot Lee committed
255
256
}

257
static void
258
259
260
history_changed_callback (NautilusHistoryView *view,
			  Nautilus_History *list,
			  gpointer callback_data)
261
{
262
	g_assert (view == callback_data);
263

264
	update_history (view, list);
265
}
Elliot Lee's avatar
Elliot Lee committed
266

267
static void
268
nautilus_history_view_class_init (NautilusHistoryViewClass *klass)
Elliot Lee's avatar
Elliot Lee committed
269
{
270
271
272
273
274
275
	GtkObjectClass *object_class;
	
	object_class = GTK_OBJECT_CLASS (klass);
	
	object_class->destroy = nautilus_history_view_destroy;
}
Elliot Lee's avatar
Elliot Lee committed
276

277
static void
278
nautilus_history_view_init (NautilusHistoryView *view)
279
280
281
282
283
284
285
286
287
288
{
	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));
289
	
290
291
292
	window = gtk_scrolled_window_new (gtk_clist_get_hadjustment (list),
					  gtk_clist_get_vadjustment (list));
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (window),
293
294
					GTK_POLICY_AUTOMATIC,
					GTK_POLICY_AUTOMATIC);
295
296
297
298
299
	gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (list));
	gtk_widget_show (window);
	
	nautilus_view_construct (NAUTILUS_VIEW (view), window);

300
	g_object_ref (G_OBJECT (list));
301
302
	view->list = list;

303
	g_signal_connect (list,
304
305
306
			    "button-press-event",
			    button_press_callback,
			    view);
307
	g_signal_connect (list,
308
309
310
311
			    "button-release-event",
			    button_release_callback,
			    view);

312
	g_signal_connect (view,
313
314
315
316
			    "history_changed", 
			    history_changed_callback,
			    view);
}
317

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

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

329
	g_object_unref (G_OBJECT (view->list));
330

Ramiro Estrugo's avatar
Ramiro Estrugo committed
331
	EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
Elliot Lee's avatar
Elliot Lee committed
332
333
}

334
335
int
main (int argc, char *argv[])
Elliot Lee's avatar
Elliot Lee committed
336
{
337
338
339
340
341
342
343
	/* Make criticals and warnings stop in the debugger if NAUTILUS_DEBUG is set.
	 * Unfortunately, this has to be done explicitly for each domain.
	 */
	if (g_getenv ("NAUTILUS_DEBUG") != NULL) {
		eel_make_warnings_and_criticals_stop_in_debugger (G_LOG_DOMAIN, NULL);
	}

344
345
346
347
348
349
350
351
	return nautilus_view_standard_main ("nautilus_history-view",
					    VERSION,
					    PACKAGE,
					    GNOMELOCALEDIR,
					    argc,
					    argv,
					    FACTORY_IID,
					    VIEW_IID,
352
					    nautilus_view_create_from_get_type_function,
353
					    nautilus_global_preferences_init,
354
					    nautilus_history_view_get_type);
Elliot Lee's avatar
Elliot Lee committed
355
}