1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /*
4 * Caja
5 *
6 * Copyright (C) 2000 Eazel, Inc.
7 *
8 * Caja is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * Caja 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 GNU
16 * 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, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
21 *
22 * Authors: Darin Adler <darin@bentspoon.com>
23 */
24
25 #include <config.h>
26
27 #include <X11/Xatom.h>
28 #include <gdk/gdkx.h>
29 #include <gtk/gtk.h>
30 #include <gio/gio.h>
31 #include <glib/gi18n.h>
32
33 #include <eel/eel-background.h>
34 #include <eel/eel-vfs-extensions.h>
35
36 #include <libcaja-private/caja-file-utilities.h>
37 #include <libcaja-private/caja-icon-names.h>
38
39 #include "caja-desktop-window.h"
40 #include "caja-window-private.h"
41 #include "caja-actions.h"
42
43 /* Tell screen readers that this is a desktop window */
44
45 static GType caja_desktop_window_accessible_get_type (void);
46
47 G_DEFINE_TYPE (CajaDesktopWindowAccessible, caja_desktop_window_accessible,
48 GTK_TYPE_WINDOW_ACCESSIBLE);
49
50 static AtkAttributeSet *
desktop_get_attributes(AtkObject * accessible)51 desktop_get_attributes (AtkObject *accessible)
52 {
53 AtkAttributeSet *attributes;
54 AtkAttribute *is_desktop;
55
56 attributes = ATK_OBJECT_CLASS (caja_desktop_window_accessible_parent_class)->get_attributes (accessible);
57
58 is_desktop = g_malloc (sizeof (AtkAttribute));
59 is_desktop->name = g_strdup ("is-desktop");
60 is_desktop->value = g_strdup ("true");
61
62 attributes = g_slist_append (attributes, is_desktop);
63
64 return attributes;
65 }
66
67 static void
caja_desktop_window_accessible_init(CajaDesktopWindowAccessible * window)68 caja_desktop_window_accessible_init (CajaDesktopWindowAccessible *window)
69 {
70 }
71
72 static void
caja_desktop_window_accessible_class_init(CajaDesktopWindowAccessibleClass * klass)73 caja_desktop_window_accessible_class_init (CajaDesktopWindowAccessibleClass *klass)
74 {
75 AtkObjectClass *aclass = ATK_OBJECT_CLASS (klass);
76
77 aclass->get_attributes = desktop_get_attributes;
78 }
79
80 struct _CajaDesktopWindowPrivate
81 {
82 gulong size_changed_id;
83
84 gboolean loaded;
85 };
86
87 G_DEFINE_TYPE_WITH_PRIVATE (CajaDesktopWindow, caja_desktop_window,
88 CAJA_TYPE_SPATIAL_WINDOW);
89
90 static void
caja_desktop_window_init(CajaDesktopWindow * window)91 caja_desktop_window_init (CajaDesktopWindow *window)
92 {
93 GtkAction *action;
94 AtkObject *accessible;
95
96 window->details = caja_desktop_window_get_instance_private (window);
97
98 GtkStyleContext *context;
99
100 context = gtk_widget_get_style_context (GTK_WIDGET (window));
101 gtk_style_context_add_class (context, "caja-desktop-window");
102
103 gtk_window_move (GTK_WINDOW (window), 0, 0);
104
105 /* shouldn't really be needed given our semantic type
106 * of _NET_WM_TYPE_DESKTOP, but why not
107 */
108 gtk_window_set_resizable (GTK_WINDOW (window),
109 FALSE);
110
111 g_object_set_data (G_OBJECT (window), "is_desktop_window",
112 GINT_TO_POINTER (1));
113
114 gtk_widget_hide (CAJA_WINDOW (window)->details->statusbar);
115 gtk_widget_hide (CAJA_WINDOW (window)->details->menubar);
116
117 /* Don't allow close action on desktop */
118 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
119 action = gtk_action_group_get_action (CAJA_WINDOW (window)->details->main_action_group,
120 CAJA_ACTION_CLOSE);
121 gtk_action_set_sensitive (action, FALSE);
122 G_GNUC_END_IGNORE_DEPRECATIONS;
123
124 /* Set the accessible name so that it doesn't inherit the cryptic desktop URI. */
125 accessible = gtk_widget_get_accessible (GTK_WIDGET (window));
126
127 if (accessible) {
128 atk_object_set_name (accessible, _("Desktop"));
129 }
130 }
131
132 static gint
caja_desktop_window_delete_event(CajaDesktopWindow * window)133 caja_desktop_window_delete_event (CajaDesktopWindow *window)
134 {
135 /* Returning true tells GTK+ not to delete the window. */
136 return TRUE;
137 }
138
139 void
caja_desktop_window_update_directory(CajaDesktopWindow * window)140 caja_desktop_window_update_directory (CajaDesktopWindow *window)
141 {
142 GFile *location;
143
144 g_assert (CAJA_IS_DESKTOP_WINDOW (window));
145
146 location = g_file_new_for_uri (EEL_DESKTOP_URI);
147 caja_window_go_to (CAJA_WINDOW (window), location);
148 window->details->loaded = TRUE;
149
150 g_object_unref (location);
151 }
152
153 static void
caja_desktop_window_screen_size_changed(GdkScreen * screen,CajaDesktopWindow * window)154 caja_desktop_window_screen_size_changed (GdkScreen *screen,
155 CajaDesktopWindow *window)
156 {
157 int width_request, height_request;
158 int scale;
159
160 scale = gdk_window_get_scale_factor (gdk_screen_get_root_window (screen));
161
162 width_request = WidthOfScreen (gdk_x11_screen_get_xscreen (screen)) / scale;
163 height_request = HeightOfScreen (gdk_x11_screen_get_xscreen (screen)) / scale;
164
165 g_object_set (window,
166 "width_request", width_request,
167 "height_request", height_request,
168 NULL);
169 }
170
171 CajaDesktopWindow *
caja_desktop_window_new(CajaApplication * application,GdkScreen * screen)172 caja_desktop_window_new (CajaApplication *application,
173 GdkScreen *screen)
174 {
175 CajaDesktopWindow *window;
176 int width_request, height_request;
177 int scale;
178
179 scale = gdk_window_get_scale_factor (gdk_screen_get_root_window (screen));
180
181 width_request = WidthOfScreen (gdk_x11_screen_get_xscreen (screen)) / scale;
182 height_request = HeightOfScreen (gdk_x11_screen_get_xscreen (screen)) / scale;
183
184 window = CAJA_DESKTOP_WINDOW
185 (gtk_widget_new (caja_desktop_window_get_type(),
186 "app", application,
187 "width_request", width_request,
188 "height_request", height_request,
189 "screen", screen,
190 NULL));
191 /* Stop wrong desktop window size in GTK 3.20*/
192 /* We don't want to set a default size, which the parent does, since this */
193 /* will cause the desktop window to open at the wrong size in gtk 3.20 */
194 gtk_window_set_default_size (GTK_WINDOW (window), -1, -1);
195
196 /* Special sawmill setting*/
197 GdkWindow *gdkwin;
198 gtk_widget_realize (GTK_WIDGET (window));
199 gdkwin = gtk_widget_get_window (GTK_WIDGET (window));
200 if (gdk_window_ensure_native (gdkwin)) {
201 Display *disp = GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwin));
202 XClassHint *xch = XAllocClassHint ();
203 xch->res_name = "desktop_window";
204 xch->res_class = "Caja";
205 XSetClassHint (disp, GDK_WINDOW_XID(gdkwin), xch);
206 XFree(xch);
207 }
208
209 gdk_window_set_title (gdkwin, _("Desktop"));
210
211 g_signal_connect (window, "delete_event", G_CALLBACK (caja_desktop_window_delete_event), NULL);
212
213 /* Point window at the desktop folder.
214 * Note that caja_desktop_window_init is too early to do this.
215 */
216 caja_desktop_window_update_directory (window);
217
218 return window;
219 }
220
221 static void
map(GtkWidget * widget)222 map (GtkWidget *widget)
223 {
224 /* Chain up to realize our children */
225 GTK_WIDGET_CLASS (caja_desktop_window_parent_class)->map (widget);
226 gdk_window_lower (gtk_widget_get_window (widget));
227 }
228
229 static void
unrealize(GtkWidget * widget)230 unrealize (GtkWidget *widget)
231 {
232 CajaDesktopWindow *window;
233 CajaDesktopWindowPrivate *details;
234 GdkWindow *root_window;
235
236 window = CAJA_DESKTOP_WINDOW (widget);
237 details = window->details;
238
239 root_window = gdk_screen_get_root_window (
240 gtk_window_get_screen (GTK_WINDOW (window)));
241
242 gdk_property_delete (root_window,
243 gdk_atom_intern ("CAJA_DESKTOP_WINDOW_ID", TRUE));
244
245 if (details->size_changed_id != 0) {
246 g_signal_handler_disconnect (gtk_window_get_screen (GTK_WINDOW (window)),
247 details->size_changed_id);
248 details->size_changed_id = 0;
249 }
250
251 GTK_WIDGET_CLASS (caja_desktop_window_parent_class)->unrealize (widget);
252 }
253
254 static void
set_wmspec_desktop_hint(GdkWindow * window)255 set_wmspec_desktop_hint (GdkWindow *window)
256 {
257 GdkAtom atom;
258
259 atom = gdk_atom_intern ("_NET_WM_WINDOW_TYPE_DESKTOP", FALSE);
260
261 gdk_property_change (window,
262 gdk_atom_intern ("_NET_WM_WINDOW_TYPE", FALSE),
263 gdk_x11_xatom_to_atom (XA_ATOM), 32,
264 GDK_PROP_MODE_REPLACE, (guchar *) &atom, 1);
265 }
266
267 static void
set_desktop_window_id(CajaDesktopWindow * window,GdkWindow * gdkwindow)268 set_desktop_window_id (CajaDesktopWindow *window,
269 GdkWindow *gdkwindow)
270 {
271 /* Tuck the desktop windows xid in the root to indicate we own the desktop.
272 */
273 Window window_xid;
274 GdkWindow *root_window;
275
276 root_window = gdk_screen_get_root_window (
277 gtk_window_get_screen (GTK_WINDOW (window)));
278
279 window_xid = GDK_WINDOW_XID (gdkwindow);
280
281 gdk_property_change (root_window,
282 gdk_atom_intern ("CAJA_DESKTOP_WINDOW_ID", FALSE),
283 gdk_x11_xatom_to_atom (XA_WINDOW), 32,
284 GDK_PROP_MODE_REPLACE, (guchar *) &window_xid, 1);
285 }
286
287 static void
realize(GtkWidget * widget)288 realize (GtkWidget *widget)
289 {
290 CajaDesktopWindow *window;
291 CajaDesktopWindowPrivate *details;
292 window = CAJA_DESKTOP_WINDOW (widget);
293 details = window->details;
294
295 /* Make sure we get keyboard events */
296 gtk_widget_set_events (widget, gtk_widget_get_events (widget)
297 | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
298 /* Do the work of realizing. */
299 GTK_WIDGET_CLASS (caja_desktop_window_parent_class)->realize (widget);
300
301 /* This is the new way to set up the desktop window */
302 set_wmspec_desktop_hint (gtk_widget_get_window (widget));
303
304 set_desktop_window_id (window, gtk_widget_get_window (widget));
305
306 details->size_changed_id =
307 g_signal_connect (gtk_window_get_screen (GTK_WINDOW (window)), "size_changed",
308 G_CALLBACK (caja_desktop_window_screen_size_changed), window);
309 }
310
311 static gboolean
draw(GtkWidget * widget,cairo_t * cr)312 draw (GtkWidget *widget,
313 cairo_t *cr)
314 {
315 eel_background_draw (widget, cr);
316
317 return GTK_WIDGET_CLASS (caja_desktop_window_parent_class)->draw (widget, cr);
318 }
319
320 static CajaIconInfo *
real_get_icon(CajaWindow * window,CajaWindowSlot * slot)321 real_get_icon (CajaWindow *window,
322 CajaWindowSlot *slot)
323 {
324 gint scale = gtk_widget_get_scale_factor (GTK_WIDGET (window));
325 return caja_icon_info_lookup_from_name (CAJA_ICON_DESKTOP, 48, scale);
326 }
327
328 static void
caja_desktop_window_class_init(CajaDesktopWindowClass * klass)329 caja_desktop_window_class_init (CajaDesktopWindowClass *klass)
330 {
331 GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
332 CajaWindowClass *nclass = CAJA_WINDOW_CLASS (klass);
333
334 wclass->realize = realize;
335 wclass->unrealize = unrealize;
336 wclass->map = map;
337 wclass->draw = draw;
338
339 gtk_widget_class_set_accessible_type (wclass, CAJA_TYPE_DESKTOP_WINDOW_ACCESSIBLE);
340
341 nclass->window_type = CAJA_WINDOW_DESKTOP;
342 nclass->get_icon = real_get_icon;
343 }
344
345 gboolean
caja_desktop_window_loaded(CajaDesktopWindow * window)346 caja_desktop_window_loaded (CajaDesktopWindow *window)
347 {
348 return window->details->loaded;
349 }
350