~ubuntu-branches/ubuntu/jaunty/ekiga/jaunty-updates

« back to all changes in this revision

Viewing changes to src/gui/statusmenu.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2008-12-07 10:30:45 UTC
  • mfrom: (1.2.3 experimental)
  • Revision ID: james.westby@ubuntu.com-20081207103045-iaurrjo4p7d1nngo
Tags: 3.0.1-1ubuntu1
* Merge to Debian experimental, to get Ekiga 3. (LP: #274085) Remaining
  Ubuntu changes:
  - Launchpad Integration: (Ubuntu specific)
    + debian/control.in: Add liblaunchpad-integration-dev build dependency.
    + Add ubuntu_lpi.patch: Call launchpad_integration_add_items() in main() and
      check for the launchpad-integration pkg-config module.
    + Add autoconf.patch: autoconf changes from above patch.
  - Add ubuntu_desktop-file-onlyshowin.patch: Show ekiga in Mobile, too.
    (Ubuntu specific).
  - debian/control.in: Add missing fdupes build dependency for identical
    GNOME help file symlinking. (Debian #505536)
* Drop 42_change_pixmaps.dpatch: Many of the old icons do not exist any
  more, some have been replaced, and keeping the remaining three would make
  them look very inconsistent.
* Convert our dpatches to quilt patches and rewrite them for new upstream
  version.
* Add migrate_2.0_settings.patch: Properly migrate settings from
  2.0. Taken from upstream SVN, thanks to Damien Sandras!

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
/* Ekiga -- A VoIP and Video-Conferencing application
 
3
 * Copyright (C) 2000-2008 Damien Sandras
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software Foundation,
 
17
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
18
 *
 
19
 *
 
20
 * Ekiga is licensed under the GPL license and as a special exception,
 
21
 * you have permission to link or otherwise combine this program with the
 
22
 * programs OPAL, OpenH323 and PWLIB, and distribute the combination,
 
23
 * without applying the requirements of the GNU GPL to the OPAL, OpenH323
 
24
 * and PWLIB programs, as long as you do follow the requirements of the
 
25
 * GNU GPL for all the rest of the software thus combined.
 
26
 */
 
27
 
 
28
 
 
29
/*
 
30
 *                         statusmenu.h  -  description
 
31
 *                         -------------------------------
 
32
 *   begin                : Mon Jan 28 2008
 
33
 *   copyright            : (C) 2000-2008 by Damien Sandras 
 
34
 *   description          : Contains a StatusMenu
 
35
 *
 
36
 */
 
37
 
 
38
 
 
39
/*
 
40
 * The StatusMenu can be seen as an extended preferences window. 
 
41
 * It can thus directly react to GMConf key changes and write in the GMConf 
 
42
 * database.
 
43
 *
 
44
 * It does not need to listen to sigc++ signals to react to key changes.
 
45
 *
 
46
 */
 
47
 
 
48
#include "config.h"
 
49
#include "statusmenu.h"
 
50
 
 
51
#include "gmconf.h"
 
52
#include "gmstockicons.h"
 
53
 
 
54
#include "common.h"
 
55
 
 
56
 
 
57
struct _StatusMenuPrivate
 
58
{
 
59
  GtkListStore *list_store; // List store storing the menu
 
60
  GtkWindow    *parent;     // Parent window
 
61
};
 
62
 
 
63
enum Columns
 
64
{
 
65
  COL_ICON,          // The status icon
 
66
  COL_MESSAGE,       // The status message (if any)
 
67
  COL_MESSAGE_TYPE,  // The status message type
 
68
  COL_SEPARATOR,     // A separator
 
69
  NUM_COLUMNS
 
70
};
 
71
 
 
72
enum MessageType
 
73
{
 
74
  TYPE_ONLINE,             // Generic online message
 
75
  TYPE_AWAY,               // Generic away message
 
76
  TYPE_DND,                // Generic Do Not Disturb message
 
77
  NUM_STATUS_TYPES,
 
78
  TYPE_CUSTOM_ONLINE,      // Custom online message
 
79
  TYPE_CUSTOM_AWAY,        // Custom away message
 
80
  TYPE_CUSTOM_DND,         // Custom DND message
 
81
  NUM_STATUS_CUSTOM_TYPES,
 
82
  TYPE_CUSTOM_ONLINE_NEW,  // Add new custom online message
 
83
  TYPE_CUSTOM_AWAY_NEW,    // Add new custom away message
 
84
  TYPE_CUSTOM_DND_NEW,     // Add new custom dnd message
 
85
  TYPE_CLEAR               // Clear custom message(s)
 
86
};
 
87
 
 
88
const gchar *statuses [] = 
 
89
 
90
  N_("Online"), 
 
91
  N_("Away"), 
 
92
  N_("Do Not Disturb") 
 
93
};
 
94
 
 
95
const char* status_types_names[] = 
 
96
 
97
  "online",
 
98
  "away",
 
99
  "dnd"
 
100
};
 
101
 
 
102
const char* status_types_keys[] = 
 
103
 
104
  PERSONAL_DATA_KEY "online_custom_status",
 
105
  PERSONAL_DATA_KEY "away_custom_status",
 
106
  PERSONAL_DATA_KEY "dnd_custom_status"
 
107
};
 
108
 
 
109
const char* stock_status[] = 
 
110
 
111
  GM_STOCK_STATUS_ONLINE,
 
112
  GM_STOCK_STATUS_AWAY,
 
113
  GM_STOCK_STATUS_DND
 
114
};
 
115
 
 
116
 
 
117
/**
 
118
 * Callbacks
 
119
 */
 
120
 
 
121
/** Return true if the row is a separator.
 
122
 *
 
123
 * @param model is a pointer to the GtkTreeModel
 
124
 * @param iter is a pointer to the current row in the GtkTreeModel
 
125
 * @return true if the current row is a separator, false otherwise
 
126
 */
 
127
static gboolean
 
128
status_menu_row_is_separator (GtkTreeModel *model,
 
129
                              GtkTreeIter *iter,
 
130
                              gpointer /*data*/);
 
131
 
 
132
 
 
133
/** Trigger the appropriate action when a choice is made in the StatusMenu.
 
134
 *
 
135
 * It will update the GmConf key with the chosen value or display a popup 
 
136
 * allowing to add or remove status messages.
 
137
 *
 
138
 * @param box is a pointer to the GtkComboBox
 
139
 * @param data is a pointer to the StatusMenu
 
140
 */
 
141
static void 
 
142
status_menu_option_changed (GtkComboBox *box,
 
143
                            gpointer data);
 
144
 
 
145
 
 
146
/**
 
147
 * GmConf notifiers
 
148
 */
 
149
 
 
150
/** This notifier is triggered when one of the custom messages list is updated. 
 
151
 *
 
152
 * It updates the StatusMenu content with the new values.
 
153
 *
 
154
 * @param id is the GmConf notifier id
 
155
 * @param entry is the GmConfEntry for which the notification was triggered
 
156
 * @param data is a pointer to the StatusMenu
 
157
 */
 
158
static void
 
159
status_menu_custom_messages_changed (gpointer id,
 
160
                                     GmConfEntry *entry,
 
161
                                     gpointer data);
 
162
 
 
163
 
 
164
/** This notifier is triggered when the long status message is modified. 
 
165
 *
 
166
 * It updates the StatusMenu content with the new value as current choice.
 
167
 *
 
168
 * @param id is the GmConf notifier id
 
169
 * @param entry is the GmConfEntry for which the notification was triggered
 
170
 * @param data is a pointer to the StatusMenu
 
171
 */
 
172
static void
 
173
long_status_message_changed (gpointer id,
 
174
                             GmConfEntry *entry,
 
175
                             gpointer data);
 
176
 
 
177
 
 
178
/** This notifier is triggered when the short status message is modified. 
 
179
 *
 
180
 * It updates the StatusMenu content with the new value as current choice.
 
181
 *
 
182
 * @param id is the GmConf notifier id
 
183
 * @param entry is the GmConfEntry for which the notification was triggered
 
184
 * @param data is a pointer to the StatusMenu
 
185
 */
 
186
static void
 
187
short_status_message_changed (gpointer id,
 
188
                              GmConfEntry *entry,
 
189
                              gpointer data);
 
190
 
 
191
 
 
192
/**
 
193
 * Static methods
 
194
 */
 
195
 
 
196
/** This function populates the StatusMenu with its initial content. Content is 
 
197
 * taken from the GmConf keys used by the StatusMenu.
 
198
 *
 
199
 * @param self is the StatusMenu
 
200
 * @param custom_status_array is the list of custom messages
 
201
 */
 
202
static void
 
203
status_menu_populate (StatusMenu *self,
 
204
                      GSList *custom_status_array [NUM_STATUS_TYPES]);
 
205
 
 
206
 
 
207
/** This function updates the default active status in the StatusMenu.
 
208
 *
 
209
 * @param self is the StatusMenu
 
210
 * @param short_status is the short status of the current status
 
211
 * @param long_status is the long status description of the current status
 
212
 */
 
213
static void
 
214
status_menu_set_option (StatusMenu *self,
 
215
                        const char *short_status,
 
216
                        const char *long_status);
 
217
 
 
218
 
 
219
/** This function presents a popup allowing to remove some of the user defined
 
220
 * status messages.
 
221
 *
 
222
 * @param self is the StatusMenu
 
223
 */
 
224
static void
 
225
status_menu_clear_status_message_dialog_run (StatusMenu *self);
 
226
 
 
227
 
 
228
/** This function runs a dialog allowing the user to define custom long status
 
229
 * messages that he will be able to publish.
 
230
 *
 
231
 * @param self is the StatusMenu
 
232
 * @param option is the defined message type (TYPE_CUSTOM_ONLINE, 
 
233
 * TYPE_CUSTOM_AWAY, TYPE_CUSTOM_DND)
 
234
 */
 
235
static void
 
236
status_menu_new_status_message_dialog_run (StatusMenu *self,
 
237
                                           int option);
 
238
 
 
239
 
 
240
/* 
 
241
 * GObject stuff 
 
242
 */
 
243
static GObjectClass *parent_class = NULL;
 
244
 
 
245
static void status_menu_class_init (gpointer g_class,
 
246
                                    gpointer class_data);
 
247
 
 
248
static void status_menu_init (StatusMenu *);
 
249
 
 
250
static void status_menu_dispose (GObject *obj);
 
251
 
 
252
static void status_menu_finalize (GObject *obj);
 
253
 
 
254
 
 
255
/* 
 
256
 * Callbacks
 
257
 */
 
258
static gboolean
 
259
status_menu_row_is_separator (GtkTreeModel *model,
 
260
                              GtkTreeIter *iter,
 
261
                              gpointer /*data*/)
 
262
{
 
263
  gboolean is_separator;
 
264
 
 
265
  gtk_tree_model_get (model, iter, COL_SEPARATOR, &is_separator, -1);
 
266
 
 
267
  return is_separator;
 
268
}
 
269
 
 
270
 
 
271
static void 
 
272
status_menu_option_changed (GtkComboBox *box,
 
273
                            gpointer data)
 
274
{
 
275
  GtkTreeIter iter;
 
276
  
 
277
  int i = 0;
 
278
  gchar* status = NULL;
 
279
 
 
280
  GtkTreeModel* model = NULL;
 
281
  StatusMenu* self = STATUS_MENU (data);
 
282
 
 
283
  g_return_if_fail (self != NULL);
 
284
 
 
285
  if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (box), &iter)) {
 
286
 
 
287
    model = gtk_combo_box_get_model (GTK_COMBO_BOX (box));
 
288
    gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, COL_MESSAGE_TYPE, &i, 
 
289
                        COL_MESSAGE, &status, -1);
 
290
 
 
291
    switch (i)
 
292
      {
 
293
      case TYPE_ONLINE:
 
294
        gm_conf_set_string (PERSONAL_DATA_KEY "short_status", "online");
 
295
        gm_conf_set_string (PERSONAL_DATA_KEY "long_status", "");
 
296
        break;
 
297
 
 
298
      case TYPE_AWAY:
 
299
        gm_conf_set_string (PERSONAL_DATA_KEY "short_status", "away");
 
300
        gm_conf_set_string (PERSONAL_DATA_KEY "long_status", "");
 
301
        break;
 
302
 
 
303
      case TYPE_DND:
 
304
        gm_conf_set_string (PERSONAL_DATA_KEY "short_status", "dnd");
 
305
        gm_conf_set_string (PERSONAL_DATA_KEY "long_status", "");
 
306
        break;
 
307
 
 
308
      case TYPE_CUSTOM_ONLINE:
 
309
        gm_conf_set_string (PERSONAL_DATA_KEY "short_status", "online");
 
310
        gm_conf_set_string (PERSONAL_DATA_KEY "long_status", status);
 
311
        break;
 
312
 
 
313
      case TYPE_CUSTOM_AWAY:
 
314
        gm_conf_set_string (PERSONAL_DATA_KEY "short_status", "away");
 
315
        gm_conf_set_string (PERSONAL_DATA_KEY "long_status", status);
 
316
        break;
 
317
 
 
318
      case TYPE_CUSTOM_DND:
 
319
        gm_conf_set_string (PERSONAL_DATA_KEY "short_status", "dnd");
 
320
        gm_conf_set_string (PERSONAL_DATA_KEY "long_status", status);
 
321
        break;
 
322
 
 
323
      case TYPE_CUSTOM_ONLINE_NEW:
 
324
        status_menu_new_status_message_dialog_run (self, TYPE_CUSTOM_ONLINE);
 
325
        break;
 
326
 
 
327
      case TYPE_CUSTOM_AWAY_NEW:
 
328
        status_menu_new_status_message_dialog_run (self, TYPE_CUSTOM_AWAY);
 
329
        break;
 
330
 
 
331
      case TYPE_CUSTOM_DND_NEW:
 
332
        status_menu_new_status_message_dialog_run (self, TYPE_CUSTOM_DND);
 
333
        break;
 
334
 
 
335
      case TYPE_CLEAR:
 
336
        status_menu_clear_status_message_dialog_run (self);
 
337
        break;
 
338
 
 
339
      default:
 
340
        break;
 
341
      }
 
342
 
 
343
    g_free (status);
 
344
  }
 
345
}
 
346
 
 
347
 
 
348
static void
 
349
status_menu_custom_messages_changed (gpointer /*id*/,
 
350
                                     GmConfEntry *entry,
 
351
                                     gpointer self)
 
352
{
 
353
  std::string key = gm_conf_entry_get_key (entry);
 
354
  GSList *current = gm_conf_entry_get_list (entry);
 
355
  GSList *custom_status_array [NUM_STATUS_TYPES];
 
356
 
 
357
  for (int i = 0 ; i < NUM_STATUS_TYPES ; i++) {
 
358
    if (key == status_types_keys [i])
 
359
      custom_status_array [i] = current;
 
360
    else
 
361
      custom_status_array [i] = gm_conf_get_string_list (status_types_keys [i]);
 
362
  }
 
363
 
 
364
  status_menu_populate (STATUS_MENU (self), custom_status_array);
 
365
 
 
366
  for (int i = 0 ; i < NUM_STATUS_TYPES ; i++) {
 
367
    g_slist_foreach (custom_status_array [i], (GFunc) g_free, NULL);
 
368
    g_slist_free (custom_status_array [i]);
 
369
  }
 
370
}
 
371
 
 
372
 
 
373
static void
 
374
long_status_message_changed (gpointer /*id*/,
 
375
                             GmConfEntry *entry,
 
376
                             gpointer data)
 
377
{
 
378
  StatusMenu* self = STATUS_MENU (data);
 
379
  const char* long_status = NULL;
 
380
  char* short_status = NULL;
 
381
 
 
382
  g_return_if_fail (self != NULL);
 
383
 
 
384
  short_status = gm_conf_get_string (PERSONAL_DATA_KEY "short_status");
 
385
  long_status = gm_conf_entry_get_string (entry);
 
386
 
 
387
  if (short_status == NULL)
 
388
    short_status = g_strdup ("");
 
389
 
 
390
  status_menu_set_option (self, short_status, long_status);
 
391
 
 
392
  g_free (short_status);
 
393
}
 
394
 
 
395
 
 
396
static void
 
397
short_status_message_changed (gpointer /*id*/,
 
398
                              GmConfEntry *entry,
 
399
                              gpointer data)
 
400
{
 
401
  StatusMenu* self = STATUS_MENU (data);
 
402
  const char* short_status = NULL;
 
403
  char* long_status = NULL;
 
404
 
 
405
  g_return_if_fail (self != NULL);
 
406
 
 
407
  short_status = gm_conf_entry_get_string (entry);
 
408
  long_status = gm_conf_get_string (PERSONAL_DATA_KEY "long_status");
 
409
 
 
410
  if (long_status == NULL)
 
411
    long_status = g_strdup ("");
 
412
 
 
413
  status_menu_set_option (self, short_status, long_status);
 
414
 
 
415
  g_free (long_status);
 
416
}
 
417
 
 
418
 
 
419
 
 
420
/*
 
421
 * Static methods
 
422
 */
 
423
static void
 
424
status_menu_populate (StatusMenu *self,
 
425
                      GSList *custom_status_array [NUM_STATUS_TYPES])
 
426
{
 
427
  gboolean has_custom_messages = false;
 
428
  GSList *custom_status = NULL;
 
429
  GSList *liter = NULL;
 
430
  GtkTreeIter iter;
 
431
  GdkPixbuf* icon = NULL;
 
432
 
 
433
  gtk_list_store_clear (GTK_LIST_STORE (self->priv->list_store));
 
434
 
 
435
  for (int i = 0 ; i < NUM_STATUS_TYPES ; i++) {
 
436
 
 
437
    statuses [i] = gettext (statuses [i]);
 
438
    custom_status = custom_status_array [i];
 
439
    liter = custom_status;
 
440
 
 
441
    icon = gtk_widget_render_icon (GTK_WIDGET (self),
 
442
                                   stock_status [i],
 
443
                                   GTK_ICON_SIZE_MENU, NULL);
 
444
 
 
445
    gtk_list_store_append (GTK_LIST_STORE (self->priv->list_store), &iter);
 
446
    gtk_list_store_set (GTK_LIST_STORE (self->priv->list_store), &iter,
 
447
                        COL_ICON, icon, 
 
448
                        COL_MESSAGE, statuses[i], 
 
449
                        COL_MESSAGE_TYPE, i,
 
450
                        COL_SEPARATOR, false, 
 
451
                        -1); 
 
452
 
 
453
    gtk_list_store_append (GTK_LIST_STORE (self->priv->list_store), &iter);
 
454
    gtk_list_store_set (GTK_LIST_STORE (self->priv->list_store), &iter,
 
455
                        COL_ICON, icon, 
 
456
                        COL_MESSAGE, _("Custom message..."), 
 
457
                        COL_MESSAGE_TYPE, NUM_STATUS_CUSTOM_TYPES + (i + 1),
 
458
                        COL_SEPARATOR, false, 
 
459
                        -1); 
 
460
 
 
461
    while (liter) {
 
462
 
 
463
      gtk_list_store_append (GTK_LIST_STORE (self->priv->list_store), &iter);
 
464
      gtk_list_store_set (GTK_LIST_STORE (self->priv->list_store), &iter,
 
465
                          COL_ICON, icon, 
 
466
                          COL_MESSAGE, (char*) (liter->data), 
 
467
                          COL_MESSAGE_TYPE, NUM_STATUS_TYPES + (i + 1),
 
468
                          COL_SEPARATOR, false, 
 
469
                          -1); 
 
470
 
 
471
      liter = g_slist_next (liter);
 
472
      has_custom_messages = true;
 
473
    }
 
474
 
 
475
    if (i < NUM_STATUS_TYPES - 1) {
 
476
      gtk_list_store_append (GTK_LIST_STORE (self->priv->list_store), &iter);
 
477
      gtk_list_store_set (GTK_LIST_STORE (self->priv->list_store), &iter,
 
478
                          COL_SEPARATOR, true, 
 
479
                          -1); 
 
480
    }
 
481
 
 
482
    g_object_unref (icon);
 
483
  }
 
484
 
 
485
  /* Clear message */
 
486
  if (has_custom_messages) {
 
487
 
 
488
    gtk_list_store_append (GTK_LIST_STORE (self->priv->list_store), &iter);
 
489
    gtk_list_store_set (GTK_LIST_STORE (self->priv->list_store), &iter,
 
490
                        COL_SEPARATOR, true, 
 
491
                        -1); 
 
492
 
 
493
    icon = gtk_widget_render_icon (GTK_WIDGET (self),
 
494
                                   GTK_STOCK_CLEAR,
 
495
                                   GTK_ICON_SIZE_MENU, NULL);
 
496
    gtk_list_store_append (GTK_LIST_STORE (self->priv->list_store), &iter);
 
497
    gtk_list_store_set (GTK_LIST_STORE (self->priv->list_store), &iter,
 
498
                        COL_ICON, icon,
 
499
                        COL_MESSAGE, _("Clear"), 
 
500
                        COL_MESSAGE_TYPE, TYPE_CLEAR,
 
501
                        COL_SEPARATOR, false, 
 
502
                        -1); 
 
503
    g_object_unref (icon);
 
504
  }
 
505
}
 
506
 
 
507
 
 
508
static void
 
509
status_menu_set_option (StatusMenu *self,
 
510
                        const char *short_status,
 
511
                        const char *long_status)
 
512
{
 
513
  GtkTreeIter iter;
 
514
 
 
515
  bool valid = false;
 
516
  gchar *status = NULL;
 
517
  int i = 0;
 
518
  int cpt = 0;
 
519
 
 
520
  g_return_if_fail (short_status != NULL && long_status != NULL);
 
521
 
 
522
  valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->priv->list_store), &iter);
 
523
  while (valid) {
 
524
 
 
525
    gtk_tree_model_get (GTK_TREE_MODEL (self->priv->list_store), &iter, 
 
526
                        COL_MESSAGE_TYPE, &i,
 
527
                        COL_MESSAGE, &status, -1); 
 
528
 
 
529
    // Check if it is a custom status message and if it is in the list
 
530
    if (i == TYPE_CUSTOM_ONLINE || i == TYPE_CUSTOM_AWAY || i == TYPE_CUSTOM_DND) {
 
531
      if (!strcmp (status_types_names[i - NUM_STATUS_TYPES - 1], short_status) && !strcmp (long_status, status))
 
532
        break;
 
533
    }
 
534
 
 
535
    // Long status empty, the user did not set a custom message
 
536
    if (i == TYPE_ONLINE || i == TYPE_AWAY || i == TYPE_DND) {
 
537
      if (long_status && !strcmp(long_status, "") && !strcmp (status_types_names[i], short_status)) 
 
538
        break;
 
539
    }
 
540
 
 
541
    g_free (status);
 
542
 
 
543
    cpt++;
 
544
    valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (self->priv->list_store), &iter);
 
545
  }
 
546
 
 
547
  if (valid) {
 
548
    gtk_combo_box_set_active (GTK_COMBO_BOX (self), cpt);
 
549
 
 
550
    if (status)
 
551
      g_free (status);
 
552
  }
 
553
}
 
554
 
 
555
 
 
556
static void
 
557
status_menu_clear_status_message_dialog_run (StatusMenu *self)
 
558
{
 
559
  GtkTreeIter iter, liter;
 
560
 
 
561
  GSList *conf_list [3] = { NULL, NULL, NULL };
 
562
  GtkWidget *dialog = NULL;
 
563
  GtkWidget *vbox = NULL;
 
564
  GtkWidget *frame = NULL;
 
565
  GtkWidget *tree_view = NULL;
 
566
 
 
567
  GdkPixbuf *pixbuf = NULL;
 
568
  GtkTreeSelection *selection = NULL;
 
569
  GtkListStore *list_store = NULL;
 
570
  GtkCellRenderer *renderer = NULL;
 
571
  GtkTreeViewColumn *column = NULL;
 
572
  GtkWidget *label = NULL;
 
573
 
 
574
  bool found = false;
 
575
  bool close = false;
 
576
  int response = 0;
 
577
  int i = 0;
 
578
  gchar *message = NULL;
 
579
  gchar *short_status = NULL;
 
580
  gchar *long_status = NULL;
 
581
  
 
582
  // Current status
 
583
  short_status = gm_conf_get_string (PERSONAL_DATA_KEY "short_status");
 
584
  long_status = gm_conf_get_string (PERSONAL_DATA_KEY "long_status");
 
585
 
 
586
  // Build the dialog
 
587
  dialog = gtk_dialog_new_with_buttons (_("Custom Message"),
 
588
                                        self->priv->parent,
 
589
                                        (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
 
590
                                        GTK_STOCK_DELETE,
 
591
                                        GTK_RESPONSE_APPLY,
 
592
                                        GTK_STOCK_CLOSE,
 
593
                                        GTK_RESPONSE_CLOSE,
 
594
                                        NULL);
 
595
  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
 
596
  gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_DELETE);
 
597
 
 
598
  vbox = gtk_vbox_new (false, 0);
 
599
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
 
600
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, false, false, 2);
 
601
 
 
602
 
 
603
  label = gtk_label_new (_("Delete custom messages:"));
 
604
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
 
605
  gtk_box_pack_start (GTK_BOX (vbox), label, false, false, 2);
 
606
 
 
607
  list_store = gtk_list_store_new (3,
 
608
                                   GDK_TYPE_PIXBUF,
 
609
                                   G_TYPE_STRING,
 
610
                                   G_TYPE_INT);
 
611
  tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
 
612
  g_object_unref (list_store);
 
613
  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), false);
 
614
 
 
615
  column = gtk_tree_view_column_new ();
 
616
  renderer = gtk_cell_renderer_pixbuf_new ();
 
617
  gtk_tree_view_column_pack_start (column, renderer, FALSE);
 
618
  gtk_tree_view_column_set_attributes (column, renderer,
 
619
                                       "pixbuf", 0,
 
620
                                       NULL);
 
621
 
 
622
  renderer = gtk_cell_renderer_text_new ();
 
623
  gtk_tree_view_column_pack_start (column, renderer, FALSE);
 
624
  gtk_tree_view_column_set_attributes (column, renderer,
 
625
                                       "text", 1,
 
626
                                       NULL);
 
627
  gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
 
628
 
 
629
  frame = gtk_frame_new (NULL);
 
630
  gtk_container_add (GTK_CONTAINER (frame), tree_view);
 
631
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 2);
 
632
 
 
633
  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->priv->list_store), &iter)) {
 
634
 
 
635
    do {
 
636
 
 
637
      gtk_tree_model_get (GTK_TREE_MODEL (self->priv->list_store), &iter,
 
638
                          COL_MESSAGE_TYPE, &i, -1);
 
639
 
 
640
      if (i == TYPE_CUSTOM_ONLINE || i == TYPE_CUSTOM_AWAY || i == TYPE_CUSTOM_DND) {
 
641
 
 
642
        gtk_tree_model_get (GTK_TREE_MODEL (self->priv->list_store), &iter,
 
643
                            COL_ICON, &pixbuf, 
 
644
                            COL_MESSAGE, &message,
 
645
                            -1);
 
646
        gtk_list_store_append (GTK_LIST_STORE (list_store), &liter);
 
647
        gtk_list_store_set (GTK_LIST_STORE (list_store), &liter, 
 
648
                            COL_ICON, pixbuf, 
 
649
                            COL_MESSAGE, message,
 
650
                            COL_MESSAGE_TYPE, i,
 
651
                            -1);
 
652
        g_free (message);
 
653
        g_object_unref (pixbuf);
 
654
      }
 
655
 
 
656
    } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->priv->list_store), &iter));
 
657
  }
 
658
 
 
659
  // Select the first iter
 
660
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
 
661
  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter)) 
 
662
    gtk_tree_selection_select_iter (selection, &iter);
 
663
 
 
664
  gtk_widget_show_all (dialog);
 
665
  while (!close) {
 
666
    response = gtk_dialog_run (GTK_DIALOG (dialog));
 
667
 
 
668
    switch (response)
 
669
      {
 
670
        case GTK_RESPONSE_APPLY:
 
671
          if (gtk_tree_selection_get_selected (selection, NULL, &iter)) 
 
672
            gtk_list_store_remove (GTK_LIST_STORE (list_store), &iter);
 
673
          if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter)) 
 
674
            gtk_tree_selection_select_iter (selection, &iter);
 
675
          else
 
676
            close = true;
 
677
        break;
 
678
 
 
679
        case GTK_RESPONSE_CLOSE:
 
680
        default:
 
681
        close = true;
 
682
      }
 
683
  }
 
684
 
 
685
  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter)) {
 
686
 
 
687
    do {
 
688
 
 
689
      gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
 
690
                          1, &message,
 
691
                          2, &i, -1);
 
692
      if (long_status && message && !strcmp (long_status, message))
 
693
        found = true;
 
694
 
 
695
      conf_list[i - NUM_STATUS_TYPES - 1] = g_slist_append (conf_list[i - NUM_STATUS_TYPES - 1], g_strdup (message));
 
696
      g_free (message);
 
697
    } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), &iter));
 
698
  }
 
699
 
 
700
  for (int j = 0 ; j < 3 ; j++) {
 
701
    gm_conf_set_string_list (status_types_keys[j], conf_list[j]);
 
702
    g_slist_foreach (conf_list[j], (GFunc) g_free, NULL);
 
703
    g_slist_free (conf_list[j]);
 
704
  }
 
705
 
 
706
  if (!found) {
 
707
    // Reset current config
 
708
    gm_conf_set_string (PERSONAL_DATA_KEY "short_status", "online");
 
709
    gm_conf_set_string (PERSONAL_DATA_KEY "long_status", "");
 
710
  }
 
711
  else {
 
712
    status_menu_set_option (STATUS_MENU (self), short_status, long_status);
 
713
  }
 
714
 
 
715
  gtk_widget_destroy (dialog);
 
716
}
 
717
 
 
718
 
 
719
static void
 
720
status_menu_new_status_message_dialog_run (StatusMenu *self,
 
721
                                           int option)
 
722
{
 
723
  gchar *short_status = NULL;
 
724
  gchar *long_status = NULL;
 
725
 
 
726
  GSList *clist = NULL;
 
727
  GtkWidget *dialog = NULL;
 
728
  GtkWidget *label = NULL;
 
729
  GtkWidget *entry = NULL;
 
730
  GtkWidget *vbox = NULL;
 
731
  GtkWidget *hbox = NULL;
 
732
  GtkWidget *image = NULL;
 
733
 
 
734
  GdkPixbuf* icon = NULL;
 
735
 
 
736
  const char *message = NULL;
 
737
 
 
738
  short_status = gm_conf_get_string (PERSONAL_DATA_KEY "short_status");
 
739
  long_status = gm_conf_get_string (PERSONAL_DATA_KEY "long_status");
 
740
 
 
741
  dialog = gtk_dialog_new_with_buttons (_("Custom Message"),
 
742
                                        self->priv->parent,
 
743
                                        (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
 
744
                                        GTK_STOCK_OK,
 
745
                                        GTK_RESPONSE_ACCEPT,
 
746
                                        NULL);
 
747
  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
 
748
 
 
749
  vbox = gtk_vbox_new (false, 0);
 
750
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
 
751
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, false, false, 2);
 
752
 
 
753
  hbox = gtk_hbox_new (false, 2);
 
754
  icon = gtk_widget_render_icon (GTK_WIDGET (self),
 
755
                                 stock_status [option - NUM_STATUS_TYPES - 1],
 
756
                                 GTK_ICON_SIZE_MENU, NULL);
 
757
  gtk_window_set_icon (GTK_WINDOW (dialog), icon);
 
758
  image = gtk_image_new_from_pixbuf (icon);
 
759
  g_object_unref (icon);
 
760
  gtk_box_pack_start (GTK_BOX (hbox), image, false, false, 2);
 
761
 
 
762
  label = gtk_label_new (_("Define a custom message:"));
 
763
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
 
764
  gtk_box_pack_start (GTK_BOX (hbox), label, false, false, 2);
 
765
  gtk_box_pack_start (GTK_BOX (vbox), hbox, false, false, 2);
 
766
 
 
767
  entry = gtk_entry_new ();
 
768
  gtk_entry_set_activates_default (GTK_ENTRY (entry), true);
 
769
  gtk_box_pack_start (GTK_BOX (vbox), entry, false, false, 2);
 
770
 
 
771
  gtk_widget_show_all (dialog);
 
772
  switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
 
773
 
 
774
  case GTK_RESPONSE_ACCEPT:
 
775
    message = gtk_entry_get_text (GTK_ENTRY (entry));
 
776
    clist = gm_conf_get_string_list (status_types_keys[option - NUM_STATUS_TYPES - 1]);
 
777
    if (strcmp (message, "")) { 
 
778
      clist = g_slist_append (clist, g_strdup (message));
 
779
      gm_conf_set_string_list (status_types_keys[option - NUM_STATUS_TYPES - 1], clist);
 
780
      gm_conf_set_string (PERSONAL_DATA_KEY "long_status", message);
 
781
      gm_conf_set_string (PERSONAL_DATA_KEY "short_status", status_types_names[option - NUM_STATUS_TYPES - 1]);
 
782
    }
 
783
    else {
 
784
      status_menu_set_option (self, short_status, long_status);
 
785
    }
 
786
    g_slist_foreach (clist, (GFunc) g_free, NULL);
 
787
    g_slist_free (clist);
 
788
    break;
 
789
 
 
790
  default:
 
791
    status_menu_set_option (self, short_status, long_status);
 
792
    break;
 
793
  }
 
794
 
 
795
  gtk_widget_destroy (dialog);
 
796
 
 
797
  g_free (short_status);
 
798
  g_free (long_status);
 
799
}
 
800
 
 
801
 
 
802
/* 
 
803
 * GObject stuff
 
804
 */
 
805
static void
 
806
status_menu_class_init (gpointer g_class,
 
807
                        gpointer /*class_data*/)
 
808
{
 
809
  GObjectClass *gobject_class = NULL;
 
810
 
 
811
  parent_class = (GObjectClass *) g_type_class_peek_parent (g_class);
 
812
 
 
813
  gobject_class = (GObjectClass *) g_class;
 
814
  gobject_class->dispose = status_menu_dispose;
 
815
  gobject_class->finalize = status_menu_finalize;
 
816
}
 
817
 
 
818
 
 
819
static void
 
820
status_menu_init (StatusMenu *self)
 
821
{
 
822
  GtkCellRenderer *renderer = NULL;
 
823
  GSList *custom_status_array [NUM_STATUS_TYPES];
 
824
 
 
825
  self->priv = new StatusMenuPrivate;
 
826
 
 
827
  self->priv->parent = NULL;
 
828
  self->priv->list_store = gtk_list_store_new (NUM_COLUMNS,
 
829
                                               GDK_TYPE_PIXBUF,
 
830
                                               G_TYPE_STRING,
 
831
                                               G_TYPE_INT,
 
832
                                               G_TYPE_BOOLEAN);
 
833
 
 
834
  gtk_combo_box_set_model (GTK_COMBO_BOX (self),
 
835
                           GTK_TREE_MODEL (self->priv->list_store));
 
836
  g_object_unref (self->priv->list_store);
 
837
 
 
838
  renderer = gtk_cell_renderer_pixbuf_new ();
 
839
  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), renderer, FALSE);
 
840
  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (self), renderer,
 
841
                                  "pixbuf", COL_ICON, NULL);
 
842
 
 
843
  renderer = gtk_cell_renderer_text_new ();
 
844
  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), renderer, FALSE);
 
845
  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (self), renderer, "text", COL_MESSAGE, NULL);
 
846
  g_object_set (renderer, "width", 130, 
 
847
                "ellipsize-set", true, 
 
848
                "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 
849
 
 
850
  for (int i = 0 ; i < NUM_STATUS_TYPES ; i++)
 
851
    custom_status_array [i] = gm_conf_get_string_list (status_types_keys [i]);
 
852
 
 
853
  status_menu_populate (self, custom_status_array);
 
854
  
 
855
  for (int i = 0 ; i < NUM_STATUS_TYPES ; i++) {
 
856
    g_slist_foreach (custom_status_array [i], (GFunc) g_free, 0);
 
857
    g_slist_free (custom_status_array [i]);
 
858
  }
 
859
 
 
860
  gtk_combo_box_set_active (GTK_COMBO_BOX (self), 0);
 
861
 
 
862
  gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (self),
 
863
                                        (GtkTreeViewRowSeparatorFunc) status_menu_row_is_separator,
 
864
                                        NULL, NULL);
 
865
  gtk_container_set_border_width (GTK_CONTAINER (self), 0);
 
866
 
 
867
  g_signal_connect (G_OBJECT (self), "changed",
 
868
                    G_CALLBACK (status_menu_option_changed), self);
 
869
 
 
870
  gm_conf_notifier_add (PERSONAL_DATA_KEY "online_custom_status", 
 
871
                        status_menu_custom_messages_changed, self);
 
872
  gm_conf_notifier_add (PERSONAL_DATA_KEY "away_custom_status", 
 
873
                        status_menu_custom_messages_changed, self);
 
874
  gm_conf_notifier_add (PERSONAL_DATA_KEY "dnd_custom_status", 
 
875
                        status_menu_custom_messages_changed, self);
 
876
 
 
877
  gm_conf_notifier_add (PERSONAL_DATA_KEY "long_status", 
 
878
                        long_status_message_changed, self);
 
879
  gm_conf_notifier_add (PERSONAL_DATA_KEY "short_status", 
 
880
                        short_status_message_changed, self);
 
881
  gm_conf_notifier_trigger (PERSONAL_DATA_KEY "short_status");
 
882
}
 
883
 
 
884
 
 
885
static void
 
886
status_menu_dispose (GObject *obj)
 
887
{
 
888
  StatusMenu *self = NULL;
 
889
 
 
890
  self = STATUS_MENU (obj);
 
891
  delete self->priv;
 
892
 
 
893
  self->priv = NULL;
 
894
 
 
895
  // NULLify everything
 
896
  parent_class->dispose (obj);
 
897
}
 
898
 
 
899
 
 
900
static void
 
901
status_menu_finalize (GObject *obj)
 
902
{
 
903
  parent_class->finalize (obj);
 
904
}
 
905
 
 
906
 
 
907
GType
 
908
status_menu_get_type (void)
 
909
{
 
910
  static GType status_menu_type = 0;
 
911
 
 
912
  if (status_menu_type == 0) {
 
913
 
 
914
    static const GTypeInfo status_menu_info =
 
915
      {
 
916
        sizeof (StatusMenuClass),
 
917
        NULL,
 
918
        NULL,
 
919
        (GClassInitFunc) status_menu_class_init,
 
920
        NULL,
 
921
        NULL,
 
922
        sizeof (StatusMenu),
 
923
        0,
 
924
        (GInstanceInitFunc) status_menu_init,
 
925
        NULL
 
926
      };
 
927
 
 
928
    status_menu_type = g_type_register_static (GTK_TYPE_COMBO_BOX,
 
929
                                               "StatusMenu",
 
930
                                               &status_menu_info,
 
931
                                               (GTypeFlags) 0);
 
932
  }
 
933
 
 
934
  return status_menu_type;
 
935
}
 
936
 
 
937
 
 
938
GtkWidget *
 
939
status_menu_new ()
 
940
{
 
941
  return GTK_WIDGET (STATUS_MENU (g_object_new (STATUS_MENU_TYPE, NULL)));
 
942
}
 
943
 
 
944
 
 
945
void
 
946
status_menu_set_parent_window (StatusMenu *self,
 
947
                               GtkWindow *parent)
 
948
{
 
949
  self->priv->parent = parent;
 
950
}