pidgin/gtksavedstatuses.c

changeset 41416
253e831b0a1e
parent 41314
0dc72eacd8bf
child 41947
7b3312d0760c
equal deleted inserted replaced
41414:b76bc2b4d7cc 41416:253e831b0a1e
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
20 */ 20 */
21 21
22 #include <glib/gi18n-lib.h> 22 #include <glib/gi18n-lib.h>
23 23
24 #include <talkatu.h> 24 #include <gtk/gtk.h>
25 25
26 #include <purple.h> 26 #include <purple.h>
27 27
28 #include "gtkblist.h"
29 #include "gtksavedstatuses.h" 28 #include "gtksavedstatuses.h"
30 #include "gtkutils.h"
31 #include "pidgincore.h"
32 #include "pidgindialog.h"
33 #include "pidginiconname.h" 29 #include "pidginiconname.h"
34
35 #include "pidginstatusprimitivechooser.h"
36 #include "pidginstatusprimitivestore.h"
37
38 /*
39 * TODO: Should attach to the account-deleted and account-added signals
40 * and update the GtkListStores in any StatusEditor windows that
41 * may be open.
42 */
43
44 /*
45 * These are used for the GtkTreeView when you're scrolling through
46 * all your saved statuses.
47 */
48 enum
49 {
50 STATUS_WINDOW_COLUMN_TITLE,
51 STATUS_WINDOW_COLUMN_TYPE,
52 STATUS_WINDOW_COLUMN_MESSAGE,
53 /** A hidden column containing a pointer to the editor for this saved status. */
54 STATUS_WINDOW_COLUMN_WINDOW,
55 STATUS_WINDOW_COLUMN_ICON,
56 STATUS_WINDOW_NUM_COLUMNS
57 };
58
59 enum {
60 STATUS_RESPONSE_USE = 1,
61 STATUS_RESPONSE_ADD,
62 STATUS_RESPONSE_MODIFY,
63 STATUS_RESPONSE_DELETE,
64 STATUS_RESPONSE_SAVE,
65 STATUS_RESPONSE_SAVE_AND_USE,
66 };
67
68 /*
69 * These are used for the GtkTreeView containing the list of accounts
70 * at the bottom of the window when you're editing a particular
71 * saved status.
72 */
73 enum
74 {
75 /* A hidden column containing a pointer to the PurpleAccount. */
76 STATUS_EDITOR_COLUMN_ACCOUNT,
77 /* A hidden column containing a pointer to the editor for this substatus. */
78 STATUS_EDITOR_COLUMN_WINDOW,
79 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS,
80 STATUS_EDITOR_COLUMN_ICON,
81 STATUS_EDITOR_COLUMN_USERNAME,
82 /* A hidden column containing the ID of this PurpleStatusType. */
83 STATUS_EDITOR_COLUMN_STATUS_ID,
84 STATUS_EDITOR_COLUMN_STATUS_NAME,
85 STATUS_EDITOR_COLUMN_STATUS_MESSAGE,
86 STATUS_EDITOR_COLUMN_STATUS_ICON,
87 STATUS_EDITOR_NUM_COLUMNS
88 };
89
90 /*
91 * These are used in the GtkComboBox to select the specific PurpleStatusType
92 * when setting a (sub)status for a particular saved status.
93 */
94 enum
95 {
96 STATUS_COLUMN_ICON,
97 /** A hidden column containing the ID of this PurpleStatusType. */
98 STATUS_COLUMN_STATUS_ID,
99 STATUS_COLUMN_STATUS_NAME,
100 STATUS_NUM_COLUMNS
101 };
102
103 typedef struct
104 {
105 GtkWidget *window;
106 GtkListStore *model;
107 GtkWidget *treeview;
108 GtkWidget *use_button;
109 GtkWidget *modify_button;
110 GtkWidget *delete_button;
111 } StatusWindow;
112
113 typedef struct
114 {
115 GtkWidget *window;
116 GtkListStore *model;
117 GtkWidget *treeview;
118 GtkWidget *saveanduse_button;
119 GtkWidget *save_button;
120
121 gchar *original_title;
122 GtkEntry *title;
123 GtkComboBox *type;
124 GtkWidget *message_view;
125 GtkTextBuffer *message_buffer;
126 } StatusEditor;
127
128 typedef struct
129 {
130 StatusEditor *status_editor;
131 PurpleAccount *account;
132
133 GtkWidget *window;
134 GtkComboBox *box;
135 GtkWidget *message_view;
136 GtkTextBuffer *message_buffer;
137 } SubStatusEditor;
138
139 static StatusWindow *status_window = NULL;
140
141
142 /**************************************************************************
143 * Status window
144 **************************************************************************/
145
146 static gboolean
147 status_window_find_savedstatus(GtkTreeIter *iter, const char *title)
148 {
149 GtkTreeModel *model;
150 char *cur;
151
152 if ((status_window == NULL) || (title == NULL))
153 return FALSE;
154
155 model = GTK_TREE_MODEL(status_window->model);
156
157 if (!gtk_tree_model_get_iter_first(model, iter))
158 return FALSE;
159
160 do {
161 gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &cur, -1);
162 if (purple_strequal(title, cur))
163 {
164 g_free(cur);
165 return TRUE;
166 }
167 g_free(cur);
168 } while (gtk_tree_model_iter_next(model, iter));
169
170 return FALSE;
171 }
172
173 static gboolean
174 status_window_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
175 {
176 StatusWindow *dialog = user_data;
177
178 dialog->window = NULL;
179 pidgin_status_window_hide();
180
181 return FALSE;
182 }
183
184 static void
185 status_window_use(StatusWindow *dialog)
186 {
187 GtkTreeSelection *selection;
188 GtkTreeIter iter;
189 GList *list = NULL;
190 int num_selected = 0;
191
192 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
193
194 num_selected = gtk_tree_selection_count_selected_rows(selection);
195 if (num_selected != 1)
196 /*
197 * This shouldn't happen because the "Use" button should have
198 * been grayed out. Oh well.
199 */
200 return;
201
202 list = gtk_tree_selection_get_selected_rows(selection, NULL);
203
204 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model),
205 &iter, list->data))
206 {
207 gchar *title;
208 PurpleSavedStatus *saved_status;
209 gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter,
210 STATUS_WINDOW_COLUMN_TITLE, &title,
211 -1);
212 saved_status = purple_savedstatus_find(title);
213 g_free(title);
214 purple_savedstatus_activate(saved_status);
215 }
216
217 g_list_free_full(list, (GDestroyNotify)gtk_tree_path_free);
218 }
219
220 static void
221 status_window_modify_foreach(GtkTreeModel *model, GtkTreePath *path,
222 GtkTreeIter *iter, gpointer user_data)
223 {
224 gchar *title;
225 PurpleSavedStatus *saved_status;
226
227 gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1);
228 saved_status = purple_savedstatus_find(title);
229 g_free(title);
230 pidgin_status_editor_show(TRUE, saved_status);
231 }
232
233 static void
234 status_window_modify(StatusWindow *dialog) {
235 GtkTreeSelection *selection;
236
237 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
238
239 gtk_tree_selection_selected_foreach(selection,
240 status_window_modify_foreach, NULL);
241 }
242
243 static void
244 status_window_delete_cancel_cb(gpointer data)
245 {
246 GList *sel_titles = data;
247 g_list_free_full(sel_titles, g_free);
248 }
249
250 static void
251 status_window_delete_confirm_cb(gpointer data)
252 {
253 GtkTreeIter iter;
254 GList *sel_titles = data, *l;
255 char *title;
256
257 for (l = sel_titles; l != NULL; l = l->next) {
258 title = l->data;
259 if (purple_savedstatus_find(title) != purple_savedstatus_get_current()) {
260 if (status_window_find_savedstatus(&iter, title))
261 gtk_list_store_remove(status_window->model, &iter);
262 purple_savedstatus_delete(title);
263 }
264 g_free(title);
265 }
266 g_list_free(sel_titles);
267 }
268
269 static void
270 status_window_delete(StatusWindow *dialog) {
271 GtkTreeIter iter;
272 GtkTreeSelection *selection;
273 GList *sel_paths, *l, *sel_titles = NULL;
274 GtkTreeModel *model = GTK_TREE_MODEL(dialog->model);
275 char *title;
276 gpointer handle;
277
278 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
279 sel_paths = gtk_tree_selection_get_selected_rows(selection, NULL);
280
281 /* This is ugly because we're not allowed to modify the model from within
282 * gtk_tree_selection_selected_foreach() and the GtkTreePaths can become invalid
283 * when something is removed from the model. The selection can also change while
284 * the request dialog is displayed, so we need to capture the selected rows at this time. */
285
286 for (l = sel_paths; l != NULL; l = l->next) {
287 if (gtk_tree_model_get_iter(model, &iter, l->data)) {
288 gtk_tree_model_get(model, &iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1);
289 sel_titles = g_list_prepend(sel_titles, title);
290 }
291 gtk_tree_path_free(l->data);
292 }
293 g_list_free(sel_paths);
294
295 g_return_if_fail(sel_titles != NULL);
296 if (!sel_titles->next) {
297 title = g_strdup_printf(_("Are you sure you want to delete %s?"),
298 (const gchar *)sel_titles->data);
299 handle = purple_savedstatus_find(sel_titles->data);
300 } else {
301 title = g_strdup(_("Are you sure you want to delete the selected saved statuses?"));
302 handle = dialog;
303 }
304
305 purple_request_action(handle, NULL, title, NULL, 0,
306 NULL,
307 sel_titles, 2,
308 _("Delete"), status_window_delete_confirm_cb,
309 _("Cancel"), status_window_delete_cancel_cb);
310
311 g_free(title);
312 }
313
314 static void
315 status_selected_cb(GtkTreeSelection *sel, gpointer user_data)
316 {
317 StatusWindow *dialog = user_data;
318 GList *sel_paths, *tmp;
319 gboolean can_use = TRUE, can_delete = TRUE;
320 int num_selected;
321 GtkTreeModel *model = GTK_TREE_MODEL(dialog->model);
322
323 sel_paths = gtk_tree_selection_get_selected_rows(sel, NULL);
324
325 for (tmp = sel_paths, num_selected = 0; tmp; tmp = tmp->next, num_selected++) {
326 GtkTreeIter iter;
327 char *title;
328
329 if (gtk_tree_model_get_iter(model, &iter, tmp->data)) {
330 gtk_tree_model_get(model, &iter,
331 STATUS_WINDOW_COLUMN_TITLE, &title, -1);
332 if (purple_savedstatus_find(title) == purple_savedstatus_get_current()) {
333 can_use = can_delete = FALSE;
334 }
335
336 g_free(title);
337 }
338
339 gtk_tree_path_free(tmp->data);
340 }
341
342 gtk_widget_set_sensitive(dialog->use_button, (num_selected == 1) && can_use);
343 gtk_widget_set_sensitive(dialog->modify_button, (num_selected > 0));
344 gtk_widget_set_sensitive(dialog->delete_button, num_selected > 0 && can_delete);
345
346 g_list_free(sel_paths);
347 }
348
349 static void
350 add_status_to_saved_status_list(GtkListStore *model, PurpleSavedStatus *saved_status)
351 {
352 GtkTreeIter iter;
353 const char *title;
354 const char *type;
355 const gchar *icon;
356 char *message;
357
358 if (purple_savedstatus_is_transient(saved_status))
359 return;
360
361 title = purple_savedstatus_get_title(saved_status);
362 type = purple_primitive_get_name_from_type(purple_savedstatus_get_primitive_type(saved_status));
363 message = purple_markup_strip_html(purple_savedstatus_get_message(saved_status));
364 icon = pidgin_icon_name_from_status_primitive(purple_savedstatus_get_primitive_type(saved_status), NULL);
365
366 gtk_list_store_append(model, &iter);
367 gtk_list_store_set(model, &iter,
368 STATUS_WINDOW_COLUMN_ICON, icon,
369 STATUS_WINDOW_COLUMN_TITLE, title,
370 STATUS_WINDOW_COLUMN_TYPE, type,
371 STATUS_WINDOW_COLUMN_MESSAGE, message,
372 -1);
373 g_free(message);
374 }
375
376 static void
377 populate_saved_status_list(StatusWindow *dialog)
378 {
379 GList *saved_statuses;
380
381 gtk_list_store_clear(dialog->model);
382
383 for (saved_statuses = purple_savedstatuses_get_all(); saved_statuses != NULL;
384 saved_statuses = g_list_next(saved_statuses))
385 {
386 add_status_to_saved_status_list(dialog->model, saved_statuses->data);
387 }
388 }
389
390 static gboolean
391 search_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer search_data)
392 {
393 gboolean result;
394 char *haystack;
395
396 gtk_tree_model_get(model, iter, column, &haystack, -1);
397
398 result = (purple_strcasestr(haystack, key) == NULL);
399
400 g_free(haystack);
401
402 return result;
403 }
404
405 static void
406 savedstatus_activated_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, StatusWindow *dialog)
407 {
408 status_window_use(dialog);
409 pidgin_status_window_hide();
410 }
411
412 static void
413 saved_status_updated_cb(PurpleSavedStatus *status, StatusWindow *sw)
414 {
415 populate_saved_status_list(sw);
416 }
417
418 static GtkWidget *
419 create_saved_status_list(StatusWindow *dialog)
420 {
421 GtkWidget *treeview;
422 GtkTreeSelection *sel;
423 GtkTreeViewColumn *column;
424 GtkCellRenderer *renderer;
425
426 /* Create the list model */
427 dialog->model = gtk_list_store_new(STATUS_WINDOW_NUM_COLUMNS,
428 G_TYPE_STRING,
429 G_TYPE_STRING,
430 G_TYPE_STRING,
431 G_TYPE_POINTER,
432 G_TYPE_STRING);
433
434 /* Create the treeview */
435 treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
436 dialog->treeview = treeview;
437 g_signal_connect(G_OBJECT(treeview), "row-activated",
438 G_CALLBACK(savedstatus_activated_cb), dialog);
439
440 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
441 gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
442 g_signal_connect(G_OBJECT(sel), "changed",
443 G_CALLBACK(status_selected_cb), dialog);
444
445 /* Add columns */
446 column = gtk_tree_view_column_new();
447 gtk_tree_view_column_set_title(column, _("Title"));
448 gtk_tree_view_column_set_resizable(column, TRUE);
449 gtk_tree_view_column_set_min_width(column, 100);
450 gtk_tree_view_column_set_sort_column_id(column,
451 STATUS_WINDOW_COLUMN_TITLE);
452 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
453 renderer = gtk_cell_renderer_text_new();
454 gtk_tree_view_column_pack_start(column, renderer, TRUE);
455 gtk_tree_view_column_add_attribute(column, renderer, "text",
456 STATUS_WINDOW_COLUMN_TITLE);
457 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
458
459 column = gtk_tree_view_column_new();
460 gtk_tree_view_column_set_title(column, _("Type"));
461 gtk_tree_view_column_set_resizable(column, TRUE);
462 gtk_tree_view_column_set_sort_column_id(column,
463 STATUS_WINDOW_COLUMN_TYPE);
464 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
465 renderer = gtk_cell_renderer_pixbuf_new();
466 gtk_tree_view_column_pack_start(column, renderer, TRUE);
467 gtk_tree_view_column_add_attribute(column, renderer, "icon-name",
468 STATUS_WINDOW_COLUMN_ICON);
469 renderer = gtk_cell_renderer_text_new();
470 gtk_tree_view_column_pack_start(column, renderer, TRUE);
471 gtk_tree_view_column_add_attribute(column, renderer, "text",
472 STATUS_WINDOW_COLUMN_TYPE);
473
474 column = gtk_tree_view_column_new();
475 gtk_tree_view_column_set_title(column, _("Message"));
476 gtk_tree_view_column_set_resizable(column, TRUE);
477 gtk_tree_view_column_set_sort_column_id(column,
478 STATUS_WINDOW_COLUMN_MESSAGE);
479 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
480 renderer = gtk_cell_renderer_text_new();
481 gtk_tree_view_column_pack_start(column, renderer, TRUE);
482 gtk_tree_view_column_add_attribute(column, renderer, "text",
483 STATUS_WINDOW_COLUMN_MESSAGE);
484 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
485
486 /* Enable CTRL+F searching */
487 gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), STATUS_WINDOW_COLUMN_TITLE);
488 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), search_func, NULL, NULL);
489
490 /* Sort the title column by default */
491 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dialog->model),
492 STATUS_WINDOW_COLUMN_TITLE,
493 GTK_SORT_ASCENDING);
494
495 /* Populate list */
496 populate_saved_status_list(dialog);
497
498 gtk_widget_show_all(treeview);
499
500 return pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1);
501 }
502
503 static void
504 response_cb(GtkDialog *dialog, gint response_id, gpointer data) {
505 StatusWindow *window = data;
506
507 switch(response_id) {
508 case STATUS_RESPONSE_USE:
509 status_window_use(window);
510 break;
511 case STATUS_RESPONSE_ADD:
512 pidgin_status_editor_show(FALSE, NULL);
513 break;
514 case STATUS_RESPONSE_MODIFY:
515 status_window_modify(window);
516 break;
517 case STATUS_RESPONSE_DELETE:
518 status_window_delete(window);
519 break;
520 case GTK_RESPONSE_CLOSE:
521 pidgin_status_window_hide();
522 break;
523 default:
524 break;
525 }
526 }
527
528 static void
529 current_status_changed(PurpleSavedStatus *old, PurpleSavedStatus *new_status,
530 StatusWindow *dialog)
531 {
532 status_selected_cb(gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)), dialog);
533 }
534
535 void
536 pidgin_status_window_show(void)
537 {
538 StatusWindow *dialog;
539 GtkWidget *list;
540 GtkWidget *vbox;
541 GtkWidget *win;
542
543 if (status_window != NULL)
544 {
545 gtk_window_present(GTK_WINDOW(status_window->window));
546 return;
547 }
548
549 status_window = dialog = g_new0(StatusWindow, 1);
550
551 dialog->window = win = pidgin_dialog_new(_("Saved Statuses"), 12, "statuses", TRUE);
552 gtk_window_set_default_size(GTK_WINDOW(win), 550, 250);
553
554 g_signal_connect(win, "delete_event", G_CALLBACK(status_window_destroy_cb),
555 dialog);
556 g_signal_connect(win, "response", G_CALLBACK(response_cb), dialog);
557
558 /* Setup the vbox */
559 vbox = gtk_dialog_get_content_area(GTK_DIALOG(win));
560 gtk_box_set_spacing(GTK_BOX(vbox), 12);
561
562 /* List of saved status states */
563 list = create_saved_status_list(dialog);
564 gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0);
565
566 /* Add our buttons */
567 dialog->use_button = gtk_dialog_add_button(GTK_DIALOG(win), _("_Use"),
568 STATUS_RESPONSE_USE);
569 gtk_widget_set_sensitive(dialog->use_button, FALSE);
570
571 gtk_dialog_add_button(GTK_DIALOG(win), _("_Add"), STATUS_RESPONSE_ADD);
572
573 dialog->modify_button = gtk_dialog_add_button(GTK_DIALOG(win),
574 _("_Modify"),
575 STATUS_RESPONSE_MODIFY);
576 gtk_widget_set_sensitive(dialog->modify_button, FALSE);
577
578 dialog->delete_button = gtk_dialog_add_button(GTK_DIALOG(win), _("Delete"),
579 STATUS_RESPONSE_DELETE);
580 gtk_widget_set_sensitive(dialog->delete_button, FALSE);
581
582 gtk_dialog_add_button(GTK_DIALOG(win), _("Close"), GTK_RESPONSE_CLOSE);
583
584 purple_signal_connect(purple_savedstatuses_get_handle(),
585 "savedstatus-changed", status_window,
586 G_CALLBACK(current_status_changed), dialog);
587 purple_signal_connect(purple_savedstatuses_get_handle(),
588 "savedstatus-added", status_window,
589 G_CALLBACK(saved_status_updated_cb), dialog);
590 purple_signal_connect(purple_savedstatuses_get_handle(),
591 "savedstatus-deleted", status_window,
592 G_CALLBACK(saved_status_updated_cb), dialog);
593 purple_signal_connect(purple_savedstatuses_get_handle(),
594 "savedstatus-modified", status_window,
595 G_CALLBACK(saved_status_updated_cb), dialog);
596
597 gtk_widget_show_all(win);
598 }
599
600 void
601 pidgin_status_window_hide(void)
602 {
603 if (status_window == NULL)
604 return;
605
606 if (status_window->window != NULL)
607 gtk_widget_destroy(status_window->window);
608
609 purple_request_close_with_handle(status_window);
610 purple_notify_close_with_handle(status_window);
611 purple_signals_disconnect_by_handle(status_window);
612 g_object_unref(G_OBJECT(status_window->model));
613 g_free(status_window);
614 status_window = NULL;
615 }
616
617
618 /**************************************************************************
619 * Status editor
620 **************************************************************************/
621
622 static void substatus_editor_cancel(SubStatusEditor *dialog);
623
624 static void
625 status_editor_remove_dialog(StatusEditor *dialog)
626 {
627 GtkTreeModel *model;
628 GtkTreeIter iter;
629
630 /* Remove the reference to this dialog from our parent's list store */
631 if (status_window_find_savedstatus(&iter, dialog->original_title))
632 {
633 gtk_list_store_set(status_window->model, &iter,
634 STATUS_WINDOW_COLUMN_WINDOW, NULL,
635 -1);
636 }
637
638 /* Close any substatus editors that may be open */
639 model = GTK_TREE_MODEL(dialog->model);
640 if (gtk_tree_model_get_iter_first(model, &iter))
641 {
642 do {
643 SubStatusEditor *substatus_dialog;
644
645 gtk_tree_model_get(model, &iter,
646 STATUS_EDITOR_COLUMN_WINDOW, &substatus_dialog,
647 -1);
648 if (substatus_dialog != NULL)
649 {
650 gtk_list_store_set(dialog->model, &iter,
651 STATUS_EDITOR_COLUMN_WINDOW, NULL,
652 -1);
653 substatus_editor_cancel(substatus_dialog);
654 }
655 } while (gtk_tree_model_iter_next(model, &iter));
656 }
657 }
658
659
660 static void
661 status_editor_destroy_cb(GtkWidget *widget, gpointer user_data)
662 {
663 StatusEditor *dialog = user_data;
664
665 status_editor_remove_dialog(dialog);
666 g_free(dialog->original_title);
667 g_object_unref(G_OBJECT(dialog->model));
668 g_free(dialog);
669 }
670
671 static void
672 status_editor_ok(StatusEditor *dialog, gboolean use, gboolean save) {
673 const char *title;
674 PurpleStatusPrimitive type;
675 char *message, *unformatted;
676 PurpleSavedStatus *saved_status = NULL;
677 GtkTreeModel *model;
678 GtkTreeIter iter;
679
680 title = gtk_entry_get_text(dialog->title);
681
682 /*
683 * If we're saving this status, and the title is already taken
684 * then show an error dialog and don't do anything.
685 */
686 if ((save) && (purple_savedstatus_find(title) != NULL) &&
687 ((dialog->original_title == NULL) || (!purple_strequal(title, dialog->original_title))))
688 {
689 purple_notify_error(status_window, NULL, _("Title already in use. You must "
690 "choose a unique title."), NULL, NULL);
691 return;
692 }
693
694 type = gtk_combo_box_get_active(dialog->type) + (PURPLE_STATUS_UNSET + 1);
695 message = talkatu_markup_get_html(dialog->message_buffer, NULL);
696 unformatted = purple_markup_strip_html(message);
697
698 /*
699 * If we're editing an old status, then lookup the old status.
700 * Note: It is possible that it has been deleted or renamed
701 * or something, and no longer exists.
702 */
703 if (dialog->original_title != NULL)
704 {
705 GtkTreeIter iter;
706
707 saved_status = purple_savedstatus_find(dialog->original_title);
708
709 if (status_window_find_savedstatus(&iter, dialog->original_title))
710 gtk_list_store_remove(status_window->model, &iter);
711 }
712
713 if(saved_status == NULL) {
714 /* This is a new status */
715 if(save) {
716 saved_status = purple_savedstatus_new(title, type);
717 } else {
718 saved_status = purple_savedstatus_new(NULL, type);
719 }
720 } else {
721 /* Modify the old status */
722 if(!purple_strequal(title, dialog->original_title)) {
723 purple_savedstatus_set_title(saved_status, title);
724 }
725 purple_savedstatus_set_primitive_type(saved_status, type);
726 }
727
728 if (*unformatted == '\0')
729 purple_savedstatus_set_message(saved_status, NULL);
730 else
731 purple_savedstatus_set_message(saved_status, message);
732
733 /* Set any substatuses */
734 model = GTK_TREE_MODEL(dialog->model);
735 if (gtk_tree_model_get_iter_first(model, &iter))
736 {
737 do {
738 PurpleAccount *account;
739 gboolean enabled;
740 char *id;
741 char *message;
742 PurpleStatusType *type;
743
744 gtk_tree_model_get(model, &iter,
745 STATUS_EDITOR_COLUMN_ACCOUNT, &account,
746 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, &enabled,
747 STATUS_EDITOR_COLUMN_STATUS_ID, &id,
748 STATUS_EDITOR_COLUMN_STATUS_MESSAGE, &message,
749 -1);
750 if (enabled)
751 {
752 type = purple_account_get_status_type(account, id);
753 purple_savedstatus_set_substatus(saved_status, account, type, message);
754 }
755 else
756 {
757 purple_savedstatus_unset_substatus(saved_status, account);
758 }
759 g_free(id);
760 g_free(message);
761 } while (gtk_tree_model_iter_next(model, &iter));
762 }
763
764 g_free(message);
765 g_free(unformatted);
766
767 /* If they clicked on "Save and Use" or "Use," then activate the status */
768 if(use) {
769 purple_savedstatus_activate(saved_status);
770 }
771
772 gtk_widget_destroy(dialog->window);
773 }
774
775
776 static void
777 status_editor_response_cb(GtkDialog *dialog, gint response, gpointer data) {
778 StatusEditor *window = data;
779
780 switch(response) {
781 case GTK_RESPONSE_CLOSE:
782 gtk_widget_destroy(window->window);
783 break;
784 case STATUS_RESPONSE_USE:
785 status_editor_ok(window, TRUE, FALSE);
786 break;
787 case STATUS_RESPONSE_SAVE_AND_USE:
788 status_editor_ok(window, TRUE, TRUE);
789 break;
790 case STATUS_RESPONSE_SAVE:
791 status_editor_ok(window, FALSE, TRUE);
792 break;
793 default:
794 break;
795 }
796 }
797
798 static void
799 editor_title_changed_cb(GtkWidget *widget, gpointer user_data)
800 {
801 StatusEditor *dialog = user_data;
802 const gchar *text;
803
804 text = gtk_entry_get_text(dialog->title);
805
806 gtk_widget_set_sensitive(GTK_WIDGET(dialog->saveanduse_button), (*text != '\0'));
807 gtk_widget_set_sensitive(GTK_WIDGET(dialog->save_button), (*text != '\0'));
808 }
809
810 static void edit_substatus(StatusEditor *status_editor, PurpleAccount *account);
811
812 static void
813 edit_substatus_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer user_data)
814 {
815 StatusEditor *dialog = user_data;
816 GtkTreeIter iter;
817 PurpleAccount *account;
818
819 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
820 gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter,
821 STATUS_EDITOR_COLUMN_ACCOUNT, &account,
822 -1);
823
824 edit_substatus(dialog, account);
825 }
826
827 static void
828 status_editor_substatus_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpointer data)
829 {
830 StatusEditor *dialog = (StatusEditor *)data;
831 GtkTreeIter iter;
832 gboolean enabled;
833 PurpleAccount *account;
834
835 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(dialog->model), &iter, path_str);
836 gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter,
837 STATUS_EDITOR_COLUMN_ACCOUNT, &account,
838 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, &enabled,
839 -1);
840
841 enabled = !enabled;
842
843 if (enabled)
844 {
845 edit_substatus(dialog, account);
846 }
847 else
848 {
849 /* Remove the substatus */
850 gtk_list_store_set(dialog->model, &iter,
851 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, enabled,
852 STATUS_EDITOR_COLUMN_STATUS_ID, NULL,
853 STATUS_EDITOR_COLUMN_STATUS_NAME, NULL,
854 STATUS_EDITOR_COLUMN_STATUS_MESSAGE, NULL,
855 STATUS_EDITOR_COLUMN_STATUS_ICON, NULL,
856 -1);
857 }
858 }
859
860 static void
861 status_editor_add_columns(StatusEditor *dialog)
862 {
863 GtkCellRenderer *renderer;
864 GtkTreeViewColumn *column;
865
866 /* Enable Different status column */
867 renderer = gtk_cell_renderer_toggle_new();
868 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dialog->treeview),
869 -1, _("Different"),
870 renderer,
871 "active", STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS,
872 NULL);
873 g_signal_connect(G_OBJECT(renderer), "toggled",
874 G_CALLBACK(status_editor_substatus_cb), dialog);
875
876 /* Username column */
877 column = gtk_tree_view_column_new();
878 gtk_tree_view_column_set_resizable(column, TRUE);
879 gtk_tree_view_column_set_title(column, _("Username"));
880 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1);
881 gtk_tree_view_column_set_resizable(column, TRUE);
882
883 /* Icon */
884 renderer = gtk_cell_renderer_pixbuf_new();
885 gtk_tree_view_column_pack_start(column, renderer, FALSE);
886 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf",
887 STATUS_EDITOR_COLUMN_ICON);
888
889 /* Username */
890 renderer = gtk_cell_renderer_text_new();
891 gtk_tree_view_column_pack_start(column, renderer, TRUE);
892 gtk_tree_view_column_add_attribute(column, renderer, "text",
893 STATUS_EDITOR_COLUMN_USERNAME);
894
895 /* Status column */
896 column = gtk_tree_view_column_new();
897 gtk_tree_view_column_set_resizable(column, TRUE);
898 gtk_tree_view_column_set_title(column, _("Status"));
899 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1);
900 gtk_tree_view_column_set_resizable(column, TRUE);
901 renderer = gtk_cell_renderer_pixbuf_new();
902 gtk_tree_view_column_pack_start(column, renderer, FALSE);
903 gtk_tree_view_column_add_attribute(column, renderer, "stock-id",
904 STATUS_EDITOR_COLUMN_STATUS_ICON);
905 renderer = gtk_cell_renderer_text_new();
906 gtk_tree_view_column_pack_start(column, renderer, TRUE);
907 gtk_tree_view_column_add_attribute(column, renderer, "text",
908 STATUS_EDITOR_COLUMN_STATUS_NAME);
909
910 /* Message column */
911 column = gtk_tree_view_column_new();
912 gtk_tree_view_column_set_resizable(column, TRUE);
913 gtk_tree_view_column_set_title(column, _("Message"));
914 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1);
915 gtk_tree_view_column_set_resizable(column, TRUE);
916 renderer = gtk_cell_renderer_text_new();
917 gtk_tree_view_column_pack_start(column, renderer, TRUE);
918 gtk_tree_view_column_add_attribute(column, renderer, "text",
919 STATUS_EDITOR_COLUMN_STATUS_MESSAGE);
920
921 g_signal_connect(G_OBJECT(dialog->treeview), "row-activated",
922 G_CALLBACK(edit_substatus_cb), dialog);
923 }
924
925 static void
926 status_editor_set_account(GtkListStore *store, PurpleAccount *account,
927 GtkTreeIter *iter, PurpleSavedStatusSub *substatus)
928 {
929 GdkPixbuf *pixbuf;
930 const char *id = NULL, *name = NULL, *message = NULL;
931 PurpleStatusPrimitive prim = PURPLE_STATUS_UNSET;
932
933 pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_MEDIUM);
934 if ((pixbuf != NULL) && !purple_account_is_connected(account))
935 {
936 gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
937 }
938
939 if (substatus != NULL)
940 {
941 const PurpleStatusType *type;
942
943 type = purple_savedstatus_substatus_get_status_type(substatus);
944 id = purple_status_type_get_id(type);
945 name = purple_status_type_get_name(type);
946 prim = purple_status_type_get_primitive(type);
947 if (purple_status_type_get_attr(type, "message"))
948 message = purple_savedstatus_substatus_get_message(substatus);
949 }
950
951 gtk_list_store_set(store, iter,
952 STATUS_EDITOR_COLUMN_ACCOUNT, account,
953 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, (substatus != NULL),
954 STATUS_EDITOR_COLUMN_ICON, pixbuf,
955 STATUS_EDITOR_COLUMN_USERNAME, purple_account_get_username(account),
956 STATUS_EDITOR_COLUMN_STATUS_ID, id,
957 STATUS_EDITOR_COLUMN_STATUS_NAME, name,
958 STATUS_EDITOR_COLUMN_STATUS_MESSAGE, message,
959 STATUS_EDITOR_COLUMN_STATUS_ICON, pidgin_icon_name_from_status_primitive(prim, NULL),
960 -1);
961
962 if (pixbuf != NULL)
963 g_object_unref(G_OBJECT(pixbuf));
964 }
965
966 static void
967 status_editor_add_account(StatusEditor *dialog, PurpleAccount *account,
968 PurpleSavedStatusSub *substatus)
969 {
970 GtkTreeIter iter;
971
972 gtk_list_store_append(dialog->model, &iter);
973
974 status_editor_set_account(dialog->model, account, &iter, substatus);
975 }
976
977 static void
978 status_editor_populate_list(StatusEditor *dialog, PurpleSavedStatus *saved_status)
979 {
980 GList *iter;
981 PurpleAccountManager *manager = NULL;
982 PurpleSavedStatusSub *substatus;
983
984 gtk_list_store_clear(dialog->model);
985
986 manager = purple_account_manager_get_default();
987 iter = purple_account_manager_get_all(manager);
988
989 for(; iter != NULL; iter = iter->next) {
990 PurpleAccount *account = (PurpleAccount *)iter->data;
991
992 if (saved_status != NULL) {
993 substatus = purple_savedstatus_get_substatus(saved_status, account);
994 } else {
995 substatus = NULL;
996 }
997
998 status_editor_add_account(dialog, account, substatus);
999 }
1000 }
1001
1002 void
1003 pidgin_status_editor_show(gboolean edit, PurpleSavedStatus *saved_status)
1004 {
1005 GtkTreeIter iter;
1006 StatusEditor *dialog;
1007 GtkSizeGroup *sg;
1008 GtkWidget *dbox;
1009 GtkWidget *expander;
1010 GtkWidget *dropdown;
1011 GtkWidget *entry;
1012 GtkWidget *editor;
1013 GtkWidget *hbox;
1014 GtkWidget *vbox;
1015 GtkWidget *win;
1016 GtkListStore *store;
1017 PurpleStatusPrimitive primitive = PURPLE_STATUS_AWAY;
1018
1019 if (edit)
1020 {
1021 g_return_if_fail(saved_status != NULL);
1022 g_return_if_fail(!purple_savedstatus_is_transient(saved_status));
1023 }
1024
1025 /* Find a possible window for this saved status and present it */
1026 if (edit && status_window_find_savedstatus(&iter, purple_savedstatus_get_title(saved_status)))
1027 {
1028 gtk_tree_model_get(GTK_TREE_MODEL(status_window->model), &iter,
1029 STATUS_WINDOW_COLUMN_WINDOW, &dialog,
1030 -1);
1031 if (dialog != NULL)
1032 {
1033 gtk_window_present(GTK_WINDOW(dialog->window));
1034 return;
1035 }
1036 }
1037
1038 dialog = g_new0(StatusEditor, 1);
1039 if (edit && status_window_find_savedstatus(&iter, purple_savedstatus_get_title(saved_status)))
1040 {
1041 gtk_list_store_set(status_window->model, &iter,
1042 STATUS_WINDOW_COLUMN_WINDOW, dialog,
1043 -1);
1044 }
1045
1046 if (edit)
1047 dialog->original_title = g_strdup(purple_savedstatus_get_title(saved_status));
1048
1049 dialog->window = win = pidgin_dialog_new(_("Status"), 12, "status", TRUE);
1050
1051 g_signal_connect(win, "destroy", G_CALLBACK(status_editor_destroy_cb),
1052 dialog);
1053 g_signal_connect(win, "response", G_CALLBACK(status_editor_response_cb),
1054 dialog);
1055
1056 /* Setup the vbox */
1057 vbox = gtk_dialog_get_content_area(GTK_DIALOG(win));
1058 gtk_box_set_spacing(GTK_BOX(vbox), 12);
1059
1060 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1061
1062 /* Title */
1063 entry = gtk_entry_new();
1064 dialog->title = GTK_ENTRY(entry);
1065 if ((saved_status != NULL)
1066 && !purple_savedstatus_is_transient(saved_status)
1067 && (purple_savedstatus_get_title(saved_status) != NULL))
1068 gtk_entry_set_text(GTK_ENTRY(entry), purple_savedstatus_get_title(saved_status));
1069 g_signal_connect(G_OBJECT(entry), "changed",
1070 G_CALLBACK(editor_title_changed_cb), dialog);
1071 pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Title:"), sg, entry, TRUE, NULL);
1072
1073 /* Status type */
1074 if (saved_status != NULL) {
1075 primitive = purple_savedstatus_get_primitive_type(saved_status);
1076 }
1077
1078 store = pidgin_status_primitive_store_new();
1079 dropdown = pidgin_status_primitive_chooser_new();
1080 dialog->type = GTK_COMBO_BOX(dropdown);
1081 gtk_combo_box_set_model(GTK_COMBO_BOX(dropdown), GTK_TREE_MODEL(store));
1082 gtk_combo_box_set_active_id(GTK_COMBO_BOX(dropdown),
1083 purple_primitive_get_id_from_type(primitive));
1084 pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Status:"), sg, dropdown, TRUE, NULL);
1085
1086 /* Status message */
1087 editor = talkatu_editor_new();
1088
1089 dialog->message_view = talkatu_editor_get_input(TALKATU_EDITOR(editor));
1090 hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Message:"), sg, editor, TRUE, NULL);
1091
1092 dialog->message_buffer = talkatu_html_buffer_new();
1093 gtk_text_view_set_buffer(GTK_TEXT_VIEW(dialog->message_view), dialog->message_buffer);
1094
1095 gtk_container_child_set(GTK_CONTAINER(vbox), hbox, "expand", TRUE, "fill", TRUE, NULL);
1096
1097 if ((saved_status != NULL) && (purple_savedstatus_get_message(saved_status) != NULL)) {
1098 talkatu_markup_set_html(
1099 TALKATU_BUFFER(dialog->message_buffer),
1100 purple_savedstatus_get_message(saved_status),
1101 -1
1102 );
1103 }
1104
1105 /* Different status message expander */
1106 expander = gtk_expander_new_with_mnemonic(_("Use a _different status for some accounts"));
1107 gtk_box_pack_start(GTK_BOX(vbox), expander, FALSE, FALSE, 0);
1108
1109 /* Setup the box that the expander will cover */
1110 dbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
1111 gtk_container_add(GTK_CONTAINER(expander), dbox);
1112
1113 /* Create the list model */
1114 dialog->model = gtk_list_store_new(STATUS_EDITOR_NUM_COLUMNS,
1115 G_TYPE_POINTER,
1116 G_TYPE_POINTER,
1117 G_TYPE_BOOLEAN,
1118 GDK_TYPE_PIXBUF,
1119 G_TYPE_STRING,
1120 G_TYPE_STRING,
1121 G_TYPE_STRING,
1122 G_TYPE_STRING,
1123 G_TYPE_STRING);
1124
1125 /* Create the treeview */
1126 dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
1127 gtk_widget_set_size_request(dialog->treeview, -1, 150);
1128 gtk_box_pack_start(GTK_BOX(dbox),
1129 pidgin_make_scrollable(dialog->treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1),
1130 TRUE, TRUE, 0);
1131
1132 /* Add columns */
1133 status_editor_add_columns(dialog);
1134
1135 /* Populate list */
1136 status_editor_populate_list(dialog, saved_status);
1137
1138 /* Expand the treeview if we have substatuses */
1139 gtk_expander_set_expanded(GTK_EXPANDER(expander),
1140 (saved_status != NULL) && purple_savedstatus_has_substatuses(saved_status));
1141
1142 /* Buttons */
1143 gtk_dialog_add_button(GTK_DIALOG(win), _("Cancel"), GTK_RESPONSE_CLOSE);
1144 gtk_dialog_add_button(GTK_DIALOG(win), _("_Use"), STATUS_RESPONSE_USE);
1145
1146 dialog->saveanduse_button = gtk_dialog_add_button(GTK_DIALOG(win),
1147 _("Sa_ve and Use"),
1148 STATUS_RESPONSE_SAVE_AND_USE);
1149 if(dialog->original_title == NULL) {
1150 gtk_widget_set_sensitive(dialog->saveanduse_button, FALSE);
1151 }
1152
1153 dialog->save_button = gtk_dialog_add_button(GTK_DIALOG(win), _("Save"),
1154 STATUS_RESPONSE_SAVE);
1155 if(dialog->original_title == NULL) {
1156 gtk_widget_set_sensitive(dialog->save_button, FALSE);
1157 }
1158
1159 gtk_widget_show_all(win);
1160 g_object_unref(sg);
1161 }
1162
1163
1164 /**************************************************************************
1165 * SubStatus editor
1166 **************************************************************************/
1167
1168 static void
1169 substatus_selection_changed_cb(GtkComboBox *box, gpointer user_data)
1170 {
1171 SubStatusEditor *select = user_data;
1172 PurpleStatusType *type;
1173 PurpleStatusPrimitive primitive;
1174 const gchar *id;
1175
1176 id = gtk_combo_box_get_active_id(box);
1177 if(id == NULL) {
1178 return;
1179 }
1180
1181 primitive = purple_primitive_get_type_from_id(id);
1182 if(primitive == PURPLE_STATUS_UNSET) {
1183 return;
1184 }
1185
1186 type = purple_account_get_status_type_with_primitive(select->account,
1187 primitive);
1188
1189 if(purple_status_type_get_attr(type, "message") == NULL) {
1190 gtk_widget_set_sensitive(select->message_view, FALSE);
1191 } else {
1192 gtk_widget_set_sensitive(select->message_view, TRUE);
1193 }
1194 }
1195
1196 static gboolean
1197 status_editor_find_account_in_treemodel(GtkTreeIter *iter,
1198 StatusEditor *status_editor,
1199 PurpleAccount *account)
1200 {
1201 GtkTreeModel *model;
1202 PurpleAccount *cur;
1203
1204 g_return_val_if_fail(status_editor != NULL, FALSE);
1205 g_return_val_if_fail(account != NULL, FALSE);
1206
1207 model = GTK_TREE_MODEL(status_editor->model);
1208
1209 if (!gtk_tree_model_get_iter_first(model, iter))
1210 return FALSE;
1211
1212 do {
1213 gtk_tree_model_get(model, iter, STATUS_EDITOR_COLUMN_ACCOUNT, &cur, -1);
1214 if (cur == account)
1215 return TRUE;
1216 } while (gtk_tree_model_iter_next(model, iter));
1217
1218 return FALSE;
1219 }
1220
1221 static void
1222 substatus_editor_remove_dialog(SubStatusEditor *dialog)
1223 {
1224 GtkTreeIter iter;
1225
1226 if (status_editor_find_account_in_treemodel(&iter, dialog->status_editor, dialog->account))
1227 {
1228 /* Remove the reference to this dialog from our parent's list store */
1229 gtk_list_store_set(dialog->status_editor->model, &iter,
1230 STATUS_EDITOR_COLUMN_WINDOW, NULL,
1231 -1);
1232 }
1233 }
1234
1235 static void
1236 substatus_editor_destroy_cb(GtkWidget *widget, gpointer user_data)
1237 {
1238 SubStatusEditor *dialog = user_data;
1239
1240 substatus_editor_remove_dialog(dialog);
1241 g_free(dialog);
1242 }
1243
1244 static void
1245 substatus_editor_cancel(SubStatusEditor *dialog)
1246 {
1247 gtk_widget_destroy(dialog->window);
1248 }
1249
1250
1251 static void
1252 substatus_editor_ok(SubStatusEditor *dialog)
1253 {
1254 StatusEditor *status_editor;
1255 GtkTreeIter iter;
1256 PurpleStatusType *type;
1257 PurpleStatusPrimitive primitive;
1258 const char *id = NULL;
1259 char *message = NULL;
1260 const gchar *name = NULL, *icon_name = NULL;
1261
1262 id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(dialog->box));
1263 if(id == NULL) {
1264 gtk_widget_destroy(dialog->window);
1265 return;
1266 }
1267
1268 primitive = purple_primitive_get_type_from_id(id);
1269 if(primitive == PURPLE_STATUS_UNSET) {
1270 gtk_widget_destroy(dialog->window);
1271 return;
1272 }
1273
1274 type = purple_account_get_status_type_with_primitive(dialog->account,
1275 primitive);
1276 if (purple_status_type_get_attr(type, "message") != NULL) {
1277 message = talkatu_markup_get_html(dialog->message_buffer, NULL);
1278 }
1279 name = purple_status_type_get_name(type);
1280 icon_name = pidgin_icon_name_from_status_primitive(primitive, NULL);
1281
1282 status_editor = dialog->status_editor;
1283
1284 if (status_editor_find_account_in_treemodel(&iter, status_editor, dialog->account))
1285 {
1286 gtk_list_store_set(status_editor->model, &iter,
1287 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, TRUE,
1288 STATUS_EDITOR_COLUMN_STATUS_ID, id,
1289 STATUS_EDITOR_COLUMN_STATUS_NAME, name,
1290 STATUS_EDITOR_COLUMN_STATUS_MESSAGE, message,
1291 STATUS_EDITOR_COLUMN_WINDOW, NULL,
1292 STATUS_EDITOR_COLUMN_STATUS_ICON, icon_name,
1293 -1);
1294 }
1295
1296 gtk_widget_destroy(dialog->window);
1297 g_free(message);
1298 }
1299
1300 static void
1301 substatus_editor_response_cb(GtkDialog *dialog, gint response_id,
1302 gpointer data)
1303 {
1304 SubStatusEditor *editor = data;
1305
1306 switch(response_id) {
1307 case GTK_RESPONSE_CANCEL:
1308 substatus_editor_cancel(editor);
1309 break;
1310 case GTK_RESPONSE_OK:
1311 substatus_editor_ok(editor);
1312 break;
1313 default:
1314 break;
1315 }
1316 }
1317
1318 static void
1319 edit_substatus(StatusEditor *status_editor, PurpleAccount *account)
1320 {
1321 char *tmp;
1322 SubStatusEditor *dialog;
1323 GtkSizeGroup *sg;
1324 GtkWidget *combo;
1325 GtkWidget *hbox;
1326 GtkWidget *editor;
1327 GtkWidget *label;
1328 GtkWidget *vbox;
1329 GtkWidget *win;
1330 GtkTreeIter iter;
1331 char *status_id = NULL;
1332 char *message = NULL;
1333 gboolean parent_dialog_has_substatus = FALSE;
1334 GtkListStore *store;
1335 PurpleStatusPrimitive primitive = PURPLE_STATUS_AWAY;
1336
1337 g_return_if_fail(status_editor != NULL);
1338 g_return_if_fail(account != NULL);
1339
1340 status_editor_find_account_in_treemodel(&iter, status_editor, account);
1341 gtk_tree_model_get(GTK_TREE_MODEL(status_editor->model), &iter,
1342 STATUS_EDITOR_COLUMN_WINDOW, &dialog,
1343 -1);
1344 if (dialog != NULL)
1345 {
1346 gtk_window_present(GTK_WINDOW(dialog->window));
1347 return;
1348 }
1349
1350 dialog = g_new0(SubStatusEditor, 1);
1351 gtk_list_store_set(status_editor->model, &iter,
1352 STATUS_EDITOR_COLUMN_WINDOW, dialog,
1353 -1);
1354 dialog->status_editor = status_editor;
1355 dialog->account = account;
1356
1357 tmp = g_strdup_printf(_("Status for %s"), purple_account_get_username(account));
1358 dialog->window = win = pidgin_dialog_new(tmp, 12, "substatus", TRUE);
1359 g_free(tmp);
1360
1361 g_signal_connect(G_OBJECT(win), "destroy",
1362 G_CALLBACK(substatus_editor_destroy_cb), dialog);
1363 g_signal_connect(win, "response", G_CALLBACK(substatus_editor_response_cb),
1364 dialog);
1365
1366 /* Setup the vbox */
1367 vbox = gtk_dialog_get_content_area(GTK_DIALOG(win));
1368 gtk_box_set_spacing(GTK_BOX(vbox), 12);
1369
1370 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1371
1372 /* Status type */
1373 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1374 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1375
1376 label = gtk_label_new_with_mnemonic(_("_Status:"));
1377 gtk_label_set_xalign(GTK_LABEL(label), 0);
1378 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1379 gtk_size_group_add_widget(sg, label);
1380
1381 store = pidgin_status_primitive_store_new();
1382 pidgin_status_primitive_store_set_account(PIDGIN_STATUS_PRIMITIVE_STORE(store),
1383 account);
1384 combo = pidgin_status_primitive_chooser_new();
1385 gtk_combo_box_set_model(GTK_COMBO_BOX(combo), GTK_TREE_MODEL(store));
1386 dialog->box = GTK_COMBO_BOX(combo);
1387
1388 g_signal_connect(G_OBJECT(combo), "changed",
1389 G_CALLBACK(substatus_selection_changed_cb), dialog);
1390
1391 gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
1392
1393 /* Status message */
1394 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1395 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
1396
1397 label = gtk_label_new_with_mnemonic(_("_Message:"));
1398 gtk_label_set_xalign(GTK_LABEL(label), 0);
1399 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1400 gtk_size_group_add_widget(sg, label);
1401
1402 editor = talkatu_editor_new();
1403 gtk_box_pack_start(GTK_BOX(hbox), editor, TRUE, TRUE, 0);
1404
1405 dialog->message_view = talkatu_editor_get_input(TALKATU_EDITOR(editor));
1406 dialog->message_buffer = talkatu_html_buffer_new();
1407 gtk_text_view_set_buffer(GTK_TEXT_VIEW(dialog->message_view), dialog->message_buffer);
1408
1409 /* Add the buttons */
1410 gtk_dialog_add_button(GTK_DIALOG(win), _("Cancel"), GTK_RESPONSE_CANCEL);
1411 gtk_dialog_add_button(GTK_DIALOG(win), _("Okay"), GTK_RESPONSE_OK);
1412
1413 /* Seed the input widgets with the current values */
1414
1415 /* Only look at the saved status if we can't find it in the parent status dialog's substatuses model */
1416 gtk_tree_model_get(GTK_TREE_MODEL(status_editor->model), &iter,
1417 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, &parent_dialog_has_substatus, -1);
1418 if (parent_dialog_has_substatus) {
1419 gtk_tree_model_get(GTK_TREE_MODEL(status_editor->model), &iter,
1420 STATUS_EDITOR_COLUMN_STATUS_ID, &status_id,
1421 STATUS_EDITOR_COLUMN_STATUS_MESSAGE, &message, -1);
1422 } else if (status_editor->original_title != NULL) {
1423 PurpleSavedStatus *saved_status = NULL;
1424 PurpleSavedStatusSub *substatus = NULL;
1425
1426 if ((saved_status = purple_savedstatus_find(status_editor->original_title)) != NULL) {
1427 if ((substatus = purple_savedstatus_get_substatus(saved_status, account)) != NULL) {
1428 message = (char *)purple_savedstatus_substatus_get_message(substatus);
1429 status_id = (char *)purple_status_type_get_id(
1430 purple_savedstatus_substatus_get_status_type(substatus));
1431 }
1432 }
1433 }
1434 /* TODO: Else get the generic status type from our parent */
1435
1436 if(status_id != NULL) {
1437 PurpleStatus *status = purple_account_get_status(account, status_id);
1438 PurpleStatusType *type = purple_status_get_status_type(status);
1439
1440 primitive = purple_status_type_get_primitive(type);
1441 }
1442
1443 gtk_combo_box_set_active_id(GTK_COMBO_BOX(combo),
1444 purple_primitive_get_id_from_type(primitive));
1445
1446 if (message) {
1447 talkatu_markup_set_html(TALKATU_BUFFER(dialog->message_buffer), message, -1);
1448 }
1449
1450 if (parent_dialog_has_substatus) {
1451 /* These two were gotten from the parent tree model, so they need to be freed */
1452 g_free(status_id);
1453 g_free(message);
1454 }
1455
1456 gtk_widget_show_all(win);
1457 g_object_unref(sg);
1458 }
1459
1460
1461 /**************************************************************************
1462 * Utilities *
1463 **************************************************************************/
1464 30
1465 enum { 31 enum {
1466 SS_MENU_ENTRY_TYPE_PRIMITIVE, 32 SS_MENU_ENTRY_TYPE_PRIMITIVE,
1467 SS_MENU_ENTRY_TYPE_SAVEDSTATUS 33 SS_MENU_ENTRY_TYPE_SAVEDSTATUS
1468 }; 34 };
1727 g_signal_connect(G_OBJECT(combobox), "destroy", 293 g_signal_connect(G_OBJECT(combobox), "destroy",
1728 G_CALLBACK(purple_signals_disconnect_by_handle), NULL); 294 G_CALLBACK(purple_signals_disconnect_by_handle), NULL);
1729 295
1730 return combobox; 296 return combobox;
1731 } 297 }
1732
1733
1734 /**************************************************************************
1735 * GTK saved status glue
1736 **************************************************************************/
1737
1738 void *
1739 pidgin_status_get_handle(void)
1740 {
1741 static int handle;
1742
1743 return &handle;
1744 }
1745
1746 void
1747 pidgin_status_init(void)
1748 {
1749 }
1750
1751 void
1752 pidgin_status_uninit(void)
1753 {
1754 pidgin_status_window_hide();
1755 }

mercurial