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