1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2002 CodeFactory AB
4 * Copyright (C) 2002 Mikael Hallendal <micke@imendio.com>
5 * Copyright (C) 2004-2008 Imendio AB
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23 #include "config.h"
24 #include <string.h>
25 #include <gtk/gtk.h>
26
27 #ifdef GDK_WINDOWING_X11
28 #include <unistd.h>
29 #include <gdk/gdkx.h>
30 #define WNCK_I_KNOW_THIS_IS_UNSTABLE
31 #include <libwnck/libwnck.h>
32 #endif
33
34 #include "dh-window.h"
35 #include "dh-link.h"
36 #include "dh-parser.h"
37 #include "dh-preferences.h"
38 #include "dh-assistant.h"
39 #include "dh-util.h"
40 #include "ige-conf.h"
41 #include "dh-base.h"
42 #include "dh-book-manager.h"
43
44 typedef struct {
45 GSList *windows;
46 GSList *assistants;
47 DhBookManager *book_manager;
48 } DhBasePriv;
49
50 G_DEFINE_TYPE (DhBase, dh_base, G_TYPE_OBJECT);
51
52 #define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE \
53 (instance, DH_TYPE_BASE, DhBasePriv)
54
55 static void dh_base_init (DhBase *base);
56 static void dh_base_class_init (DhBaseClass *klass);
57
58 static DhBase *base_instance;
59
60 static void
base_finalize(GObject * object)61 base_finalize (GObject *object)
62 {
63 G_OBJECT_CLASS (dh_base_parent_class)->finalize (object);
64 }
65
66 static void
base_dispose(GObject * object)67 base_dispose (GObject *object)
68 {
69 DhBasePriv *priv;
70
71 priv = GET_PRIVATE (object);
72
73 if (priv->book_manager) {
74 g_object_unref (priv->book_manager);
75 priv->book_manager = NULL;
76 }
77 }
78
79
80 static void
dh_base_class_init(DhBaseClass * klass)81 dh_base_class_init (DhBaseClass *klass)
82 {
83 GObjectClass *object_class = G_OBJECT_CLASS (klass);
84
85 object_class->finalize = base_finalize;
86 object_class->dispose = base_dispose;
87
88 g_type_class_add_private (klass, sizeof (DhBasePriv));
89 }
90
91 static void
dh_base_init(DhBase * base)92 dh_base_init (DhBase *base)
93 {
94 DhBasePriv *priv = GET_PRIVATE (base);
95 IgeConf *conf;
96 gchar *path;
97
98 conf = ige_conf_get ();
99 path = dh_util_build_data_filename ("devhelp", "devhelp.defaults", NULL);
100 ige_conf_add_defaults (conf, path);
101 g_free (path);
102
103 priv->book_manager = dh_book_manager_new ();
104 dh_book_manager_populate (priv->book_manager);
105
106 #ifdef GDK_WINDOWING_X11
107 {
108 gint n_screens, i;
109
110 /* For some reason, libwnck doesn't seem to update its list of
111 * workspaces etc if we don't do this.
112 */
113 n_screens = gdk_display_get_n_screens (gdk_display_get_default ());
114 for (i = 0; i < n_screens; i++)
115 wnck_screen_get (i);
116 }
117 #endif
118 }
119
120 static void
base_window_or_assistant_finalized_cb(DhBase * base,gpointer window_or_assistant)121 base_window_or_assistant_finalized_cb (DhBase *base,
122 gpointer window_or_assistant)
123 {
124 DhBasePriv *priv = GET_PRIVATE (base);
125
126 priv->windows = g_slist_remove (priv->windows, window_or_assistant);
127 priv->assistants = g_slist_remove (priv->assistants, window_or_assistant);
128
129 if (priv->windows == NULL && priv->assistants == NULL) {
130 gtk_main_quit ();
131 }
132 }
133
134 DhBase *
dh_base_get(void)135 dh_base_get (void)
136 {
137 if (!base_instance) {
138 base_instance = g_object_new (DH_TYPE_BASE, NULL);
139 }
140
141 return base_instance;
142 }
143
144 DhBase *
dh_base_new(void)145 dh_base_new (void)
146 {
147 if (base_instance) {
148 g_error ("You can only have one DhBase instance.");
149 }
150
151 return dh_base_get ();
152 }
153
154 GtkWidget *
dh_base_new_window(DhBase * base)155 dh_base_new_window (DhBase *base)
156 {
157 DhBasePriv *priv;
158 GtkWidget *window;
159
160 g_return_val_if_fail (DH_IS_BASE (base), NULL);
161
162 priv = GET_PRIVATE (base);
163
164 window = dh_window_new (base);
165
166 priv->windows = g_slist_prepend (priv->windows, window);
167
168 g_object_weak_ref (G_OBJECT (window),
169 (GWeakNotify) base_window_or_assistant_finalized_cb,
170 base);
171
172 return window;
173 }
174
175 GtkWidget *
dh_base_new_assistant(DhBase * base)176 dh_base_new_assistant (DhBase *base)
177 {
178 DhBasePriv *priv;
179 GtkWidget *assistant;
180
181 g_return_val_if_fail (DH_IS_BASE (base), NULL);
182
183 priv = GET_PRIVATE (base);
184
185 assistant = dh_assistant_new (base);
186
187 priv->assistants = g_slist_prepend (priv->assistants, assistant);
188
189 g_object_weak_ref (G_OBJECT (assistant),
190 (GWeakNotify) base_window_or_assistant_finalized_cb,
191 base);
192
193 return assistant;
194 }
195
196 DhBookManager *
dh_base_get_book_manager(DhBase * base)197 dh_base_get_book_manager (DhBase *base)
198 {
199 DhBasePriv *priv;
200
201 g_return_val_if_fail (DH_IS_BASE (base), NULL);
202
203 priv = GET_PRIVATE (base);
204
205 return priv->book_manager;
206 }
207
208 GtkWidget *
dh_base_get_window_on_current_workspace(DhBase * base)209 dh_base_get_window_on_current_workspace (DhBase *base)
210 {
211 DhBasePriv *priv;
212
213 g_return_val_if_fail (DH_IS_BASE (base), NULL);
214
215 priv = GET_PRIVATE (base);
216
217 if (!priv->windows) {
218 return NULL;
219 }
220
221 #ifdef GDK_WINDOWING_X11
222 {
223 WnckWorkspace *workspace;
224 WnckScreen *screen;
225 GtkWidget *window;
226 GList *windows, *w;
227 GSList *l;
228 gulong xid;
229 pid_t pid;
230
231 screen = wnck_screen_get (0);
232 if (!screen) {
233 return NULL;
234 }
235
236 workspace = wnck_screen_get_active_workspace (screen);
237 if (!workspace) {
238 return NULL;
239 }
240
241 xid = 0;
242 pid = getpid ();
243
244 /* Use _stacked so we can use the one on top. */
245 windows = wnck_screen_get_windows_stacked (screen);
246 windows = g_list_last (windows);
247
248 for (w = windows; w; w = w->prev) {
249 if (wnck_window_is_on_workspace (w->data, workspace) &&
250 wnck_window_get_pid (w->data) == pid) {
251 xid = wnck_window_get_xid (w->data);
252 break;
253 }
254 }
255
256 if (!xid) {
257 return NULL;
258 }
259
260 /* Return the first matching window we have. */
261 for (l = priv->windows; l; l = l->next) {
262 window = l->data;
263
264 #if GTK_CHECK_VERSION (2,14,0)
265 if (GDK_WINDOW_XID (gtk_widget_get_window (window)) == xid) {
266 #else
267 if (GDK_WINDOW_XID (window->window) == xid) {
268 #endif
269 return window;
270 }
271 }
272 }
273
274 return NULL;
275 #else
276 return priv->windows->data;
277 #endif
278 }
279
280 GtkWidget *
281 dh_base_get_window (DhBase *base)
282 {
283 GtkWidget *window;
284
285 g_return_val_if_fail (DH_IS_BASE (base), NULL);
286
287 window = dh_base_get_window_on_current_workspace (base);
288 if (!window) {
289 window = dh_base_new_window (base);
290 gtk_window_present (GTK_WINDOW (window));
291 }
292
293 return window;
294 }
295
296 void
297 dh_base_quit (DhBase *base)
298 {
299 DhBasePriv *priv = GET_PRIVATE (base);
300
301 /* Make sure all of the windows get a chance to release their resources
302 * properly. As they get destroyed,
303 * base_window_or_assistant_finalized_cb() will be called, and when the
304 * last one is removed, we will quit */
305 g_slist_foreach (priv->windows, (GFunc)gtk_widget_destroy, NULL);
306 g_slist_foreach (priv->assistants, (GFunc)gtk_widget_destroy, NULL);
307 }
308