]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-presence-chooser.c
702379a7cfeab8c6fa6ce586b14ed1c5e73cb085
[empathy.git] / libempathy-gtk / empathy-presence-chooser.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2005-2007 Imendio AB
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (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 GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Richard Hult <richard@imendio.com>
21  *          Martyn Russell <martyn@imendio.com>
22  *          Xavier Claessens <xclaesse@gmail.com>
23  */
24
25 #include "config.h"
26
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <glib/gi18n-lib.h>
31 #include <gtk/gtk.h>
32 #include <glade/glade.h>
33
34 #include <telepathy-glib/util.h>
35 #include <libmissioncontrol/mc-enum-types.h>
36
37 #include <libempathy/empathy-idle.h>
38 #include <libempathy/empathy-utils.h>
39 #include <libempathy/empathy-status-presets.h>
40
41 #include "empathy-ui-utils.h"
42 #include "empathy-images.h"
43 #include "empathy-presence-chooser.h"
44
45 /* Flashing delay for icons (milliseconds). */
46 #define FLASH_TIMEOUT 500
47
48 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyPresenceChooser)
49 typedef struct {
50         EmpathyIdle *idle;
51
52         gboolean     editing_status;
53         int          block_set_editing;
54         int          block_changed;
55
56         McPresence   state;
57
58         McPresence   flash_state_1;
59         McPresence   flash_state_2;
60         guint        flash_timeout_id;
61 } EmpathyPresenceChooserPriv;
62
63 typedef struct {
64         GtkWidget    *dialog;
65         GtkWidget    *checkbutton_save;
66         GtkWidget    *comboboxentry_message;
67         GtkWidget    *entry_message;
68         GtkWidget    *combobox_status;
69         GtkTreeModel *model_status;
70 } CustomMessageDialog;
71
72 enum {
73         COL_ICON,
74         COL_LABEL,
75         COL_PRESENCE,
76         COL_COUNT
77 };
78
79 static CustomMessageDialog *message_dialog = NULL;
80 /* States to be listed in the menu.
81  * Each state has a boolean telling if it can have custom message */
82 static guint states[] = {MC_PRESENCE_AVAILABLE, TRUE,
83                          MC_PRESENCE_DO_NOT_DISTURB, TRUE,
84                          MC_PRESENCE_AWAY, TRUE,
85                          MC_PRESENCE_HIDDEN, FALSE,
86                          MC_PRESENCE_OFFLINE, FALSE};
87
88 static void            presence_chooser_finalize               (GObject                    *object);
89 static void            presence_chooser_presence_changed_cb    (EmpathyPresenceChooser      *chooser);
90 static gboolean        presence_chooser_flash_timeout_cb       (EmpathyPresenceChooser      *chooser);
91 static void            presence_chooser_flash_start            (EmpathyPresenceChooser      *chooser,
92                                                                 McPresence                  state_1,
93                                                                 McPresence                  state_2);
94 static void            presence_chooser_flash_stop             (EmpathyPresenceChooser      *chooser,
95                                                                 McPresence                  state);
96 static void            presence_chooser_menu_add_item          (GtkWidget                  *menu,
97                                                                 const gchar                *str,
98                                                                 McPresence                  state);
99 static void            presence_chooser_noncustom_activate_cb  (GtkWidget                  *item,
100                                                                 gpointer                    user_data);
101 static void            presence_chooser_set_state              (McPresence                  state,
102                                                                 const gchar                *status);
103 static void            presence_chooser_custom_activate_cb     (GtkWidget                  *item,
104                                                                 gpointer                    user_data);
105 static void            presence_chooser_dialog_show            (void);
106
107 G_DEFINE_TYPE (EmpathyPresenceChooser, empathy_presence_chooser, GTK_TYPE_COMBO_BOX_ENTRY);
108
109 static void
110 empathy_presence_chooser_class_init (EmpathyPresenceChooserClass *klass)
111 {
112         GObjectClass *object_class = G_OBJECT_CLASS (klass);
113
114         object_class->finalize = presence_chooser_finalize;
115
116         g_type_class_add_private (object_class, sizeof (EmpathyPresenceChooserPriv));
117 }
118
119 enum
120 {
121         COL_STATE_ICON_NAME,
122         COL_STATE,
123         COL_STATUS_TEXT,
124         COL_DISPLAY_MARKUP,
125         COL_TYPE,
126         N_COLUMNS
127 };
128
129 enum
130 {
131         ENTRY_TYPE_BUILTIN,
132         ENTRY_TYPE_SAVED,
133         ENTRY_TYPE_CUSTOM,
134 };
135
136 static GtkTreeModel *
137 create_model (void)
138 {
139         GtkListStore *store = gtk_list_store_new (N_COLUMNS,
140                         G_TYPE_STRING,          /* COL_STATE_ICON_NAME */
141                         MC_TYPE_PRESENCE,       /* COL_STATE */
142                         G_TYPE_STRING,          /* COL_STATUS_TEXT */
143                         G_TYPE_STRING,          /* COL_DISPLAY_MARKUP */
144                         G_TYPE_INT);            /* COL_TYPE */
145         
146         GtkTreeIter iter;
147         
148         int i;
149         for (i = 0; i < G_N_ELEMENTS (states); i += 2) {
150                 GList       *list, *l;
151
152                 const char *status = empathy_presence_get_default_message (states[i]);
153                 const char *icon_name = empathy_icon_name_for_presence (states[i]);
154
155                 gtk_list_store_append (store, &iter);
156                 gtk_list_store_set (store, &iter,
157                                 COL_STATE_ICON_NAME, icon_name,
158                                 COL_STATE, states[i],
159                                 COL_STATUS_TEXT, status,
160                                 COL_DISPLAY_MARKUP, status,
161                                 COL_TYPE, ENTRY_TYPE_BUILTIN,
162                                 -1);
163
164                 if (states[i+1]) {
165                         /* Set custom messages if wanted */
166                         list = empathy_status_presets_get (states[i], 5);
167                         for (l = list; l; l = l->next) {
168                                 gtk_list_store_append (store, &iter);
169                                 gtk_list_store_set (store, &iter,
170                                                 COL_STATE_ICON_NAME, icon_name,
171                                                 COL_STATE, states[i],
172                                                 COL_STATUS_TEXT, l->data,
173                                                 COL_DISPLAY_MARKUP, l->data,
174                                                 COL_TYPE, ENTRY_TYPE_SAVED,
175                                                 -1);
176                         }
177                         g_list_free (list);
178                 
179                         gtk_list_store_append (store, &iter);
180                         gtk_list_store_set (store, &iter,
181                                         COL_STATE_ICON_NAME, icon_name,
182                                         COL_STATE, states[i],
183                                         COL_STATUS_TEXT, "",
184                                         COL_DISPLAY_MARKUP, "<i>Custom Message...</i>",
185                                         COL_TYPE, ENTRY_TYPE_CUSTOM,
186                                         -1);
187                 }
188
189         }
190
191         return GTK_TREE_MODEL (store);
192 }
193
194 static void
195 popup_shown_cb (GObject *self, GParamSpec *pspec, gpointer user_data)
196 {
197         gboolean shown;
198         g_object_get (self, "popup-shown", &shown, NULL);
199
200         if (!shown) return;
201
202         GtkTreeModel *model = create_model ();
203
204         gtk_combo_box_set_model (GTK_COMBO_BOX (self), GTK_TREE_MODEL (model));
205         
206         g_object_unref (model);
207 }
208
209 static void
210 set_status_editing (EmpathyPresenceChooser *self, gboolean editing)
211 {
212         EmpathyPresenceChooserPriv *priv = GET_PRIV (self);
213         GtkWidget *entry = gtk_bin_get_child (GTK_BIN (self));
214
215         if (priv->block_set_editing) return;
216
217         if (editing)
218         {
219                 priv->editing_status = TRUE;
220                 gtk_entry_set_icon_from_stock (GTK_ENTRY (entry),
221                                 GTK_ENTRY_ICON_SECONDARY,
222                                 GTK_STOCK_OK);
223                 gtk_entry_set_icon_tooltip_text (GTK_ENTRY (entry),
224                                 GTK_ENTRY_ICON_SECONDARY,
225                                 "Set status");
226                 gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
227                                 GTK_ENTRY_ICON_PRIMARY,
228                                 FALSE);
229         }
230         else
231         {
232                 gtk_entry_set_icon_from_stock (GTK_ENTRY (entry),
233                                 GTK_ENTRY_ICON_SECONDARY,
234                                 NULL);
235                 gtk_entry_set_icon_tooltip_text (GTK_ENTRY (entry),
236                                 GTK_ENTRY_ICON_SECONDARY,
237                                 NULL);
238                 gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
239                                 GTK_ENTRY_ICON_PRIMARY,
240                                 TRUE);
241
242                 // FIXME - move the focus somewhere
243
244                 priv->editing_status = FALSE;
245         }
246 }
247
248 static void
249 mc_set_custom_state (EmpathyPresenceChooser *self)
250 {
251         EmpathyPresenceChooserPriv *priv = GET_PRIV (self);
252         GtkWidget *entry = gtk_bin_get_child (GTK_BIN (self));
253
254         /* update the status with MC */
255         const char *status = gtk_entry_get_text (GTK_ENTRY (entry));
256         empathy_idle_set_presence (priv->idle, priv->state, status);
257 }
258
259 static void
260 ui_set_custom_state (EmpathyPresenceChooser *self,
261                            McPresence state,
262                            const char *status)
263 {
264         EmpathyPresenceChooserPriv *priv = GET_PRIV (self);
265         GtkWidget *entry = gtk_bin_get_child (GTK_BIN (self));
266         const char *icon_name;
267
268         priv->block_set_editing++;
269         priv->block_changed++;
270
271         icon_name = empathy_icon_name_for_presence (state);
272         gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
273                         GTK_ENTRY_ICON_PRIMARY,
274                         icon_name);
275         gtk_entry_set_text (GTK_ENTRY (entry), status);
276
277         priv->block_changed--;
278         priv->block_set_editing--;
279 }
280
281 static void
282 entry_icon_release_cb (EmpathyPresenceChooser   *self,
283                        GtkEntryIconPosition      icon_pos,
284                        GdkEvent         *event,
285                        GtkEntry         *entry)
286 {
287         set_status_editing (self, FALSE);
288         mc_set_custom_state (self);
289 }
290
291 static void
292 entry_activate_cb (EmpathyPresenceChooser       *self,
293                    GtkEntry                     *entry)
294 {
295         set_status_editing (self, FALSE);
296         mc_set_custom_state (self);
297 }
298
299 static void
300 changed_cb (GtkComboBox *self, gpointer user_data)
301 {
302         EmpathyPresenceChooserPriv *priv = GET_PRIV (self);
303
304         if (priv->block_changed) return;
305
306         GtkTreeIter iter;
307         char *icon_name;
308         int type = -1;
309
310         GtkTreeModel *model = gtk_combo_box_get_model (self);
311         if (!gtk_combo_box_get_active_iter (self, &iter))
312         {
313                 /* the combo is being edited to a custom entry */
314                 if (!priv->editing_status)
315                 {
316                         set_status_editing (EMPATHY_PRESENCE_CHOOSER (self), TRUE);
317                 }
318                 return;
319         }
320
321         gtk_tree_model_get (model, &iter,
322                         COL_STATE_ICON_NAME, &icon_name,
323                         COL_STATE, &priv->state,
324                         COL_TYPE, &type,
325                         -1);
326
327         GtkWidget *entry = gtk_bin_get_child (GTK_BIN (self));
328         gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
329                         GTK_ENTRY_ICON_PRIMARY,
330                         icon_name);
331
332         if (type == ENTRY_TYPE_CUSTOM)
333         {
334                 /* grab the focus */
335                 gtk_widget_grab_focus (entry);
336                 set_status_editing (EMPATHY_PRESENCE_CHOOSER (self), TRUE);
337         }
338         else
339         {
340                 char *status;
341                 /* just in case we were setting a new status when
342                  * things were changed */
343                 set_status_editing (EMPATHY_PRESENCE_CHOOSER (self), FALSE);
344
345                 gtk_tree_model_get (model, &iter,
346                                 COL_STATUS_TEXT, &status,
347                                 -1);
348
349                 empathy_idle_set_presence (priv->idle, priv->state, status);
350
351                 g_free (status);
352         }
353
354         g_free (icon_name);
355 }
356
357 static void
358 empathy_presence_chooser_init (EmpathyPresenceChooser *chooser)
359 {
360         EmpathyPresenceChooserPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (chooser,
361                 EMPATHY_TYPE_PRESENCE_CHOOSER, EmpathyPresenceChooserPriv);
362
363         chooser->priv = priv;
364         
365         GtkTreeModel *model = create_model ();
366
367         gtk_combo_box_set_model (GTK_COMBO_BOX (chooser), GTK_TREE_MODEL (model));
368         gtk_combo_box_entry_set_text_column (GTK_COMBO_BOX_ENTRY (chooser), COL_STATUS_TEXT);
369         
370         GtkWidget *entry = gtk_bin_get_child (GTK_BIN (chooser));
371         gtk_entry_set_icon_activatable (GTK_ENTRY (entry),
372                         GTK_ENTRY_ICON_PRIMARY, FALSE);
373         g_signal_connect_object (entry, "icon-release",
374                         G_CALLBACK (entry_icon_release_cb), chooser,
375                         G_CONNECT_SWAPPED);
376         g_signal_connect_object (entry, "activate",
377                         G_CALLBACK (entry_activate_cb), chooser,
378                         G_CONNECT_SWAPPED);
379         // FIXME - should this also happen when the user presses TAB ?
380
381         GtkCellRenderer *renderer;
382         gtk_cell_layout_clear (GTK_CELL_LAYOUT (chooser));
383
384         renderer = gtk_cell_renderer_pixbuf_new ();
385         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (chooser), renderer, FALSE);
386         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (chooser), renderer,
387                         "icon-name", COL_STATE_ICON_NAME,
388                         NULL);
389         g_object_set (renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL);
390
391         renderer = gtk_cell_renderer_text_new ();
392         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (chooser), renderer, TRUE);
393         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (chooser), renderer,
394                         "markup", COL_DISPLAY_MARKUP,
395                         NULL);
396
397         g_object_unref (model);
398
399         g_signal_connect (chooser, "notify::popup-shown",
400                         G_CALLBACK (popup_shown_cb), NULL);
401         g_signal_connect (chooser, "changed",
402                         G_CALLBACK (changed_cb), NULL);
403
404         priv->idle = empathy_idle_dup_singleton ();
405         presence_chooser_presence_changed_cb (chooser);
406         g_signal_connect_swapped (priv->idle, "notify",
407                                   G_CALLBACK (presence_chooser_presence_changed_cb),
408                                   chooser);
409 }
410
411 static void
412 presence_chooser_finalize (GObject *object)
413 {
414         EmpathyPresenceChooserPriv *priv;
415
416         priv = GET_PRIV (object);
417
418         if (priv->flash_timeout_id) {
419                 g_source_remove (priv->flash_timeout_id);
420         }
421
422         g_signal_handlers_disconnect_by_func (priv->idle,
423                                               presence_chooser_presence_changed_cb,
424                                               object);
425         g_object_unref (priv->idle);
426
427         G_OBJECT_CLASS (empathy_presence_chooser_parent_class)->finalize (object);
428 }
429
430 GtkWidget *
431 empathy_presence_chooser_new (void)
432 {
433         GtkWidget *chooser;
434
435         chooser = g_object_new (EMPATHY_TYPE_PRESENCE_CHOOSER, NULL);
436
437         return chooser;
438 }
439
440 static void
441 presence_chooser_presence_changed_cb (EmpathyPresenceChooser *chooser)
442 {
443         EmpathyPresenceChooserPriv *priv;
444         McPresence                 state;
445         McPresence                 flash_state;
446         const gchar               *status;
447
448         priv = GET_PRIV (chooser);
449
450         state = empathy_idle_get_state (priv->idle);
451         status = empathy_idle_get_status (priv->idle);
452         flash_state = empathy_idle_get_flash_state (priv->idle);
453
454         /* look through the model and attempt to find a matching state */
455         GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser));
456         GtkTreeIter iter;
457         gboolean valid, match_state = FALSE, match = FALSE;
458         for (valid = gtk_tree_model_get_iter_first (model, &iter);
459              valid;
460              valid = gtk_tree_model_iter_next (model, &iter))
461         {
462                 int m_type;
463                 McPresence m_state;
464                 char *m_status;
465
466                 gtk_tree_model_get (model, &iter,
467                                 COL_STATE, &m_state,
468                                 COL_TYPE, &m_type,
469                                 -1);
470
471                 if (m_type == ENTRY_TYPE_CUSTOM)
472                 {
473                         continue;
474                 }
475                 else if (!match_state && state == m_state)
476                 {
477                         /* we are now in the section that can contain our
478                          * match */
479                         match_state = TRUE;
480                 }
481                 else if (match_state && state != m_state)
482                 {
483                         /* we have passed the section that can contain our
484                          * match */
485                         break;
486                 }
487
488                 gtk_tree_model_get (model, &iter,
489                                 COL_STATUS_TEXT, &m_status,
490                                 -1);
491
492                 match = !strcmp (status, m_status);
493
494                 g_free (m_status);
495
496                 if (match) break;
497
498         }
499
500         if (match)
501         {
502                 priv->block_changed++;
503                 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (chooser), &iter);
504                 priv->block_changed--;
505         }
506         else
507         {
508                 // FIXME - do we insert the match into the menu?
509                 ui_set_custom_state (chooser, state, status);
510         }
511
512         if (flash_state != MC_PRESENCE_UNSET) {
513                 presence_chooser_flash_start (chooser, state, flash_state);
514         } else {
515                 presence_chooser_flash_stop (chooser, state);
516         }
517 }
518
519 static gboolean
520 presence_chooser_flash_timeout_cb (EmpathyPresenceChooser *chooser)
521 {
522         EmpathyPresenceChooserPriv *priv;
523         McPresence                 state;
524         static gboolean            on = FALSE;
525
526         priv = GET_PRIV (chooser);
527
528         if (on) {
529                 state = priv->flash_state_1;
530         } else {
531                 state = priv->flash_state_2;
532         }
533
534         GtkWidget *entry = gtk_bin_get_child (GTK_BIN (chooser));
535         gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
536                         GTK_ENTRY_ICON_PRIMARY,
537                         empathy_icon_name_for_presence (state));
538
539         on = !on;
540
541         return TRUE;
542 }
543
544 static void
545 presence_chooser_flash_start (EmpathyPresenceChooser *chooser,
546                               McPresence             state_1,
547                               McPresence             state_2)
548 {
549         EmpathyPresenceChooserPriv *priv;
550
551         g_return_if_fail (EMPATHY_IS_PRESENCE_CHOOSER (chooser));
552
553         priv = GET_PRIV (chooser);
554
555         priv->flash_state_1 = state_1;
556         priv->flash_state_2 = state_2;
557
558         if (!priv->flash_timeout_id) {
559                 priv->flash_timeout_id = g_timeout_add (FLASH_TIMEOUT,
560                                                         (GSourceFunc) presence_chooser_flash_timeout_cb,
561                                                         chooser);
562         }
563 }
564
565 static void
566 presence_chooser_flash_stop (EmpathyPresenceChooser *chooser,
567                              McPresence             state)
568 {
569         EmpathyPresenceChooserPriv *priv;
570
571         g_return_if_fail (EMPATHY_IS_PRESENCE_CHOOSER (chooser));
572
573         priv = GET_PRIV (chooser);
574
575         if (priv->flash_timeout_id) {
576                 g_source_remove (priv->flash_timeout_id);
577                 priv->flash_timeout_id = 0;
578         }
579         GtkWidget *entry = gtk_bin_get_child (GTK_BIN (chooser));
580         
581         gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
582                         GTK_ENTRY_ICON_PRIMARY,
583                         empathy_icon_name_for_presence (state));
584
585         // FIXME - what does this do?
586         // priv->last_state = state;
587 }
588
589 GtkWidget *
590 empathy_presence_chooser_create_menu (void)
591 {
592         const gchar *status;
593         GtkWidget   *menu;
594         GtkWidget   *item;
595         GtkWidget   *image;
596         guint        i;
597
598         menu = gtk_menu_new ();
599
600         for (i = 0; i < G_N_ELEMENTS (states); i += 2) {
601                 GList       *list, *l;
602
603                 status = empathy_presence_get_default_message (states[i]);
604                 presence_chooser_menu_add_item (menu,
605                                                 status,
606                                                 states[i]);
607
608                 if (states[i+1]) {
609                         /* Set custom messages if wanted */
610                         list = empathy_status_presets_get (states[i], 5);
611                         for (l = list; l; l = l->next) {
612                                 presence_chooser_menu_add_item (menu,
613                                                                 l->data,
614                                                                 states[i]);
615                         }
616                         g_list_free (list);
617                 }
618
619         }
620
621         /* Separator. */
622         item = gtk_menu_item_new ();
623         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
624         gtk_widget_show (item);
625
626         /* Custom messages */
627         item = gtk_image_menu_item_new_with_label (_("Custom messages..."));
628         image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
629         gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
630         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
631         gtk_widget_show (image);
632         gtk_widget_show (item);
633
634         g_signal_connect (item,
635                           "activate",
636                           G_CALLBACK (presence_chooser_custom_activate_cb),
637                           NULL);
638
639         return menu;
640 }
641
642 static void
643 presence_chooser_menu_add_item (GtkWidget   *menu,
644                                 const gchar *str,
645                                 McPresence   state)
646 {
647         GtkWidget   *item;
648         GtkWidget   *image;
649         const gchar *icon_name;
650
651         item = gtk_image_menu_item_new_with_label (str);
652         icon_name = empathy_icon_name_for_presence (state);
653
654         g_signal_connect (item, "activate",
655                           G_CALLBACK (presence_chooser_noncustom_activate_cb),
656                           NULL);
657
658         image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
659         gtk_widget_show (image);
660
661         gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
662         gtk_widget_show (item);
663
664         g_object_set_data_full (G_OBJECT (item),
665                                 "status", g_strdup (str),
666                                 (GDestroyNotify) g_free);
667
668         g_object_set_data (G_OBJECT (item), "state", GINT_TO_POINTER (state));
669
670         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
671 }
672
673 static void
674 presence_chooser_noncustom_activate_cb (GtkWidget *item,
675                                         gpointer   user_data)
676 {
677         McPresence   state;
678         const gchar *status;
679
680         status = g_object_get_data (G_OBJECT (item), "status");
681         state = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "state"));
682
683         presence_chooser_set_state (state, status);
684 }
685
686 static void
687 presence_chooser_set_state (McPresence   state,
688                             const gchar *status)
689 {
690         EmpathyIdle *idle;
691
692         idle = empathy_idle_dup_singleton ();
693         empathy_idle_set_presence (idle, state, status);
694         g_object_unref (idle);
695 }
696
697 static void
698 presence_chooser_custom_activate_cb (GtkWidget *item,
699                                      gpointer   user_data)
700 {
701         presence_chooser_dialog_show ();
702 }
703
704 static McPresence
705 presence_chooser_dialog_get_selected (CustomMessageDialog *dialog)
706 {
707         GtkTreeModel *model;
708         GtkTreeIter   iter;
709         McPresence    presence = LAST_MC_PRESENCE;
710
711         model = gtk_combo_box_get_model (GTK_COMBO_BOX (dialog->combobox_status));
712         if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (dialog->combobox_status), &iter)) {
713                 gtk_tree_model_get (model, &iter,
714                                     COL_PRESENCE, &presence,
715                                     -1);
716         }
717
718         return presence;
719 }
720
721 static void
722 presence_chooser_dialog_status_changed_cb (GtkWidget           *widget,
723                                            CustomMessageDialog *dialog)
724 {
725         GtkListStore *store;
726         GtkTreeIter   iter;
727         McPresence    presence = LAST_MC_PRESENCE;
728         GList        *messages, *l;
729
730         presence = presence_chooser_dialog_get_selected (dialog);
731
732         store = gtk_list_store_new (1, G_TYPE_STRING);
733         messages = empathy_status_presets_get (presence, -1);
734         for (l = messages; l; l = l->next) {
735                 gtk_list_store_append (store, &iter);
736                 gtk_list_store_set (store, &iter, 0, l->data, -1);
737         }
738
739         gtk_entry_set_text (GTK_ENTRY (dialog->entry_message),
740                             messages ? messages->data : "");
741
742         g_list_free (messages);
743
744         gtk_combo_box_set_model (GTK_COMBO_BOX (dialog->comboboxentry_message),
745                                  GTK_TREE_MODEL (store));
746
747         g_object_unref (store);
748 }
749
750 static void
751 presence_chooser_dialog_message_changed_cb (GtkWidget           *widget,
752                                             CustomMessageDialog *dialog)
753 {
754         McPresence   presence;
755         GList       *messages, *l;
756         const gchar *text;
757         gboolean     found = FALSE;
758
759         presence = presence_chooser_dialog_get_selected (dialog);
760         text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_message));
761
762         messages = empathy_status_presets_get (presence, -1);
763         for (l = messages; l; l = l->next) {
764                 if (!tp_strdiff (text, l->data)) {
765                         found = TRUE;
766                         break;
767                 }
768         }
769         g_list_free (messages);
770
771         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->checkbutton_save),
772                                       found);
773 }
774
775 static void
776 presence_chooser_dialog_save_toggled_cb (GtkWidget           *widget,
777                                          CustomMessageDialog *dialog)
778 {
779         gboolean     active;
780         McPresence   state;
781         const gchar *text;
782
783         active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->checkbutton_save));
784         state = presence_chooser_dialog_get_selected (dialog);
785         text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_message));
786
787         if (active) {
788                 empathy_status_presets_set_last (state, text);
789         } else {
790                 empathy_status_presets_remove (state, text);
791         }
792 }
793
794 static void
795 presence_chooser_dialog_setup (CustomMessageDialog *dialog)
796 {
797         GtkListStore    *store;
798         GtkCellRenderer *renderer;
799         GtkTreeIter      iter;
800         guint            i;
801
802         store = gtk_list_store_new (COL_COUNT,
803                                     G_TYPE_STRING,     /* Icon name */
804                                     G_TYPE_STRING,     /* Label     */
805                                     MC_TYPE_PRESENCE); /* Presence   */
806         gtk_combo_box_set_model (GTK_COMBO_BOX (dialog->combobox_status),
807                                  GTK_TREE_MODEL (store));
808
809         renderer = gtk_cell_renderer_pixbuf_new ();
810         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (dialog->combobox_status), renderer, FALSE);
811         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (dialog->combobox_status), renderer,
812                                         "icon-name", COL_ICON,
813                                         NULL);
814         g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL);
815
816         renderer = gtk_cell_renderer_text_new ();
817         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (dialog->combobox_status), renderer, TRUE);
818         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (dialog->combobox_status), renderer,
819                                         "text", COL_LABEL,
820                                         NULL);
821
822         for (i = 0; i < G_N_ELEMENTS (states); i += 2) {
823                 if (!states[i+1]) {
824                         continue;
825                 }
826
827                 gtk_list_store_append (store, &iter);
828                 gtk_list_store_set (store, &iter,
829                                     COL_ICON, empathy_icon_name_for_presence (states[i]),
830                                     COL_LABEL, empathy_presence_get_default_message (states[i]),
831                                     COL_PRESENCE, states[i],
832                                     -1);
833         }
834
835         gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->combobox_status), 0);
836 }
837
838 static void
839 presence_chooser_dialog_response_cb (GtkWidget           *widget,
840                                      gint                 response,
841                                      CustomMessageDialog *dialog)
842 {
843         if (response == GTK_RESPONSE_APPLY) {
844                 McPresence   state;
845                 const gchar *text;
846
847                 state = presence_chooser_dialog_get_selected (dialog);
848                 text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_message));
849
850                 presence_chooser_set_state (state, text);
851         }
852
853         gtk_widget_destroy (widget);
854 }
855
856 static void
857 presence_chooser_dialog_destroy_cb (GtkWidget           *widget,
858                                     CustomMessageDialog *dialog)
859 {
860
861         g_free (dialog);
862         message_dialog = NULL;
863 }
864
865 static void
866 presence_chooser_dialog_show (void)
867 {
868         GladeXML *glade;
869         gchar    *filename;
870
871         if (message_dialog) {
872                 gtk_window_present (GTK_WINDOW (message_dialog->dialog));
873                 return;
874         }
875
876         message_dialog = g_new0 (CustomMessageDialog, 1);
877
878         filename = empathy_file_lookup ("empathy-presence-chooser.glade",
879                                         "libempathy-gtk");
880         glade = empathy_glade_get_file (filename,
881                                        "custom_message_dialog",
882                                        NULL,
883                                        "custom_message_dialog", &message_dialog->dialog,
884                                        "checkbutton_save", &message_dialog->checkbutton_save,
885                                        "comboboxentry_message", &message_dialog->comboboxentry_message,
886                                        "combobox_status", &message_dialog->combobox_status,
887                                        NULL);
888         g_free (filename);
889
890         empathy_glade_connect (glade,
891                                message_dialog,
892                                "custom_message_dialog", "destroy", presence_chooser_dialog_destroy_cb,
893                                "custom_message_dialog", "response", presence_chooser_dialog_response_cb,
894                                "combobox_status", "changed", presence_chooser_dialog_status_changed_cb,
895                                "checkbutton_save", "toggled", presence_chooser_dialog_save_toggled_cb,
896                                NULL);
897
898         g_object_unref (glade);
899
900         /* Setup the message combobox */
901         message_dialog->entry_message = GTK_BIN (message_dialog->comboboxentry_message)->child;
902         gtk_entry_set_activates_default (GTK_ENTRY (message_dialog->entry_message), TRUE);
903         gtk_entry_set_width_chars (GTK_ENTRY (message_dialog->entry_message), 25);
904         g_signal_connect (message_dialog->entry_message, "changed",
905                           G_CALLBACK (presence_chooser_dialog_message_changed_cb),
906                           message_dialog);
907
908         presence_chooser_dialog_setup (message_dialog);
909
910         gtk_combo_box_entry_set_text_column (GTK_COMBO_BOX_ENTRY (message_dialog->comboboxentry_message), 0);
911
912         /* FIXME: Set transian for a window ? */
913
914         gtk_widget_show_all (message_dialog->dialog);
915 }
916