switch new message dialog to use an header bar
[empathy.git] / libempathy-gtk / empathy-new-message-dialog.c
1 /*
2  * Copyright (C) 2007-2008 Collabora Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Authors: Xavier Claessens <xclaesse@gmail.com>
19  */
20
21 #include "config.h"
22 #include "empathy-new-message-dialog.h"
23
24 #include <glib/gi18n-lib.h>
25
26 #include "empathy-request-util.h"
27 #include "empathy-contact-chooser.h"
28 #include "empathy-ui-utils.h"
29 #include "empathy-images.h"
30
31 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
32 #include "empathy-debug.h"
33
34 static EmpathyNewMessageDialog *dialog_singleton = NULL;
35
36 G_DEFINE_TYPE(EmpathyNewMessageDialog, empathy_new_message_dialog,
37     GTK_TYPE_DIALOG)
38
39 struct _EmpathyNewMessageDialogPriv {
40   GtkWidget *chooser;
41   GtkWidget *button_chat;
42   GtkWidget *button_sms;
43 };
44
45 /**
46  * SECTION:empathy-new-message-dialog
47  * @title: EmpathyNewMessageDialog
48  * @short_description: A dialog to show a new message
49  * @include: libempathy-gtk/empathy-new-message-dialog.h
50  *
51  * #EmpathyNewMessageDialog is a dialog which allows a text chat
52  * to be started with any contact on any enabled account.
53  */
54
55 enum
56 {
57   EMP_NEW_MESSAGE_TEXT,
58   EMP_NEW_MESSAGE_SMS,
59 };
60
61 static const gchar *
62 get_error_display_message (GError *error)
63 {
64   if (error->domain != TP_ERROR)
65     goto out;
66
67   switch (error->code)
68     {
69       case TP_ERROR_NETWORK_ERROR:
70         return _("Network error");
71       case TP_ERROR_OFFLINE:
72         return _("The contact is offline");
73       case TP_ERROR_INVALID_HANDLE:
74         return _("The specified contact is either invalid or unknown");
75       case TP_ERROR_NOT_CAPABLE:
76         return _("The contact does not support this kind of conversation");
77       case TP_ERROR_NOT_IMPLEMENTED:
78         return _("The requested functionality is not implemented "
79                  "for this protocol");
80       case TP_ERROR_INVALID_ARGUMENT:
81         /* Not very user friendly to say 'invalid arguments' */
82         break;
83       case TP_ERROR_NOT_AVAILABLE:
84         return _("Could not start a conversation with the given contact");
85       case TP_ERROR_CHANNEL_BANNED:
86         return _("You are banned from this channel");
87       case TP_ERROR_CHANNEL_FULL:
88         return _("This channel is full");
89       case TP_ERROR_CHANNEL_INVITE_ONLY:
90         return _("You must be invited to join this channel");
91       case TP_ERROR_DISCONNECTED:
92         return _("Can't proceed while disconnected");
93       case TP_ERROR_PERMISSION_DENIED:
94         return _("Permission denied");
95       default:
96         DEBUG ("Unhandled error code: %d", error->code);
97     }
98
99 out:
100   return _("There was an error starting the conversation");
101 }
102
103 static void
104 show_chat_error (GError *error,
105     GtkWindow *parent)
106 {
107   GtkWidget *dialog;
108
109   dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL,
110       GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
111       "%s",
112       get_error_display_message (error));
113
114   g_signal_connect_swapped (dialog, "response",
115       G_CALLBACK (gtk_widget_destroy),
116       dialog);
117
118   gtk_widget_show (dialog);
119 }
120
121 static void
122 ensure_text_channel_cb (GObject *source,
123     GAsyncResult *result,
124     gpointer user_data)
125 {
126   GError *error = NULL;
127
128   if (!tp_account_channel_request_ensure_channel_finish (
129         TP_ACCOUNT_CHANNEL_REQUEST (source), result, &error))
130     {
131       DEBUG ("Failed to ensure text channel: %s", error->message);
132       show_chat_error (error, user_data);
133       g_error_free (error);
134     }
135 }
136
137 static void
138 empathy_new_message_dialog_response (GtkDialog *dialog,
139     int response_id)
140 {
141   EmpathyNewMessageDialog *self = (EmpathyNewMessageDialog *) dialog;
142   FolksIndividual *individual = NULL;
143   EmpathyContact *contact = NULL;
144
145   if (response_id < EMP_NEW_MESSAGE_TEXT)
146     goto out;
147
148   individual = empathy_contact_chooser_dup_selected (
149       EMPATHY_CONTACT_CHOOSER (self->priv->chooser));
150   if (individual == NULL)
151     goto out;
152
153   switch (response_id)
154     {
155       case EMP_NEW_MESSAGE_TEXT:
156         contact = empathy_contact_dup_best_for_action (individual,
157             EMPATHY_ACTION_CHAT);
158         g_return_if_fail (contact != NULL);
159
160         empathy_chat_with_contact_id (empathy_contact_get_account (contact),
161             empathy_contact_get_id (contact),
162             empathy_get_current_action_time (),
163             ensure_text_channel_cb,
164             gtk_widget_get_parent_window (GTK_WIDGET (dialog)));
165         break;
166
167       case EMP_NEW_MESSAGE_SMS:
168         contact = empathy_contact_dup_best_for_action (individual,
169             EMPATHY_ACTION_SMS);
170         g_return_if_fail (contact != NULL);
171
172         empathy_sms_contact_id (empathy_contact_get_account (contact),
173             empathy_contact_get_id (contact),
174             empathy_get_current_action_time (),
175             ensure_text_channel_cb,
176             gtk_widget_get_parent_window (GTK_WIDGET (dialog)));
177         break;
178
179       default:
180         g_warn_if_reached ();
181     }
182
183 out:
184   tp_clear_object (&individual);
185   tp_clear_object (&contact);
186   gtk_widget_destroy (GTK_WIDGET (dialog));
187 }
188
189 static GObject *
190 empathy_new_message_dialog_constructor (GType type,
191     guint n_props,
192     GObjectConstructParam *props)
193 {
194   GObject *retval;
195
196   if (dialog_singleton)
197     {
198       retval = G_OBJECT (dialog_singleton);
199       g_object_ref (retval);
200     }
201   else
202     {
203       retval = G_OBJECT_CLASS (
204       empathy_new_message_dialog_parent_class)->constructor (type,
205         n_props, props);
206
207       dialog_singleton = EMPATHY_NEW_MESSAGE_DIALOG (retval);
208       g_object_add_weak_pointer (retval, (gpointer) &dialog_singleton);
209     }
210
211   return retval;
212 }
213
214 static gboolean
215 individual_supports_action (FolksIndividual *individual,
216     EmpathyActionType action)
217 {
218   EmpathyContact *contact;
219
220   contact = empathy_contact_dup_best_for_action (individual, action);
221   if (contact == NULL)
222     return FALSE;
223
224   g_object_unref (contact);
225   return TRUE;
226 }
227
228 static gboolean
229 filter_individual (EmpathyContactChooser *chooser,
230     FolksIndividual *individual,
231     gboolean is_online,
232     gboolean searching,
233     gpointer user_data)
234 {
235   return individual_supports_action (individual, EMPATHY_ACTION_CHAT) ||
236     individual_supports_action (individual, EMPATHY_ACTION_SMS);
237 }
238
239 static void
240 selection_changed_cb (GtkWidget *chooser,
241     FolksIndividual *selected,
242     EmpathyNewMessageDialog *self)
243 {
244   gboolean can_chat, can_sms;
245
246   if (selected == NULL)
247     {
248       can_chat = can_sms = FALSE;
249     }
250   else
251     {
252       can_chat = individual_supports_action (selected, EMPATHY_ACTION_CHAT);
253       can_sms = individual_supports_action (selected, EMPATHY_ACTION_SMS);
254     }
255
256   gtk_widget_set_sensitive (self->priv->button_chat, can_chat);
257 #if 0
258   gtk_widget_set_sensitive (self->priv->button_sms, can_sms);
259 #endif
260 }
261
262 static void
263 selection_activate_cb (GtkWidget *chooser,
264     EmpathyNewMessageDialog *self)
265 {
266   gtk_dialog_response (GTK_DIALOG (self), EMP_NEW_MESSAGE_TEXT);
267 }
268
269 static void
270 empathy_new_message_dialog_init (EmpathyNewMessageDialog *self)
271 {
272   GtkWidget *image;
273   GtkWidget *content;
274
275   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
276       EMPATHY_TYPE_NEW_MESSAGE_DIALOG, EmpathyNewMessageDialogPriv);
277
278   content = gtk_dialog_get_content_area (GTK_DIALOG (self));
279
280   /* contact chooser */
281   self->priv->chooser = empathy_contact_chooser_new ();
282
283   empathy_contact_chooser_set_filter_func (
284       EMPATHY_CONTACT_CHOOSER (self->priv->chooser), filter_individual, self);
285
286   gtk_box_pack_start (GTK_BOX (content), self->priv->chooser, TRUE, TRUE, 0);
287   gtk_widget_show (self->priv->chooser);
288
289   g_signal_connect (self->priv->chooser, "selection-changed",
290       G_CALLBACK (selection_changed_cb), self);
291   g_signal_connect (self->priv->chooser, "activate",
292       G_CALLBACK (selection_activate_cb), self);
293
294   /* close button */
295   gtk_dialog_add_button (GTK_DIALOG (self),
296       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
297
298 #if 0
299   /* add SMS button */
300   self->priv->button_sms = gtk_button_new_with_mnemonic (_("_SMS"));
301   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_SMS,
302       GTK_ICON_SIZE_BUTTON);
303   gtk_button_set_image (GTK_BUTTON (self->priv->button_sms), image);
304 #endif
305
306   /* add chat button */
307   self->priv->button_chat = gtk_button_new_with_mnemonic (_("_Done"));
308   image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_NEW_MESSAGE,
309       GTK_ICON_SIZE_BUTTON);
310   gtk_button_set_image (GTK_BUTTON (self->priv->button_chat), image);
311
312 #if 0
313   gtk_dialog_add_action_widget (GTK_DIALOG (self), self->priv->button_sms,
314       EMP_NEW_MESSAGE_SMS);
315   gtk_widget_show (self->priv->button_sms);
316 #endif
317
318   gtk_dialog_add_action_widget (GTK_DIALOG (self), self->priv->button_chat,
319       EMP_NEW_MESSAGE_TEXT);
320   gtk_widget_show (self->priv->button_chat);
321
322   /* Tweak the dialog */
323   gtk_window_set_title (GTK_WINDOW (self), _("New Conversation"));
324   gtk_window_set_role (GTK_WINDOW (self), "new_message");
325
326   /* Set a default height so a few contacts are displayed */
327   gtk_window_set_default_size (GTK_WINDOW (self), -1, 600);
328
329   gtk_widget_set_sensitive (self->priv->button_chat, FALSE);
330 #if 0
331   gtk_widget_set_sensitive (self->priv->button_sms, FALSE);
332 #endif
333 }
334
335 static void
336 empathy_new_message_dialog_class_init (
337   EmpathyNewMessageDialogClass *class)
338 {
339   GObjectClass *object_class = G_OBJECT_CLASS (class);
340   GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (class);
341
342   object_class->constructor = empathy_new_message_dialog_constructor;
343
344   dialog_class->response = empathy_new_message_dialog_response;
345
346   g_type_class_add_private (class, sizeof (EmpathyNewMessageDialogPriv));
347 }
348
349 /**
350  * empathy_new_message_dialog_new:
351  * @parent: parent #GtkWindow of the dialog
352  *
353  * Create a new #EmpathyNewMessageDialog it.
354  *
355  * Return value: the new #EmpathyNewMessageDialog
356  */
357 GtkWidget *
358 empathy_new_message_dialog_show (GtkWindow *parent)
359 {
360   GtkWidget *dialog;
361
362   dialog = g_object_new (EMPATHY_TYPE_NEW_MESSAGE_DIALOG,
363                   "use-header-bar", 1,
364                   NULL);
365
366   if (parent)
367     {
368       gtk_window_set_transient_for (GTK_WINDOW (dialog),
369           GTK_WINDOW (parent));
370     }
371
372   gtk_widget_show (dialog);
373   return dialog;
374 }