nautilus-desktop-window.c 11.8 KB
Newer Older
1
2
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
 * Nautilus
 *
 * Copyright (C) 2000 Eazel, Inc.
 *
 * Nautilus 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.
 *
 * Nautilus 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
Cosimo Cecchi's avatar
Cosimo Cecchi committed
19
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20
 *
21
 * Authors: Darin Adler <darin@bentspoon.com>
22
 */
23
24
25

#include <config.h>
#include "nautilus-desktop-window.h"
26
#include "nautilus-window.h"
27
#include "nautilus-application.h"
28

29
30
#include <X11/Xatom.h>
#include <gdk/gdkx.h>
31
#include <gtk/gtk.h>
32
33
34
#include <gio/gio.h>
#include <glib/gi18n.h>

Alexander Larsson's avatar
Alexander Larsson committed
35
#include <eel/eel-vfs-extensions.h>
36
#include <libnautilus-private/nautilus-desktop-link-monitor.h>
37
#include <libnautilus-private/nautilus-file-utilities.h>
Luca Ferretti's avatar
Luca Ferretti committed
38
#include <libnautilus-private/nautilus-icon-names.h>
39
#include <libnautilus-private/nautilus-global-preferences.h>
40

41
struct NautilusDesktopWindowDetails {
Cosimo Cecchi's avatar
Cosimo Cecchi committed
42
	gulong size_changed_id;
43
44

	gboolean loaded;
45
46

	GtkWidget *desktop_selection;
47
48
};

49
G_DEFINE_TYPE (NautilusDesktopWindow, nautilus_desktop_window, 
50
	       NAUTILUS_TYPE_WINDOW);
51

Cosimo Cecchi's avatar
Cosimo Cecchi committed
52
53
54
55
56
57
58
59
60
static void
nautilus_desktop_window_update_directory (NautilusDesktopWindow *window)
{
	GFile *location;

	g_assert (NAUTILUS_IS_DESKTOP_WINDOW (window));

	window->details->loaded = FALSE;
	location = g_file_new_for_uri (EEL_DESKTOP_URI);
61
62
63
        /* We use NAUTILUS_WINDOW_OPEN_FLAG_DONT_MAKE_ACTIVE so the nautilus-window
         * doesn't call gtk_window_present () which raises the window on the stack
         * and actually hides it from the background */
64
        nautilus_application_open_location_full (NAUTILUS_APPLICATION (g_application_get_default ()),
65
66
67
                                                 location,
                                                 NAUTILUS_WINDOW_OPEN_FLAG_DONT_MAKE_ACTIVE,
                                                 NULL, NAUTILUS_WINDOW (window), NULL);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
68
69
70
71
72
	window->details->loaded = TRUE;

	g_object_unref (location);
}

73
static void
74
nautilus_desktop_window_finalize (GObject *obj)
75
{
76
	nautilus_desktop_link_monitor_shutdown ();
Cosimo Cecchi's avatar
Cosimo Cecchi committed
77

78
79
80
81
82
83
	G_OBJECT_CLASS (nautilus_desktop_window_parent_class)->finalize (obj);
}

static void
nautilus_desktop_window_init_actions (NautilusDesktopWindow *window)
{
84
	GAction *action;
85
86

	/* Don't allow close action on desktop */
87
88
89
	action = g_action_map_lookup_action (G_ACTION_MAP (window),
					     "close-current-view");
	g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
90

91
	/* Don't allow new tab on desktop */
92
93
94
	action = g_action_map_lookup_action (G_ACTION_MAP (window),
					     "new-tab");
	g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
95
}
96

97
98
99
100
101
102
103
104
static void
selection_get_cb (GtkWidget          *widget,
		  GtkSelectionData   *selection_data,
		  guint               info,
		  guint               time)
{
	/* No extra targets atm */
}
Cosimo Cecchi's avatar
Cosimo Cecchi committed
105

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
static gboolean
selection_clear_event_cb (GtkWidget	        *widget,
			  GdkEventSelection     *event,
			  NautilusDesktopWindow *window)
{
	gtk_widget_destroy (GTK_WIDGET (window));

	return TRUE;
}

static void
nautilus_desktop_window_init_selection (NautilusDesktopWindow *window)
{
	char selection_name[32];
	GdkAtom selection_atom;
	Window selection_owner;
	GdkDisplay *display;
	GtkWidget *selection_widget;
	GdkScreen *screen;

	screen = gdk_screen_get_default ();

	g_snprintf (selection_name, sizeof (selection_name),
		    "_NET_DESKTOP_MANAGER_S%d", gdk_screen_get_number (screen));
	selection_atom = gdk_atom_intern (selection_name, FALSE);
	display = gdk_screen_get_display (screen);

	selection_owner = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
					      gdk_x11_atom_to_xatom_for_display (display,
										 selection_atom));
	if (selection_owner != None) {
		g_critical ("Another desktop manager in use; desktop window won't be created");
		return;
	}

	selection_widget = gtk_invisible_new_for_screen (screen);
	/* We need this for gdk_x11_get_server_time() */
	gtk_widget_add_events (selection_widget, GDK_PROPERTY_CHANGE_MASK);

	if (!gtk_selection_owner_set_for_display (display,
						  selection_widget,
						  selection_atom,
						  gdk_x11_get_server_time (gtk_widget_get_window (selection_widget)))) {
		gtk_widget_destroy (selection_widget);
		g_critical ("Can't set ourselves as selection owner for desktop manager; "
			    "desktop window won't be created");
		return;
Cosimo Cecchi's avatar
Cosimo Cecchi committed
153
	}
154
155
156
157
158
159
160

	g_signal_connect (selection_widget, "selection-get",
			  G_CALLBACK (selection_get_cb), window);
	g_signal_connect (selection_widget, "selection-clear-event",
			  G_CALLBACK (selection_clear_event_cb), window);

	window->details->desktop_selection = selection_widget;
161
162
}

163
static void
164
nautilus_desktop_window_constructed (GObject *obj)
165
{
166
167
168
169
170
171
172
	AtkObject *accessible;
	NautilusDesktopWindow *window = NAUTILUS_DESKTOP_WINDOW (obj);

	G_OBJECT_CLASS (nautilus_desktop_window_parent_class)->constructed (obj);

	/* Initialize the desktop link monitor singleton */
	nautilus_desktop_link_monitor_get ();
173
174
175
176
177
178

	/* shouldn't really be needed given our semantic type
	 * of _NET_WM_TYPE_DESKTOP, but why not
	 */
	gtk_window_set_resizable (GTK_WINDOW (window),
				  FALSE);
179
180
	gtk_window_set_decorated (GTK_WINDOW (window),
				  FALSE);
181

182
183
	gtk_window_move (GTK_WINDOW (window), 0, 0);

184
185
	g_object_set_data (G_OBJECT (window), "is_desktop_window", 
			   GINT_TO_POINTER (1));
186
187
188
189

	nautilus_desktop_window_init_selection (window);
	nautilus_desktop_window_init_actions (window);

190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
	if (window->details->desktop_selection != NULL) {
		/* Set the accessible name so that it doesn't inherit the cryptic desktop URI. */
		accessible = gtk_widget_get_accessible (GTK_WIDGET (window));

		if (accessible) {
			atk_object_set_name (accessible, _("Desktop"));
		}

		/* Special sawmill setting */
		gtk_window_set_wmclass (GTK_WINDOW (window), "desktop_window", "Nautilus");

		/* Point window at the desktop folder.
		 * Note that nautilus_desktop_window_init is too early to do this.
		 */
		nautilus_desktop_window_update_directory (window);

		/* We realize it immediately so that the NAUTILUS_DESKTOP_WINDOW_ID
		 * property is set so gnome-settings-daemon doesn't try to set
		 * background. And we do a gdk_flush() to be sure X gets it.
		 */
		gtk_widget_realize (GTK_WIDGET (window));
		gdk_flush ();
212
213
214
215
216
217
	}
}

static void
nautilus_desktop_window_init (NautilusDesktopWindow *window)
{
218
219
220
	const gchar *css;
	GtkCssProvider *provider;

221
222
	window->details = G_TYPE_INSTANCE_GET_PRIVATE (window, NAUTILUS_TYPE_DESKTOP_WINDOW,
						       NautilusDesktopWindowDetails);
223
224
225
226
227
228
229
230
231
232
233
234
235
236

	css = "nautilus-desktop-window,"
	      "nautilus-desktop-window notebook {"
	      "    background: transparent;"
	      "}";

	provider = gtk_css_provider_new ();
	gtk_css_provider_load_from_data (provider, css, -1, NULL);

	gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
	                                           GTK_STYLE_PROVIDER (provider),
	                                           GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);

	g_object_unref (provider);
237
238
}

Anders Carlsson's avatar
Anders Carlsson committed
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
static void
nautilus_desktop_window_screen_size_changed (GdkScreen             *screen,
					     NautilusDesktopWindow *window)
{
	int width_request, height_request;

	width_request = gdk_screen_get_width (screen);
	height_request = gdk_screen_get_height (screen);
	
	g_object_set (window,
		      "width_request", width_request,
		      "height_request", height_request,
		      NULL);
}

254
255
static NautilusDesktopWindow *
nautilus_desktop_window_new (void)
256
{
257
258
	GdkScreen *screen;
	GApplication *application;
259
	NautilusDesktopWindow *window;
260
261
	int width_request, height_request;

262
263
	application = g_application_get_default ();
	screen = gdk_screen_get_default ();
264
265
	width_request = gdk_screen_get_width (screen);
	height_request = gdk_screen_get_height (screen);
266

267
	window = g_object_new (NAUTILUS_TYPE_DESKTOP_WINDOW,
268
			       "application", application,
269
270
271
272
			       "disable-chrome", TRUE,
			       "width_request", width_request,
			       "height_request", height_request,
			       NULL);
273

274
275
	return window;
}
276

277
static NautilusDesktopWindow *the_desktop_window = NULL;
278

279
280
281
282
void
nautilus_desktop_window_ensure (void)
{
	NautilusDesktopWindow *window;
283

284
285
286
287
288
289
290
291
292
293
294
	if (!the_desktop_window) {
		window = nautilus_desktop_window_new ();
		g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &the_desktop_window);
		the_desktop_window = window;
	}
}

GtkWidget *
nautilus_desktop_window_get (void)
{
	return GTK_WIDGET (the_desktop_window);
295
}
Darin Adler's avatar
Darin Adler committed
296

297
298
299
300
301
302
303
304
static gboolean
nautilus_desktop_window_delete_event (GtkWidget *widget,
				      GdkEventAny *event)
{
	/* Returning true tells GTK+ not to delete the window. */
	return TRUE;
}

305
306
307
308
static void
map (GtkWidget *widget)
{
	/* Chain up to realize our children */
309
	GTK_WIDGET_CLASS (nautilus_desktop_window_parent_class)->map (widget);
310
	gdk_window_lower (gtk_widget_get_window (widget));
311
}
312

313
314
315
316
static void
unrealize (GtkWidget *widget)
{
	NautilusDesktopWindow *window;
Cosimo Cecchi's avatar
Cosimo Cecchi committed
317
	NautilusDesktopWindowDetails *details;
318
319

	window = NAUTILUS_DESKTOP_WINDOW (widget);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
320
	details = window->details;
321

Cosimo Cecchi's avatar
Cosimo Cecchi committed
322
323
324
325
326
327
	if (details->size_changed_id != 0) {
		g_signal_handler_disconnect (gtk_window_get_screen (GTK_WINDOW (window)),
					     details->size_changed_id);
		details->size_changed_id = 0;
	}

328
329
	gtk_widget_destroy (details->desktop_selection);

330
	GTK_WIDGET_CLASS (nautilus_desktop_window_parent_class)->unrealize (widget);
331
332
}

333
334
335
static void
set_wmspec_desktop_hint (GdkWindow *window)
{
336
337
338
	GdkAtom atom;

	atom = gdk_atom_intern ("_NET_WM_WINDOW_TYPE_DESKTOP", FALSE);
339
        
340
341
342
343
	gdk_property_change (window,
			     gdk_atom_intern ("_NET_WM_WINDOW_TYPE", FALSE),
			     gdk_x11_xatom_to_atom (XA_ATOM), 32,
			     GDK_PROP_MODE_REPLACE, (guchar *) &atom, 1);
344
345
346
347
348
349
}

static void
realize (GtkWidget *widget)
{
	NautilusDesktopWindow *window;
Cosimo Cecchi's avatar
Cosimo Cecchi committed
350
	NautilusDesktopWindowDetails *details;
351
	GdkVisual *visual;
352
353

	window = NAUTILUS_DESKTOP_WINDOW (widget);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
354
	details = window->details;
Darin Adler's avatar
Darin Adler committed
355

356
357
	/* Make sure we get keyboard events */
	gtk_widget_set_events (widget, gtk_widget_get_events (widget) 
Alexander Larsson's avatar
Alexander Larsson committed
358
			      | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
359
			      
360
361
362
363
364
	visual = gdk_screen_get_rgba_visual (gtk_widget_get_screen (widget));
	if (visual) {
		gtk_widget_set_visual (widget, visual);
	}

365
	/* Do the work of realizing. */
366
	GTK_WIDGET_CLASS (nautilus_desktop_window_parent_class)->realize (widget);
Darin Adler's avatar
Darin Adler committed
367

368
	/* This is the new way to set up the desktop window */
369
	set_wmspec_desktop_hint (gtk_widget_get_window (widget));
370

Cosimo Cecchi's avatar
Cosimo Cecchi committed
371
	details->size_changed_id =
William Jon McCann's avatar
William Jon McCann committed
372
		g_signal_connect (gtk_window_get_screen (GTK_WINDOW (window)), "size-changed",
Cosimo Cecchi's avatar
Cosimo Cecchi committed
373
				  G_CALLBACK (nautilus_desktop_window_screen_size_changed), window);
Darin Adler's avatar
Darin Adler committed
374
}
375

376
377
378
379
380
381
382
383
static void
real_sync_title (NautilusWindow *window,
		 NautilusWindowSlot *slot)
{
	/* hardcode "Desktop" */
	gtk_window_set_title (GTK_WINDOW (window), _("Desktop"));
}

384
385
386
387
388
389
390
static void
real_window_close (NautilusWindow *window)
{
	/* stub, does nothing */
	return;
}

391
static void
Cosimo Cecchi's avatar
Cosimo Cecchi committed
392
nautilus_desktop_window_class_init (NautilusDesktopWindowClass *klass)
393
{
Cosimo Cecchi's avatar
Cosimo Cecchi committed
394
395
	GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
	NautilusWindowClass *nclass = NAUTILUS_WINDOW_CLASS (klass);
396
397
398
	GObjectClass *oclass = G_OBJECT_CLASS (klass);

	oclass->constructed = nautilus_desktop_window_constructed;
399
	oclass->finalize = nautilus_desktop_window_finalize;
400

Cosimo Cecchi's avatar
Cosimo Cecchi committed
401
402
403
	wclass->realize = realize;
	wclass->unrealize = unrealize;
	wclass->map = map;
404
	wclass->delete_event = nautilus_desktop_window_delete_event;
405

406
	nclass->sync_title = real_sync_title;
407
	nclass->close = real_window_close;
408

409
410
	gtk_widget_class_set_css_name (wclass, "nautilus-desktop-window");

Cosimo Cecchi's avatar
Cosimo Cecchi committed
411
	g_type_class_add_private (klass, sizeof (NautilusDesktopWindowDetails));
412
}
413
414
415
416
417
418

gboolean
nautilus_desktop_window_loaded (NautilusDesktopWindow *window)
{
	return window->details->loaded;
}