[Window] Fix automatic pulldown code
[tilda-gobject.git] / tilda-controller.c
1 #include "tilda.h"
2 #include "tilda-config.h"
3 #include "tilda-controller.h"
4 #include "tilda-controller-dbus-glue.h"
5 #include "tilda-window.h"
6
7 /**
8  * Register this object with DBus, so it can be interacted with!
9  */
10 static void
11 tilda_controller_dbus_register_object (TildaController *self)
12 {
13         debug_enter  ();
14         debug_assert (TILDA_IS_CONTROLLER(self));
15
16         static const gchar object_path[] = "/net/sourceforge/Tilda";
17
18         /* If DBus is not running, leave */
19         if (!dbus_connection)
20                 return;
21
22         dbus_g_connection_register_g_object (dbus_connection, object_path, G_OBJECT(self));
23 }
24
25 /**
26  * Find the next free window number, so it can be used by a TildaWindow
27  */
28 static gint
29 tilda_controller_find_next_free_window_number (TildaController *self)
30 {
31         debug_enter  ();
32         debug_assert (TILDA_IS_CONTROLLER(self));
33
34         gint i, j;
35         gboolean found;
36
37         for (i=0; i<INT_MAX; ++i)
38         {
39                 found = FALSE;
40
41                 for (j=0; j<self->windows->len; ++j)
42                 {
43                         TildaWindow *tw = g_ptr_array_index (self->windows, j);
44
45                         if (tw->number == i)
46                         {
47                                 found = TRUE;
48                                 break;
49                         }
50                 }
51
52                 if (!found)
53                         return i;
54         }
55
56         return 0;
57 }
58
59 gboolean
60 tilda_controller_add_window (TildaController *self)
61 {
62         debug_enter  ();
63         debug_assert (TILDA_IS_CONTROLLER(self));
64
65         TildaWindow *ret;
66         gint number;
67
68         number = tilda_controller_find_next_free_window_number (self);
69         ret = g_object_new (TILDA_TYPE_WINDOW,
70                                                 "number", number,
71                                                 "controller", G_OBJECT(self),
72                                                 NULL);
73
74         g_ptr_array_add (self->windows, ret);
75
76         debug_printf ("Adding window: 0x%p (number %d of %d)\n", ret, ret->number, self->windows->len);
77         return TRUE;
78 }
79
80 gboolean
81 tilda_controller_remove_window (TildaController *self, gint window_number)
82 {
83         debug_enter  ();
84         debug_assert (TILDA_IS_CONTROLLER(self));
85         debug_assert (window_number >= 0);
86
87         gint i;
88         TildaWindow *win;
89
90         for (i=0; i<self->windows->len; ++i)
91         {
92                 win = g_ptr_array_index (self->windows, i);
93
94                 if (win->number == window_number)
95                 {
96                         debug_printf ("Deleting TildaWindow 0x%p (number %d of %d)\n",
97                                                   win, win->number, self->windows->len);
98                         g_ptr_array_remove_index (self->windows, i);
99                         g_object_unref (G_OBJECT(win));
100
101                         if (self->windows->len == 0)
102                         {
103                                 debug_printf ("No windows left, exiting...\n");
104
105                                 /* We get unref'd in main() */
106                                 gtk_main_quit ();
107                         }
108
109                         /* We were able to remove the window */
110                         return TRUE;
111                 }
112         }
113
114         /* There must have been no window to remove */
115         return FALSE;
116 }
117
118 /**
119  * Check if a key is used in one of our windows.
120  *
121  * This is needed because the tomboy_keybinder_bind() function allows
122  * more than one callback to be registered for the same key.
123  */
124 gboolean
125 tilda_controller_global_key_in_use (const TildaController *self, const gchar *keystr)
126 {
127         gint i;
128         TildaWindow *tw;
129
130         guint key1, key2;
131         GdkModifierType mod1, mod2;
132
133         gtk_accelerator_parse (keystr, &key1, &mod1);
134
135         for (i=0; i<self->windows->len; ++i)
136         {
137                 tw = g_ptr_array_index (self->windows, i);
138                 gtk_accelerator_parse (tw->key, &key2, &mod2);
139
140                 if (key1 == key2 && mod1 == mod2)
141                         return TRUE;
142         }
143
144         /* No identical keys found, we're ok */
145         return FALSE;
146 }
147
148 gboolean
149 tilda_controller_quit (TildaController *self, GError **error)
150 {
151         debug_enter  ();
152         debug_assert (TILDA_IS_CONTROLLER(self));
153
154         /* Not much left but to quit, since we get unref'd in main() */
155         gtk_main_quit ();
156
157         return TRUE;
158 }
159
160 /*******************************************************************************
161  * GObject code below... it is doubtful you'll need to make big changes :)
162  ******************************************************************************/
163
164 static GObjectClass *parent_class = NULL;
165
166 enum tilda_controller_properties {
167         TILDA_CONTROLLER_INITIAL_WINDOWS = 1,
168 };
169
170 static void
171 tilda_controller_instance_init (GTypeInstance *instance,
172                                                                 gpointer       g_class)
173 {
174         debug_enter ();
175
176         TildaController *self = (TildaController *) instance;
177
178         self->dispose_has_run = FALSE;
179         self->windows = g_ptr_array_new ();
180         self->initial_windows = 1;
181 }
182
183 static void
184 tilda_controller_set_property (GObject      *object,
185                                                            guint         property_id,
186                                                            const GValue *value,
187                                                            GParamSpec   *pspec)
188 {
189         debug_enter ();
190
191         TildaController *self = (TildaController *) object;
192
193         switch (property_id)
194         {
195                 case TILDA_CONTROLLER_INITIAL_WINDOWS:
196                         self->initial_windows = g_value_get_int (value);
197                         debug_printf ("tilda controller initial windows: %d\n", self->initial_windows);
198                         break;
199
200                 default:
201                         /* We don't have any other properties */
202                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
203                         break;
204         }
205 }
206
207 static void
208 tilda_controller_get_property (GObject    *object,
209                                                            guint       property_id,
210                                                            GValue     *value,
211                                                            GParamSpec *pspec)
212 {
213         debug_enter ();
214
215         TildaController *self = (TildaController *) object;
216
217         switch (property_id)
218         {
219                 case TILDA_CONTROLLER_INITIAL_WINDOWS:
220                         g_value_set_int (value, self->initial_windows);
221                         break;
222
223                 default:
224                         /* We don't have any other properties */
225                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
226                         break;
227         }
228 }
229
230 static GObject *
231 tilda_controller_constructor (GType                  type,
232                                                           guint                  n_construct_properties,
233                                                           GObjectConstructParam *construct_properties)
234 {
235         debug_enter ();
236
237         GObject *obj;
238         TildaController *self;
239         gint i;
240
241         /* Invoke the parent constructor */
242         TildaControllerClass *klass;
243         klass = TILDA_CONTROLLER_CLASS (g_type_class_peek (TILDA_TYPE_CONTROLLER));
244         obj = parent_class->constructor (type,
245                                                                          n_construct_properties,
246                                                                          construct_properties);
247
248         /* The object is ready, and all constructor-time properties have been set.
249          * Have fun! */
250         self = TILDA_CONTROLLER(obj);
251
252         /* Set all of the properties from the config */
253         tilda_controller_set_property_from_config (self, "initial-windows");
254
255         /* Add initial windows */
256         for (i=0; i<self->initial_windows; ++i)
257                 tilda_controller_add_window (self);
258
259         /* Register this object with DBus */
260         tilda_controller_dbus_register_object (self);
261
262         return obj;
263 }
264
265 static void
266 tilda_controller_dispose (GObject *obj)
267 {
268         debug_enter ();
269
270         TildaController *self = (TildaController *) obj;
271
272         /* We must only run dispose once ... */
273         if (self->dispose_has_run)
274                 return;
275
276         self->dispose_has_run = TRUE;
277
278         /*
279          * In dispose, you are supposed to free all types referenced from this
280          * object which might themselves hold a reference to self. Generally,
281          * the most simple solution is to unref all members on which you own a
282          * reference.
283          */
284         g_ptr_array_foreach (self->windows, g_object_unref, NULL);
285
286         /* Chain up to the parent class */
287         G_OBJECT_CLASS (parent_class)->dispose (obj);
288 }
289
290 static void
291 tilda_controller_finalize (GObject *obj)
292 {
293         debug_enter ();
294
295         TildaController *self = (TildaController *) obj;
296
297         /*
298          * Here, complete the object's destruction.
299          * You might not need to do much more than
300          * g_free() any primitives.
301          */
302
303         /* Chain up to the parent class */
304         G_OBJECT_CLASS (parent_class)->finalize (obj);
305 }
306
307 static void
308 tilda_controller_class_init (gpointer g_class,
309                                                          gpointer g_class_data)
310 {
311         debug_enter ();
312
313         GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
314         TildaControllerClass *klass = TILDA_CONTROLLER_CLASS (g_class);
315         GParamSpec *pspec;
316
317         gobject_class->set_property = tilda_controller_set_property;
318         gobject_class->get_property = tilda_controller_get_property;
319         gobject_class->dispose = tilda_controller_dispose;
320         gobject_class->finalize = tilda_controller_finalize;
321         gobject_class->constructor = tilda_controller_constructor;
322
323         parent_class = g_type_class_peek_parent (klass);
324
325         /* Add properties here */
326         pspec = g_param_spec_int ("initial-windows",
327                                                           _("The number of windows that will be opened on startup"),
328                                                           NULL,
329                                                           1,
330                                                           100, /* Sane Limit */
331                                                           1,
332                                                           G_PARAM_READWRITE);
333
334         g_object_class_install_property (gobject_class,
335                                                                          TILDA_CONTROLLER_INITIAL_WINDOWS,
336                                                                          pspec);
337
338         /* Hook the TildaController type into DBus */
339         dbus_g_object_type_install_info (tilda_controller_get_type(),
340                                                                          &dbus_glib_tilda_controller_object_info);
341 }
342
343 GType tilda_controller_get_type (void)
344 {
345         static GType type = 0;
346
347         if (type == 0)
348         {
349                 static const GTypeInfo info = {
350                         sizeof (TildaControllerClass),
351                         NULL,   /* base_init */
352                         NULL,   /* base_finalize */
353                         tilda_controller_class_init,    /* class_init */
354                         NULL,   /* class_finalize */
355                         NULL,   /* class_data */
356                         sizeof (TildaController),
357                         0,              /* n_preallocs */
358                         tilda_controller_instance_init, /* instance_init */
359                 };
360
361                 type = g_type_register_static (G_TYPE_OBJECT,
362                                                                            "TildaControllerType",
363                                                                            &info,
364                                                                            0);
365         }
366
367         return type;
368 }
369
370 /* vim: set ts=4 sts=4 sw=4 noet tw=112: */