| |
1 /** |
| |
2 * @file gtksavedstatus.c GTK+ Saved Status Editor UI |
| |
3 * @ingroup gtkui |
| |
4 * |
| |
5 * pidgin |
| |
6 * |
| |
7 * Pidgin is the legal property of its developers, whose names are too numerous |
| |
8 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
9 * source distribution. |
| |
10 * |
| |
11 * This program is free software; you can redistribute it and/or modify |
| |
12 * it under the terms of the GNU General Public License as published by |
| |
13 * the Free Software Foundation; either version 2 of the License, or |
| |
14 * (at your option) any later version. |
| |
15 * |
| |
16 * This program is distributed in the hope that it will be useful, |
| |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
19 * GNU General Public License for more details. |
| |
20 * |
| |
21 * You should have received a copy of the GNU General Public License |
| |
22 * along with this program; if not, write to the Free Software |
| |
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
24 */ |
| |
25 |
| |
26 #include "account.h" |
| |
27 #include "internal.h" |
| |
28 #include "notify.h" |
| |
29 #include "request.h" |
| |
30 #include "savedstatuses.h" |
| |
31 #include "status.h" |
| |
32 #include "util.h" |
| |
33 |
| |
34 #include "gtkblist.h" |
| |
35 #include "gtkexpander.h" |
| |
36 #include "pidgin.h" |
| |
37 #include "gtkimhtml.h" |
| |
38 #include "gtkimhtmltoolbar.h" |
| |
39 #include "gtksavedstatuses.h" |
| |
40 #include "pidginstock.h" |
| |
41 #include "gtkutils.h" |
| |
42 |
| |
43 /* |
| |
44 * TODO: Should attach to the account-deleted and account-added signals |
| |
45 * and update the GtkListStores in any StatusEditor windows that |
| |
46 * may be open. |
| |
47 */ |
| |
48 |
| |
49 /** |
| |
50 * These are used for the GtkTreeView when you're scrolling through |
| |
51 * all your saved statuses. |
| |
52 */ |
| |
53 enum |
| |
54 { |
| |
55 STATUS_WINDOW_COLUMN_TITLE, |
| |
56 STATUS_WINDOW_COLUMN_TYPE, |
| |
57 STATUS_WINDOW_COLUMN_MESSAGE, |
| |
58 /** A hidden column containing a pointer to the editor for this saved status. */ |
| |
59 STATUS_WINDOW_COLUMN_WINDOW, |
| |
60 STATUS_WINDOW_NUM_COLUMNS |
| |
61 }; |
| |
62 |
| |
63 /** |
| |
64 * These is used for the GtkTreeView containing the list of accounts |
| |
65 * at the bottom of the window when you're editing a particular |
| |
66 * saved status. |
| |
67 */ |
| |
68 enum |
| |
69 { |
| |
70 /** A hidden column containing a pointer to the PurpleAccount. */ |
| |
71 STATUS_EDITOR_COLUMN_ACCOUNT, |
| |
72 /** A hidden column containing a pointer to the editor for this substatus. */ |
| |
73 STATUS_EDITOR_COLUMN_WINDOW, |
| |
74 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, |
| |
75 STATUS_EDITOR_COLUMN_ICON, |
| |
76 STATUS_EDITOR_COLUMN_SCREENNAME, |
| |
77 /** A hidden column containing the ID of this PurpleStatusType. */ |
| |
78 STATUS_EDITOR_COLUMN_STATUS_ID, |
| |
79 STATUS_EDITOR_COLUMN_STATUS_NAME, |
| |
80 STATUS_EDITOR_COLUMN_STATUS_MESSAGE, |
| |
81 STATUS_EDITOR_NUM_COLUMNS |
| |
82 }; |
| |
83 |
| |
84 /** |
| |
85 * These are used in the GtkComboBox to select the specific |
| |
86 * PurpleStatusType when setting a substatus for a particular saved |
| |
87 * status. |
| |
88 */ |
| |
89 enum |
| |
90 { |
| |
91 SUBSTATUS_COLUMN_ICON, |
| |
92 /** A hidden column containing the ID of this PurpleStatusType. */ |
| |
93 SUBSTATUS_COLUMN_STATUS_ID, |
| |
94 SUBSTATUS_COLUMN_STATUS_NAME, |
| |
95 SUBSTATUS_NUM_COLUMNS |
| |
96 }; |
| |
97 |
| |
98 typedef struct |
| |
99 { |
| |
100 GtkWidget *window; |
| |
101 GtkListStore *model; |
| |
102 GtkWidget *treeview; |
| |
103 GtkWidget *use_button; |
| |
104 GtkWidget *modify_button; |
| |
105 GtkWidget *delete_button; |
| |
106 } StatusWindow; |
| |
107 |
| |
108 typedef struct |
| |
109 { |
| |
110 GtkWidget *window; |
| |
111 GtkListStore *model; |
| |
112 GtkWidget *treeview; |
| |
113 GtkButton *saveanduse_button; |
| |
114 GtkButton *save_button; |
| |
115 |
| |
116 gchar *original_title; |
| |
117 GtkEntry *title; |
| |
118 GtkOptionMenu *type; |
| |
119 GtkIMHtml *message; |
| |
120 } StatusEditor; |
| |
121 |
| |
122 typedef struct |
| |
123 { |
| |
124 StatusEditor *status_editor; |
| |
125 PurpleAccount *account; |
| |
126 |
| |
127 GtkWidget *window; |
| |
128 GtkListStore *model; |
| |
129 GtkComboBox *box; |
| |
130 GtkIMHtml *message; |
| |
131 GtkIMHtmlToolbar *toolbar; |
| |
132 } SubStatusEditor; |
| |
133 |
| |
134 static StatusWindow *status_window = NULL; |
| |
135 |
| |
136 |
| |
137 /************************************************************************** |
| |
138 * Status window |
| |
139 **************************************************************************/ |
| |
140 |
| |
141 static gboolean |
| |
142 status_window_find_savedstatus(GtkTreeIter *iter, const char *title) |
| |
143 { |
| |
144 GtkTreeModel *model; |
| |
145 char *cur; |
| |
146 |
| |
147 if ((status_window == NULL) || (title == NULL)) |
| |
148 return FALSE; |
| |
149 |
| |
150 model = GTK_TREE_MODEL(status_window->model); |
| |
151 |
| |
152 if (!gtk_tree_model_get_iter_first(model, iter)) |
| |
153 return FALSE; |
| |
154 |
| |
155 do { |
| |
156 gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &cur, -1); |
| |
157 if (!strcmp(title, cur)) |
| |
158 { |
| |
159 g_free(cur); |
| |
160 return TRUE; |
| |
161 } |
| |
162 g_free(cur); |
| |
163 } while (gtk_tree_model_iter_next(model, iter)); |
| |
164 |
| |
165 return FALSE; |
| |
166 } |
| |
167 |
| |
168 static gboolean |
| |
169 status_window_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) |
| |
170 { |
| |
171 StatusWindow *dialog = user_data; |
| |
172 |
| |
173 dialog->window = NULL; |
| |
174 pidgin_status_window_hide(); |
| |
175 |
| |
176 return FALSE; |
| |
177 } |
| |
178 |
| |
179 #if !GTK_CHECK_VERSION(2,2,0) |
| |
180 static void |
| |
181 count_selected_helper(GtkTreeModel *model, GtkTreePath *path, |
| |
182 GtkTreeIter *iter, gpointer user_data) |
| |
183 { |
| |
184 (*(gint *)user_data)++; |
| |
185 } |
| |
186 |
| |
187 static void |
| |
188 list_selected_helper(GtkTreeModel *model, GtkTreePath *path, |
| |
189 GtkTreeIter *iter, gpointer user_data) |
| |
190 { |
| |
191 GList **list = (GList **)user_data; |
| |
192 *list = g_list_append(*list, gtk_tree_path_copy(path)); |
| |
193 } |
| |
194 #endif |
| |
195 |
| |
196 static void |
| |
197 status_window_use_cb(GtkButton *button, StatusWindow *dialog) |
| |
198 { |
| |
199 GtkTreeSelection *selection; |
| |
200 GtkTreeIter iter; |
| |
201 GList *list = NULL; |
| |
202 int num_selected = 0; |
| |
203 |
| |
204 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); |
| |
205 |
| |
206 #if GTK_CHECK_VERSION(2,2,0) |
| |
207 num_selected = gtk_tree_selection_count_selected_rows(selection); |
| |
208 #else |
| |
209 gtk_tree_selection_selected_foreach(selection, count_selected_helper, &num_selected); |
| |
210 #endif |
| |
211 if (num_selected != 1) |
| |
212 /* |
| |
213 * This shouldn't happen because the "Use" button should have |
| |
214 * been grayed out. Oh well. |
| |
215 */ |
| |
216 return; |
| |
217 |
| |
218 #if GTK_CHECK_VERSION(2,2,0) |
| |
219 list = gtk_tree_selection_get_selected_rows(selection, NULL); |
| |
220 #else |
| |
221 gtk_tree_selection_selected_foreach(selection, list_selected_helper, &list); |
| |
222 #endif |
| |
223 |
| |
224 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), |
| |
225 &iter, list->data)) |
| |
226 { |
| |
227 gchar *title; |
| |
228 PurpleSavedStatus *saved_status; |
| |
229 gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, |
| |
230 STATUS_WINDOW_COLUMN_TITLE, &title, |
| |
231 -1); |
| |
232 saved_status = purple_savedstatus_find(title); |
| |
233 g_free(title); |
| |
234 purple_savedstatus_activate(saved_status); |
| |
235 } |
| |
236 |
| |
237 g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL); |
| |
238 g_list_free(list); |
| |
239 } |
| |
240 |
| |
241 static void |
| |
242 status_window_add_cb(GtkButton *button, gpointer user_data) |
| |
243 { |
| |
244 pidgin_status_editor_show(FALSE, NULL); |
| |
245 } |
| |
246 |
| |
247 static void |
| |
248 status_window_modify_foreach(GtkTreeModel *model, GtkTreePath *path, |
| |
249 GtkTreeIter *iter, gpointer user_data) |
| |
250 { |
| |
251 gchar *title; |
| |
252 PurpleSavedStatus *saved_status; |
| |
253 |
| |
254 gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1); |
| |
255 saved_status = purple_savedstatus_find(title); |
| |
256 g_free(title); |
| |
257 pidgin_status_editor_show(TRUE, saved_status); |
| |
258 } |
| |
259 |
| |
260 static void |
| |
261 status_window_modify_cb(GtkButton *button, gpointer user_data) |
| |
262 { |
| |
263 StatusWindow *dialog = user_data; |
| |
264 GtkTreeSelection *selection; |
| |
265 |
| |
266 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); |
| |
267 |
| |
268 gtk_tree_selection_selected_foreach(selection, status_window_modify_foreach, user_data); |
| |
269 } |
| |
270 |
| |
271 static void |
| |
272 status_window_delete_cancel_cb(gpointer data) |
| |
273 { |
| |
274 GList *sel_titles = data; |
| |
275 g_list_foreach(sel_titles, (GFunc) g_free, NULL); |
| |
276 g_list_free(sel_titles); |
| |
277 } |
| |
278 |
| |
279 static void |
| |
280 status_window_delete_confirm_cb(gpointer data) |
| |
281 { |
| |
282 GtkTreeIter iter; |
| |
283 GList *sel_titles = data, *l; |
| |
284 char *title; |
| |
285 |
| |
286 for (l = sel_titles; l != NULL; l = l->next) { |
| |
287 title = l->data; |
| |
288 if (status_window_find_savedstatus(&iter, title)) |
| |
289 gtk_list_store_remove(status_window->model, &iter); |
| |
290 purple_savedstatus_delete(title); |
| |
291 g_free(title); |
| |
292 } |
| |
293 g_list_free(sel_titles); |
| |
294 } |
| |
295 |
| |
296 static void |
| |
297 status_window_delete_cb(GtkButton *button, gpointer user_data) |
| |
298 { |
| |
299 StatusWindow *dialog = user_data; |
| |
300 GtkTreeIter iter; |
| |
301 GtkTreeSelection *selection; |
| |
302 GList *sel_paths, *l, *sel_titles = NULL; |
| |
303 GtkTreeModel *model = GTK_TREE_MODEL(dialog->model); |
| |
304 char *title; |
| |
305 |
| |
306 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); |
| |
307 #if GTK_CHECK_VERSION(2,2,0) |
| |
308 sel_paths = gtk_tree_selection_get_selected_rows(selection, NULL); |
| |
309 #else |
| |
310 gtk_tree_selection_selected_foreach(selection, list_selected_helper, &sel_paths); |
| |
311 #endif |
| |
312 |
| |
313 /* This is ugly because we're not allowed to modify the model from within |
| |
314 * gtk_tree_selection_selected_foreach() and the GtkTreePaths can become invalid |
| |
315 * when something is removed from the model. The selection can also change while |
| |
316 * the request dialog is displayed, so we need to capture the selected rows at this time. */ |
| |
317 |
| |
318 for (l = sel_paths; l != NULL; l = l->next) { |
| |
319 if (gtk_tree_model_get_iter(model, &iter, l->data)) { |
| |
320 gtk_tree_model_get(model, &iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1); |
| |
321 sel_titles = g_list_prepend(sel_titles, title); |
| |
322 } |
| |
323 gtk_tree_path_free(l->data); |
| |
324 } |
| |
325 g_list_free(sel_paths); |
| |
326 |
| |
327 if (g_list_length(sel_titles) == 1) |
| |
328 title = g_strdup_printf(_("Are you sure you want to delete %s?"), |
| |
329 (const gchar *)sel_titles->data); |
| |
330 else |
| |
331 title = g_strdup(_("Are you sure you want to delete the selected saved statuses?")); |
| |
332 |
| |
333 purple_request_action(dialog, NULL, title, |
| |
334 NULL, 0, sel_titles, 2, |
| |
335 _("Delete"), status_window_delete_confirm_cb, |
| |
336 _("Cancel"), status_window_delete_cancel_cb); |
| |
337 |
| |
338 g_free(title); |
| |
339 } |
| |
340 |
| |
341 static void |
| |
342 status_window_close_cb(GtkButton *button, gpointer user_data) |
| |
343 { |
| |
344 pidgin_status_window_hide(); |
| |
345 } |
| |
346 |
| |
347 static void |
| |
348 status_selected_cb(GtkTreeSelection *sel, gpointer user_data) |
| |
349 { |
| |
350 StatusWindow *dialog = user_data; |
| |
351 int num_selected = 0; |
| |
352 |
| |
353 #if GTK_CHECK_VERSION(2,2,0) |
| |
354 num_selected = gtk_tree_selection_count_selected_rows(sel); |
| |
355 #else |
| |
356 gtk_tree_selection_selected_foreach(sel, count_selected_helper, &num_selected); |
| |
357 #endif |
| |
358 |
| |
359 gtk_widget_set_sensitive(dialog->use_button, (num_selected == 1)); |
| |
360 gtk_widget_set_sensitive(dialog->modify_button, (num_selected > 0)); |
| |
361 gtk_widget_set_sensitive(dialog->delete_button, (num_selected > 0)); |
| |
362 } |
| |
363 |
| |
364 static void |
| |
365 add_status_to_saved_status_list(GtkListStore *model, PurpleSavedStatus *saved_status) |
| |
366 { |
| |
367 GtkTreeIter iter; |
| |
368 const char *title; |
| |
369 const char *type; |
| |
370 char *message; |
| |
371 |
| |
372 if (purple_savedstatus_is_transient(saved_status)) |
| |
373 return; |
| |
374 |
| |
375 title = purple_savedstatus_get_title(saved_status); |
| |
376 type = purple_primitive_get_name_from_type(purple_savedstatus_get_type(saved_status)); |
| |
377 message = purple_markup_strip_html(purple_savedstatus_get_message(saved_status)); |
| |
378 |
| |
379 gtk_list_store_append(model, &iter); |
| |
380 gtk_list_store_set(model, &iter, |
| |
381 STATUS_WINDOW_COLUMN_TITLE, title, |
| |
382 STATUS_WINDOW_COLUMN_TYPE, type, |
| |
383 STATUS_WINDOW_COLUMN_MESSAGE, message, |
| |
384 -1); |
| |
385 free(message); |
| |
386 } |
| |
387 |
| |
388 static void |
| |
389 populate_saved_status_list(StatusWindow *dialog) |
| |
390 { |
| |
391 const GList *saved_statuses; |
| |
392 |
| |
393 gtk_list_store_clear(dialog->model); |
| |
394 |
| |
395 for (saved_statuses = purple_savedstatuses_get_all(); saved_statuses != NULL; |
| |
396 saved_statuses = g_list_next(saved_statuses)) |
| |
397 { |
| |
398 add_status_to_saved_status_list(dialog->model, saved_statuses->data); |
| |
399 } |
| |
400 } |
| |
401 |
| |
402 static gboolean |
| |
403 search_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer search_data) |
| |
404 { |
| |
405 gboolean result; |
| |
406 char *haystack; |
| |
407 |
| |
408 gtk_tree_model_get(model, iter, column, &haystack, -1); |
| |
409 |
| |
410 result = (purple_strcasestr(haystack, key) == NULL); |
| |
411 |
| |
412 g_free(haystack); |
| |
413 |
| |
414 return result; |
| |
415 } |
| |
416 |
| |
417 static void |
| |
418 savedstatus_activated_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, StatusWindow *dialog) |
| |
419 { |
| |
420 status_window_modify_cb(NULL, dialog); |
| |
421 } |
| |
422 |
| |
423 static GtkWidget * |
| |
424 create_saved_status_list(StatusWindow *dialog) |
| |
425 { |
| |
426 GtkWidget *sw; |
| |
427 GtkWidget *treeview; |
| |
428 GtkTreeSelection *sel; |
| |
429 GtkTreeViewColumn *column; |
| |
430 GtkCellRenderer *renderer; |
| |
431 |
| |
432 /* Create the scrolled window */ |
| |
433 sw = gtk_scrolled_window_new(0, 0); |
| |
434 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), |
| |
435 GTK_POLICY_AUTOMATIC, |
| |
436 GTK_POLICY_ALWAYS); |
| |
437 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), |
| |
438 GTK_SHADOW_IN); |
| |
439 |
| |
440 /* Create the list model */ |
| |
441 dialog->model = gtk_list_store_new(STATUS_WINDOW_NUM_COLUMNS, |
| |
442 G_TYPE_STRING, |
| |
443 G_TYPE_STRING, |
| |
444 G_TYPE_STRING, |
| |
445 G_TYPE_POINTER); |
| |
446 |
| |
447 /* Create the treeview */ |
| |
448 treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); |
| |
449 dialog->treeview = treeview; |
| |
450 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); |
| |
451 g_signal_connect(G_OBJECT(treeview), "row-activated", |
| |
452 G_CALLBACK(savedstatus_activated_cb), dialog); |
| |
453 |
| |
454 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); |
| |
455 gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); |
| |
456 g_signal_connect(G_OBJECT(sel), "changed", |
| |
457 G_CALLBACK(status_selected_cb), dialog); |
| |
458 |
| |
459 gtk_container_add(GTK_CONTAINER(sw), treeview); |
| |
460 |
| |
461 /* Add columns */ |
| |
462 column = gtk_tree_view_column_new(); |
| |
463 gtk_tree_view_column_set_title(column, _("Title")); |
| |
464 gtk_tree_view_column_set_resizable(column, TRUE); |
| |
465 gtk_tree_view_column_set_min_width(column, 100); |
| |
466 gtk_tree_view_column_set_sort_column_id(column, |
| |
467 STATUS_WINDOW_COLUMN_TITLE); |
| |
468 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); |
| |
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_TITLE); |
| |
473 #if GTK_CHECK_VERSION(2,6,0) |
| |
474 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); |
| |
475 #endif |
| |
476 |
| |
477 column = gtk_tree_view_column_new(); |
| |
478 gtk_tree_view_column_set_title(column, _("Type")); |
| |
479 gtk_tree_view_column_set_resizable(column, TRUE); |
| |
480 gtk_tree_view_column_set_sort_column_id(column, |
| |
481 STATUS_WINDOW_COLUMN_TYPE); |
| |
482 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); |
| |
483 renderer = gtk_cell_renderer_text_new(); |
| |
484 gtk_tree_view_column_pack_start(column, renderer, TRUE); |
| |
485 gtk_tree_view_column_add_attribute(column, renderer, "text", |
| |
486 STATUS_WINDOW_COLUMN_TYPE); |
| |
487 |
| |
488 column = gtk_tree_view_column_new(); |
| |
489 gtk_tree_view_column_set_title(column, _("Message")); |
| |
490 gtk_tree_view_column_set_resizable(column, TRUE); |
| |
491 gtk_tree_view_column_set_sort_column_id(column, |
| |
492 STATUS_WINDOW_COLUMN_MESSAGE); |
| |
493 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); |
| |
494 renderer = gtk_cell_renderer_text_new(); |
| |
495 gtk_tree_view_column_pack_start(column, renderer, TRUE); |
| |
496 gtk_tree_view_column_add_attribute(column, renderer, "text", |
| |
497 STATUS_WINDOW_COLUMN_MESSAGE); |
| |
498 #if GTK_CHECK_VERSION(2,6,0) |
| |
499 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); |
| |
500 #endif |
| |
501 |
| |
502 /* Enable CTRL+F searching */ |
| |
503 gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), STATUS_WINDOW_COLUMN_TITLE); |
| |
504 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), search_func, NULL, NULL); |
| |
505 |
| |
506 /* Sort the title column by default */ |
| |
507 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dialog->model), |
| |
508 STATUS_WINDOW_COLUMN_TITLE, |
| |
509 GTK_SORT_ASCENDING); |
| |
510 |
| |
511 /* Populate list */ |
| |
512 populate_saved_status_list(dialog); |
| |
513 |
| |
514 gtk_widget_show_all(sw); |
| |
515 |
| |
516 return sw; |
| |
517 } |
| |
518 |
| |
519 static gboolean |
| |
520 configure_cb(GtkWidget *widget, GdkEventConfigure *event, StatusWindow *dialog) |
| |
521 { |
| |
522 if (GTK_WIDGET_VISIBLE(widget)) |
| |
523 { |
| |
524 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/status/dialog/width", event->width); |
| |
525 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/status/dialog/height", event->height); |
| |
526 } |
| |
527 |
| |
528 return FALSE; |
| |
529 } |
| |
530 |
| |
531 void |
| |
532 pidgin_status_window_show(void) |
| |
533 { |
| |
534 StatusWindow *dialog; |
| |
535 GtkWidget *bbox; |
| |
536 GtkWidget *button; |
| |
537 GtkWidget *list; |
| |
538 GtkWidget *vbox; |
| |
539 GtkWidget *win; |
| |
540 int width, height; |
| |
541 |
| |
542 if (status_window != NULL) |
| |
543 { |
| |
544 gtk_window_present(GTK_WINDOW(status_window->window)); |
| |
545 return; |
| |
546 } |
| |
547 |
| |
548 status_window = dialog = g_new0(StatusWindow, 1); |
| |
549 |
| |
550 width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/status/dialog/width"); |
| |
551 height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/status/dialog/height"); |
| |
552 |
| |
553 dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
| |
554 gtk_window_set_default_size(GTK_WINDOW(win), width, height); |
| |
555 gtk_window_set_role(GTK_WINDOW(win), "statuses"); |
| |
556 gtk_window_set_title(GTK_WINDOW(win), _("Saved Statuses")); |
| |
557 gtk_container_set_border_width(GTK_CONTAINER(win), PIDGIN_HIG_BORDER); |
| |
558 |
| |
559 g_signal_connect(G_OBJECT(win), "delete_event", |
| |
560 G_CALLBACK(status_window_destroy_cb), dialog); |
| |
561 g_signal_connect(G_OBJECT(win), "configure_event", |
| |
562 G_CALLBACK(configure_cb), dialog); |
| |
563 |
| |
564 /* Setup the vbox */ |
| |
565 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); |
| |
566 gtk_container_add(GTK_CONTAINER(win), vbox); |
| |
567 |
| |
568 /* List of saved status states */ |
| |
569 list = create_saved_status_list(dialog); |
| |
570 gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0); |
| |
571 |
| |
572 /* Button box. */ |
| |
573 bbox = gtk_hbutton_box_new(); |
| |
574 gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); |
| |
575 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); |
| |
576 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); |
| |
577 |
| |
578 /* Use button */ |
| |
579 button = pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE, |
| |
580 PIDGIN_BUTTON_HORIZONTAL); |
| |
581 dialog->use_button = button; |
| |
582 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
583 gtk_widget_set_sensitive(button, FALSE); |
| |
584 |
| |
585 g_signal_connect(G_OBJECT(button), "clicked", |
| |
586 G_CALLBACK(status_window_use_cb), dialog); |
| |
587 |
| |
588 /* Add button */ |
| |
589 button = gtk_button_new_from_stock(GTK_STOCK_ADD); |
| |
590 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
591 |
| |
592 g_signal_connect(G_OBJECT(button), "clicked", |
| |
593 G_CALLBACK(status_window_add_cb), dialog); |
| |
594 |
| |
595 /* Modify button */ |
| |
596 button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY); |
| |
597 dialog->modify_button = button; |
| |
598 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
599 gtk_widget_set_sensitive(button, FALSE); |
| |
600 |
| |
601 g_signal_connect(G_OBJECT(button), "clicked", |
| |
602 G_CALLBACK(status_window_modify_cb), dialog); |
| |
603 |
| |
604 /* Delete button */ |
| |
605 button = gtk_button_new_from_stock(GTK_STOCK_DELETE); |
| |
606 dialog->delete_button = button; |
| |
607 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
608 gtk_widget_set_sensitive(button, FALSE); |
| |
609 |
| |
610 g_signal_connect(G_OBJECT(button), "clicked", |
| |
611 G_CALLBACK(status_window_delete_cb), dialog); |
| |
612 |
| |
613 /* Close button */ |
| |
614 button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); |
| |
615 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
616 |
| |
617 g_signal_connect(G_OBJECT(button), "clicked", |
| |
618 G_CALLBACK(status_window_close_cb), dialog); |
| |
619 |
| |
620 gtk_widget_show_all(win); |
| |
621 } |
| |
622 |
| |
623 void |
| |
624 pidgin_status_window_hide(void) |
| |
625 { |
| |
626 if (status_window == NULL) |
| |
627 return; |
| |
628 |
| |
629 if (status_window->window != NULL) |
| |
630 gtk_widget_destroy(status_window->window); |
| |
631 |
| |
632 purple_request_close_with_handle(status_window); |
| |
633 purple_notify_close_with_handle(status_window); |
| |
634 g_free(status_window); |
| |
635 status_window = NULL; |
| |
636 } |
| |
637 |
| |
638 |
| |
639 /************************************************************************** |
| |
640 * Status editor |
| |
641 **************************************************************************/ |
| |
642 |
| |
643 static void substatus_editor_cancel_cb(GtkButton *button, gpointer user_data); |
| |
644 |
| |
645 static void |
| |
646 status_editor_remove_dialog(StatusEditor *dialog) |
| |
647 { |
| |
648 GtkTreeModel *model; |
| |
649 GtkTreeIter iter; |
| |
650 |
| |
651 /* Remove the reference to this dialog from our parent's list store */ |
| |
652 if (status_window_find_savedstatus(&iter, dialog->original_title)) |
| |
653 { |
| |
654 gtk_list_store_set(status_window->model, &iter, |
| |
655 STATUS_WINDOW_COLUMN_WINDOW, NULL, |
| |
656 -1); |
| |
657 } |
| |
658 |
| |
659 /* Close any substatus editors that may be open */ |
| |
660 model = GTK_TREE_MODEL(dialog->model); |
| |
661 if (gtk_tree_model_get_iter_first(model, &iter)) |
| |
662 { |
| |
663 do { |
| |
664 SubStatusEditor *substatus_dialog; |
| |
665 |
| |
666 gtk_tree_model_get(model, &iter, |
| |
667 STATUS_EDITOR_COLUMN_WINDOW, &substatus_dialog, |
| |
668 -1); |
| |
669 if (substatus_dialog != NULL) |
| |
670 { |
| |
671 gtk_list_store_set(dialog->model, &iter, |
| |
672 STATUS_EDITOR_COLUMN_WINDOW, NULL, |
| |
673 -1); |
| |
674 substatus_editor_cancel_cb(NULL, substatus_dialog); |
| |
675 } |
| |
676 } while (gtk_tree_model_iter_next(model, &iter)); |
| |
677 } |
| |
678 } |
| |
679 |
| |
680 |
| |
681 static gboolean |
| |
682 status_editor_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) |
| |
683 { |
| |
684 StatusEditor *dialog = user_data; |
| |
685 |
| |
686 status_editor_remove_dialog(dialog); |
| |
687 g_free(dialog->original_title); |
| |
688 g_free(dialog); |
| |
689 |
| |
690 return FALSE; |
| |
691 } |
| |
692 |
| |
693 static void |
| |
694 status_editor_cancel_cb(GtkButton *button, gpointer user_data) |
| |
695 { |
| |
696 StatusEditor *dialog = user_data; |
| |
697 |
| |
698 status_editor_remove_dialog(dialog); |
| |
699 gtk_widget_destroy(dialog->window); |
| |
700 g_free(dialog->original_title); |
| |
701 g_free(dialog); |
| |
702 } |
| |
703 |
| |
704 static void |
| |
705 status_editor_ok_cb(GtkButton *button, gpointer user_data) |
| |
706 { |
| |
707 StatusEditor *dialog = user_data; |
| |
708 const char *title; |
| |
709 PurpleStatusPrimitive type; |
| |
710 char *message, *unformatted; |
| |
711 PurpleSavedStatus *saved_status = NULL; |
| |
712 GtkTreeModel *model; |
| |
713 GtkTreeIter iter; |
| |
714 |
| |
715 title = gtk_entry_get_text(dialog->title); |
| |
716 |
| |
717 /* |
| |
718 * If we're saving this status, and the title is already taken |
| |
719 * then show an error dialog and don't do anything. |
| |
720 */ |
| |
721 if (((button == dialog->saveanduse_button) || (button == dialog->save_button)) && |
| |
722 (purple_savedstatus_find(title) != NULL) && |
| |
723 ((dialog->original_title == NULL) || (strcmp(title, dialog->original_title)))) |
| |
724 { |
| |
725 purple_notify_error(status_window, NULL, _("Title already in use. You must " |
| |
726 "choose a unique title."), NULL); |
| |
727 return; |
| |
728 } |
| |
729 |
| |
730 type = gtk_option_menu_get_history(dialog->type) + (PURPLE_STATUS_UNSET + 1); |
| |
731 message = gtk_imhtml_get_markup(dialog->message); |
| |
732 unformatted = purple_markup_strip_html(message); |
| |
733 |
| |
734 /* |
| |
735 * If we're editing an old status, then lookup the old status. |
| |
736 * Note: It is possible that it has been deleted or renamed |
| |
737 * or something, and no longer exists. |
| |
738 */ |
| |
739 if (dialog->original_title != NULL) |
| |
740 { |
| |
741 GtkTreeIter iter; |
| |
742 |
| |
743 saved_status = purple_savedstatus_find(dialog->original_title); |
| |
744 |
| |
745 if (status_window_find_savedstatus(&iter, dialog->original_title)) |
| |
746 gtk_list_store_remove(status_window->model, &iter); |
| |
747 } |
| |
748 |
| |
749 if (saved_status == NULL) |
| |
750 { |
| |
751 /* This is a new status */ |
| |
752 if ((button == dialog->saveanduse_button) |
| |
753 || (button == dialog->save_button)) |
| |
754 saved_status = purple_savedstatus_new(title, type); |
| |
755 else |
| |
756 saved_status = purple_savedstatus_new(NULL, type); |
| |
757 } |
| |
758 else |
| |
759 { |
| |
760 /* Modify the old status */ |
| |
761 if (strcmp(title, dialog->original_title)) |
| |
762 purple_savedstatus_set_title(saved_status, title); |
| |
763 purple_savedstatus_set_type(saved_status, type); |
| |
764 } |
| |
765 |
| |
766 if (*unformatted == '\0') |
| |
767 purple_savedstatus_set_message(saved_status, NULL); |
| |
768 else |
| |
769 purple_savedstatus_set_message(saved_status, message); |
| |
770 |
| |
771 /* Set any substatuses */ |
| |
772 model = GTK_TREE_MODEL(dialog->model); |
| |
773 if (gtk_tree_model_get_iter_first(model, &iter)) |
| |
774 { |
| |
775 do { |
| |
776 PurpleAccount *account; |
| |
777 gboolean enabled; |
| |
778 char *id; |
| |
779 char *message; |
| |
780 PurpleStatusType *type; |
| |
781 |
| |
782 gtk_tree_model_get(model, &iter, |
| |
783 STATUS_EDITOR_COLUMN_ACCOUNT, &account, |
| |
784 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, &enabled, |
| |
785 STATUS_EDITOR_COLUMN_STATUS_ID, &id, |
| |
786 STATUS_EDITOR_COLUMN_STATUS_MESSAGE, &message, |
| |
787 -1); |
| |
788 if (enabled) |
| |
789 { |
| |
790 type = purple_account_get_status_type(account, id); |
| |
791 purple_savedstatus_set_substatus(saved_status, account, type, message); |
| |
792 } |
| |
793 else |
| |
794 { |
| |
795 purple_savedstatus_unset_substatus(saved_status, account); |
| |
796 } |
| |
797 g_free(id); |
| |
798 g_free(message); |
| |
799 } while (gtk_tree_model_iter_next(model, &iter)); |
| |
800 } |
| |
801 |
| |
802 g_free(message); |
| |
803 g_free(unformatted); |
| |
804 |
| |
805 status_editor_remove_dialog(dialog); |
| |
806 gtk_widget_destroy(dialog->window); |
| |
807 g_free(dialog->original_title); |
| |
808 |
| |
809 if (status_window != NULL) |
| |
810 add_status_to_saved_status_list(status_window->model, saved_status); |
| |
811 |
| |
812 /* If they clicked on "Save & Use" or "Use," then activate the status */ |
| |
813 if (button != dialog->save_button) |
| |
814 purple_savedstatus_activate(saved_status); |
| |
815 |
| |
816 g_free(dialog); |
| |
817 } |
| |
818 |
| |
819 static void |
| |
820 editor_title_changed_cb(GtkWidget *widget, gpointer user_data) |
| |
821 { |
| |
822 StatusEditor *dialog = user_data; |
| |
823 const gchar *text; |
| |
824 |
| |
825 text = gtk_entry_get_text(dialog->title); |
| |
826 |
| |
827 gtk_widget_set_sensitive(GTK_WIDGET(dialog->saveanduse_button), (*text != '\0')); |
| |
828 gtk_widget_set_sensitive(GTK_WIDGET(dialog->save_button), (*text != '\0')); |
| |
829 } |
| |
830 |
| |
831 static GtkWidget * |
| |
832 create_status_type_menu(PurpleStatusPrimitive type) |
| |
833 { |
| |
834 int i; |
| |
835 GtkWidget *dropdown; |
| |
836 GtkWidget *menu; |
| |
837 GtkWidget *item; |
| |
838 |
| |
839 dropdown = gtk_option_menu_new(); |
| |
840 menu = gtk_menu_new(); |
| |
841 |
| |
842 for (i = PURPLE_STATUS_UNSET + 1; i < PURPLE_STATUS_NUM_PRIMITIVES; i++) |
| |
843 { |
| |
844 item = gtk_menu_item_new_with_label(purple_primitive_get_name_from_type(i)); |
| |
845 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); |
| |
846 } |
| |
847 |
| |
848 gtk_menu_set_active(GTK_MENU(menu), type - (PURPLE_STATUS_UNSET + 1)); |
| |
849 gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu); |
| |
850 gtk_widget_show_all(menu); |
| |
851 |
| |
852 return dropdown; |
| |
853 } |
| |
854 |
| |
855 static void edit_substatus(StatusEditor *status_editor, PurpleAccount *account); |
| |
856 |
| |
857 static void |
| |
858 edit_substatus_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer user_data) |
| |
859 { |
| |
860 StatusEditor *dialog = user_data; |
| |
861 GtkTreeIter iter; |
| |
862 PurpleAccount *account; |
| |
863 |
| |
864 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path); |
| |
865 gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, |
| |
866 STATUS_EDITOR_COLUMN_ACCOUNT, &account, |
| |
867 -1); |
| |
868 |
| |
869 edit_substatus(dialog, account); |
| |
870 } |
| |
871 |
| |
872 static void |
| |
873 status_editor_substatus_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpointer data) |
| |
874 { |
| |
875 StatusEditor *dialog = (StatusEditor *)data; |
| |
876 GtkTreeIter iter; |
| |
877 gboolean enabled; |
| |
878 PurpleAccount *account; |
| |
879 |
| |
880 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(dialog->model), &iter, path_str); |
| |
881 gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, |
| |
882 STATUS_EDITOR_COLUMN_ACCOUNT, &account, |
| |
883 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, &enabled, |
| |
884 -1); |
| |
885 |
| |
886 enabled = !enabled; |
| |
887 |
| |
888 if (enabled) |
| |
889 { |
| |
890 edit_substatus(dialog, account); |
| |
891 } |
| |
892 else |
| |
893 { |
| |
894 /* Remove the substatus */ |
| |
895 gtk_list_store_set(dialog->model, &iter, |
| |
896 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, enabled, |
| |
897 STATUS_EDITOR_COLUMN_STATUS_ID, NULL, |
| |
898 STATUS_EDITOR_COLUMN_STATUS_NAME, NULL, |
| |
899 STATUS_EDITOR_COLUMN_STATUS_MESSAGE, NULL, |
| |
900 -1); |
| |
901 } |
| |
902 } |
| |
903 |
| |
904 static void |
| |
905 status_editor_add_columns(StatusEditor *dialog) |
| |
906 { |
| |
907 GtkCellRenderer *renderer; |
| |
908 GtkTreeViewColumn *column; |
| |
909 |
| |
910 /* Enable Different status column */ |
| |
911 renderer = gtk_cell_renderer_toggle_new(); |
| |
912 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dialog->treeview), |
| |
913 -1, _("Different"), |
| |
914 renderer, |
| |
915 "active", STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, |
| |
916 NULL); |
| |
917 g_signal_connect(G_OBJECT(renderer), "toggled", |
| |
918 G_CALLBACK(status_editor_substatus_cb), dialog); |
| |
919 |
| |
920 /* Screen Name column */ |
| |
921 column = gtk_tree_view_column_new(); |
| |
922 gtk_tree_view_column_set_resizable(column, TRUE); |
| |
923 gtk_tree_view_column_set_title(column, _("Screen Name")); |
| |
924 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1); |
| |
925 gtk_tree_view_column_set_resizable(column, TRUE); |
| |
926 |
| |
927 /* Icon */ |
| |
928 renderer = gtk_cell_renderer_pixbuf_new(); |
| |
929 gtk_tree_view_column_pack_start(column, renderer, FALSE); |
| |
930 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", |
| |
931 STATUS_EDITOR_COLUMN_ICON); |
| |
932 |
| |
933 /* Screen Name */ |
| |
934 renderer = gtk_cell_renderer_text_new(); |
| |
935 gtk_tree_view_column_pack_start(column, renderer, TRUE); |
| |
936 gtk_tree_view_column_add_attribute(column, renderer, "text", |
| |
937 STATUS_EDITOR_COLUMN_SCREENNAME); |
| |
938 |
| |
939 /* Status column */ |
| |
940 column = gtk_tree_view_column_new(); |
| |
941 gtk_tree_view_column_set_resizable(column, TRUE); |
| |
942 gtk_tree_view_column_set_title(column, _("Status")); |
| |
943 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1); |
| |
944 gtk_tree_view_column_set_resizable(column, TRUE); |
| |
945 renderer = gtk_cell_renderer_text_new(); |
| |
946 gtk_tree_view_column_pack_start(column, renderer, TRUE); |
| |
947 gtk_tree_view_column_add_attribute(column, renderer, "text", |
| |
948 STATUS_EDITOR_COLUMN_STATUS_NAME); |
| |
949 |
| |
950 /* Message column */ |
| |
951 column = gtk_tree_view_column_new(); |
| |
952 gtk_tree_view_column_set_resizable(column, TRUE); |
| |
953 gtk_tree_view_column_set_title(column, _("Message")); |
| |
954 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1); |
| |
955 gtk_tree_view_column_set_resizable(column, TRUE); |
| |
956 renderer = gtk_cell_renderer_text_new(); |
| |
957 gtk_tree_view_column_pack_start(column, renderer, TRUE); |
| |
958 gtk_tree_view_column_add_attribute(column, renderer, "text", |
| |
959 STATUS_EDITOR_COLUMN_STATUS_MESSAGE); |
| |
960 |
| |
961 g_signal_connect(G_OBJECT(dialog->treeview), "row-activated", |
| |
962 G_CALLBACK(edit_substatus_cb), dialog); |
| |
963 } |
| |
964 |
| |
965 static void |
| |
966 status_editor_set_account(GtkListStore *store, PurpleAccount *account, |
| |
967 GtkTreeIter *iter, PurpleSavedStatusSub *substatus) |
| |
968 { |
| |
969 GdkPixbuf *pixbuf; |
| |
970 const char *id = NULL, *name = NULL, *message = NULL; |
| |
971 |
| |
972 pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM); |
| |
973 if ((pixbuf != NULL) && !purple_account_is_connected(account)) |
| |
974 { |
| |
975 gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE); |
| |
976 } |
| |
977 |
| |
978 if (substatus != NULL) |
| |
979 { |
| |
980 const PurpleStatusType *type; |
| |
981 |
| |
982 type = purple_savedstatus_substatus_get_type(substatus); |
| |
983 id = purple_status_type_get_id(type); |
| |
984 name = purple_status_type_get_name(type); |
| |
985 if (purple_status_type_get_attr(type, "message")) |
| |
986 message = purple_savedstatus_substatus_get_message(substatus); |
| |
987 } |
| |
988 |
| |
989 gtk_list_store_set(store, iter, |
| |
990 STATUS_EDITOR_COLUMN_ACCOUNT, account, |
| |
991 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, (substatus != NULL), |
| |
992 STATUS_EDITOR_COLUMN_ICON, pixbuf, |
| |
993 STATUS_EDITOR_COLUMN_SCREENNAME, purple_account_get_username(account), |
| |
994 STATUS_EDITOR_COLUMN_STATUS_ID, id, |
| |
995 STATUS_EDITOR_COLUMN_STATUS_NAME, name, |
| |
996 STATUS_EDITOR_COLUMN_STATUS_MESSAGE, message, |
| |
997 -1); |
| |
998 |
| |
999 if (pixbuf != NULL) |
| |
1000 g_object_unref(G_OBJECT(pixbuf)); |
| |
1001 } |
| |
1002 |
| |
1003 static void |
| |
1004 status_editor_add_account(StatusEditor *dialog, PurpleAccount *account, |
| |
1005 PurpleSavedStatusSub *substatus) |
| |
1006 { |
| |
1007 GtkTreeIter iter; |
| |
1008 |
| |
1009 gtk_list_store_append(dialog->model, &iter); |
| |
1010 |
| |
1011 status_editor_set_account(dialog->model, account, &iter, substatus); |
| |
1012 } |
| |
1013 |
| |
1014 static void |
| |
1015 status_editor_populate_list(StatusEditor *dialog, PurpleSavedStatus *saved_status) |
| |
1016 { |
| |
1017 GList *iter; |
| |
1018 PurpleSavedStatusSub *substatus; |
| |
1019 |
| |
1020 gtk_list_store_clear(dialog->model); |
| |
1021 |
| |
1022 for (iter = purple_accounts_get_all(); iter != NULL; iter = iter->next) |
| |
1023 { |
| |
1024 PurpleAccount *account = (PurpleAccount *)iter->data; |
| |
1025 |
| |
1026 if (saved_status != NULL) |
| |
1027 substatus = purple_savedstatus_get_substatus(saved_status, account); |
| |
1028 else |
| |
1029 substatus = NULL; |
| |
1030 |
| |
1031 status_editor_add_account(dialog, account, substatus); |
| |
1032 } |
| |
1033 } |
| |
1034 |
| |
1035 void |
| |
1036 pidgin_status_editor_show(gboolean edit, PurpleSavedStatus *saved_status) |
| |
1037 { |
| |
1038 GtkTreeIter iter; |
| |
1039 StatusEditor *dialog; |
| |
1040 GtkSizeGroup *sg; |
| |
1041 GtkWidget *bbox; |
| |
1042 GtkWidget *button; |
| |
1043 GtkWidget *dbox; |
| |
1044 GtkWidget *expander; |
| |
1045 GtkWidget *dropdown; |
| |
1046 GtkWidget *entry; |
| |
1047 GtkWidget *frame; |
| |
1048 GtkWidget *hbox; |
| |
1049 GtkWidget *label; |
| |
1050 GtkWidget *sw; |
| |
1051 GtkWidget *text; |
| |
1052 GtkWidget *toolbar; |
| |
1053 GtkWidget *vbox; |
| |
1054 GtkWidget *win; |
| |
1055 GList *focus_chain = NULL; |
| |
1056 |
| |
1057 if (edit) |
| |
1058 { |
| |
1059 g_return_if_fail(saved_status != NULL); |
| |
1060 g_return_if_fail(!purple_savedstatus_is_transient(saved_status)); |
| |
1061 } |
| |
1062 |
| |
1063 /* Find a possible window for this saved status and present it */ |
| |
1064 if (edit && status_window_find_savedstatus(&iter, purple_savedstatus_get_title(saved_status))) |
| |
1065 { |
| |
1066 gtk_tree_model_get(GTK_TREE_MODEL(status_window->model), &iter, |
| |
1067 STATUS_WINDOW_COLUMN_WINDOW, &dialog, |
| |
1068 -1); |
| |
1069 if (dialog != NULL) |
| |
1070 { |
| |
1071 gtk_window_present(GTK_WINDOW(dialog->window)); |
| |
1072 return; |
| |
1073 } |
| |
1074 } |
| |
1075 |
| |
1076 dialog = g_new0(StatusEditor, 1); |
| |
1077 if (edit && status_window_find_savedstatus(&iter, purple_savedstatus_get_title(saved_status))) |
| |
1078 { |
| |
1079 gtk_list_store_set(status_window->model, &iter, |
| |
1080 STATUS_WINDOW_COLUMN_WINDOW, dialog, |
| |
1081 -1); |
| |
1082 } |
| |
1083 |
| |
1084 if (edit) |
| |
1085 dialog->original_title = g_strdup(purple_savedstatus_get_title(saved_status)); |
| |
1086 |
| |
1087 dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
| |
1088 gtk_window_set_role(GTK_WINDOW(win), "status"); |
| |
1089 gtk_window_set_title(GTK_WINDOW(win), _("Status")); |
| |
1090 gtk_window_set_resizable(GTK_WINDOW(win), FALSE); |
| |
1091 gtk_container_set_border_width(GTK_CONTAINER(win), PIDGIN_HIG_BORDER); |
| |
1092 |
| |
1093 g_signal_connect(G_OBJECT(win), "delete_event", |
| |
1094 G_CALLBACK(status_editor_destroy_cb), dialog); |
| |
1095 |
| |
1096 /* Setup the vbox */ |
| |
1097 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); |
| |
1098 gtk_container_add(GTK_CONTAINER(win), vbox); |
| |
1099 |
| |
1100 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); |
| |
1101 |
| |
1102 /* Title */ |
| |
1103 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); |
| |
1104 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
| |
1105 |
| |
1106 label = gtk_label_new_with_mnemonic(_("_Title:")); |
| |
1107 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
1108 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
| |
1109 gtk_size_group_add_widget(sg, label); |
| |
1110 |
| |
1111 entry = gtk_entry_new(); |
| |
1112 dialog->title = GTK_ENTRY(entry); |
| |
1113 if ((saved_status != NULL) |
| |
1114 && !purple_savedstatus_is_transient(saved_status) |
| |
1115 && (purple_savedstatus_get_title(saved_status) != NULL)) |
| |
1116 gtk_entry_set_text(GTK_ENTRY(entry), purple_savedstatus_get_title(saved_status)); |
| |
1117 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); |
| |
1118 g_signal_connect(G_OBJECT(entry), "changed", |
| |
1119 G_CALLBACK(editor_title_changed_cb), dialog); |
| |
1120 |
| |
1121 /* Status type */ |
| |
1122 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); |
| |
1123 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
| |
1124 |
| |
1125 label = gtk_label_new_with_mnemonic(_("_Status:")); |
| |
1126 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
1127 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
| |
1128 gtk_size_group_add_widget(sg, label); |
| |
1129 |
| |
1130 if (saved_status != NULL) |
| |
1131 dropdown = create_status_type_menu(purple_savedstatus_get_type(saved_status)); |
| |
1132 else |
| |
1133 dropdown = create_status_type_menu(PURPLE_STATUS_AWAY); |
| |
1134 dialog->type = GTK_OPTION_MENU(dropdown); |
| |
1135 gtk_box_pack_start(GTK_BOX(hbox), dropdown, TRUE, TRUE, 0); |
| |
1136 |
| |
1137 /* Status message */ |
| |
1138 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); |
| |
1139 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
| |
1140 |
| |
1141 label = gtk_label_new_with_mnemonic(_("_Message:")); |
| |
1142 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
1143 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
| |
1144 gtk_size_group_add_widget(sg, label); |
| |
1145 |
| |
1146 frame = pidgin_create_imhtml(TRUE, &text, &toolbar, NULL); |
| |
1147 dialog->message = GTK_IMHTML(text); |
| |
1148 gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); |
| |
1149 focus_chain = g_list_prepend(focus_chain, dialog->message); |
| |
1150 gtk_container_set_focus_chain(GTK_CONTAINER(hbox), focus_chain); |
| |
1151 |
| |
1152 if ((saved_status != NULL) && (purple_savedstatus_get_message(saved_status) != NULL)) |
| |
1153 gtk_imhtml_append_text(GTK_IMHTML(text), |
| |
1154 purple_savedstatus_get_message(saved_status), 0); |
| |
1155 |
| |
1156 /* Different status message expander */ |
| |
1157 expander = gtk_expander_new_with_mnemonic(_("Use a _different status for some accounts")); |
| |
1158 gtk_box_pack_start(GTK_BOX(vbox), expander, FALSE, FALSE, 0); |
| |
1159 |
| |
1160 /* Setup the box that the expander will cover */ |
| |
1161 dbox = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); |
| |
1162 gtk_container_add(GTK_CONTAINER(expander), dbox); |
| |
1163 |
| |
1164 /* Different status message treeview */ |
| |
1165 sw = gtk_scrolled_window_new(NULL, NULL); |
| |
1166 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), |
| |
1167 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); |
| |
1168 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), |
| |
1169 GTK_SHADOW_IN); |
| |
1170 gtk_box_pack_start(GTK_BOX(dbox), sw, TRUE, TRUE, 0); |
| |
1171 |
| |
1172 /* Create the list model */ |
| |
1173 dialog->model = gtk_list_store_new(STATUS_EDITOR_NUM_COLUMNS, |
| |
1174 G_TYPE_POINTER, |
| |
1175 G_TYPE_POINTER, |
| |
1176 G_TYPE_BOOLEAN, |
| |
1177 GDK_TYPE_PIXBUF, |
| |
1178 G_TYPE_STRING, |
| |
1179 G_TYPE_STRING, |
| |
1180 G_TYPE_STRING, |
| |
1181 G_TYPE_STRING); |
| |
1182 |
| |
1183 /* Create the treeview */ |
| |
1184 dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); |
| |
1185 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(dialog->treeview), TRUE); |
| |
1186 gtk_widget_set_size_request(dialog->treeview, -1, 150); |
| |
1187 gtk_container_add(GTK_CONTAINER(sw), dialog->treeview); |
| |
1188 |
| |
1189 /* Add columns */ |
| |
1190 status_editor_add_columns(dialog); |
| |
1191 |
| |
1192 /* Populate list */ |
| |
1193 status_editor_populate_list(dialog, saved_status); |
| |
1194 |
| |
1195 /* Expand the treeview if we have substatuses */ |
| |
1196 gtk_expander_set_expanded(GTK_EXPANDER(expander), |
| |
1197 (saved_status != NULL) && purple_savedstatus_has_substatuses(saved_status)); |
| |
1198 |
| |
1199 /* Button box */ |
| |
1200 bbox = gtk_hbutton_box_new(); |
| |
1201 gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); |
| |
1202 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); |
| |
1203 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); |
| |
1204 |
| |
1205 /* Cancel button */ |
| |
1206 button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); |
| |
1207 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
1208 |
| |
1209 g_signal_connect(G_OBJECT(button), "clicked", |
| |
1210 G_CALLBACK(status_editor_cancel_cb), dialog); |
| |
1211 |
| |
1212 /* Use button */ |
| |
1213 button = pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE, |
| |
1214 PIDGIN_BUTTON_HORIZONTAL); |
| |
1215 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
1216 |
| |
1217 g_signal_connect(G_OBJECT(button), "clicked", |
| |
1218 G_CALLBACK(status_editor_ok_cb), dialog); |
| |
1219 |
| |
1220 /* Save & Use button */ |
| |
1221 button = pidgin_pixbuf_button_from_stock(_("Sa_ve & Use"), GTK_STOCK_OK, |
| |
1222 PIDGIN_BUTTON_HORIZONTAL); |
| |
1223 dialog->saveanduse_button = GTK_BUTTON(button); |
| |
1224 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
1225 if (dialog->original_title == NULL) |
| |
1226 gtk_widget_set_sensitive(button, FALSE); |
| |
1227 |
| |
1228 g_signal_connect(G_OBJECT(button), "clicked", |
| |
1229 G_CALLBACK(status_editor_ok_cb), dialog); |
| |
1230 |
| |
1231 /* Save button */ |
| |
1232 button = gtk_button_new_from_stock(GTK_STOCK_SAVE); |
| |
1233 dialog->save_button = GTK_BUTTON(button); |
| |
1234 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
1235 if (dialog->original_title == NULL) |
| |
1236 gtk_widget_set_sensitive(button, FALSE); |
| |
1237 |
| |
1238 g_signal_connect(G_OBJECT(button), "clicked", |
| |
1239 G_CALLBACK(status_editor_ok_cb), dialog); |
| |
1240 |
| |
1241 gtk_widget_show_all(win); |
| |
1242 } |
| |
1243 |
| |
1244 |
| |
1245 /************************************************************************** |
| |
1246 * SubStatus editor |
| |
1247 **************************************************************************/ |
| |
1248 |
| |
1249 static void |
| |
1250 substatus_selection_changed_cb(GtkComboBox *box, gpointer user_data) |
| |
1251 { |
| |
1252 SubStatusEditor *select = user_data; |
| |
1253 GtkTreeIter iter; |
| |
1254 char *id; |
| |
1255 PurpleStatusType *type; |
| |
1256 |
| |
1257 if (!gtk_combo_box_get_active_iter(box, &iter)) |
| |
1258 return; |
| |
1259 gtk_tree_model_get(GTK_TREE_MODEL(select->model), &iter, |
| |
1260 SUBSTATUS_COLUMN_STATUS_ID, &id, |
| |
1261 -1); |
| |
1262 type = purple_account_get_status_type(select->account, id); |
| |
1263 g_free(id); |
| |
1264 |
| |
1265 if (purple_status_type_get_attr(type, "message") == NULL) |
| |
1266 { |
| |
1267 gtk_widget_set_sensitive(GTK_WIDGET(select->message), FALSE); |
| |
1268 gtk_widget_set_sensitive(GTK_WIDGET(select->toolbar), FALSE); |
| |
1269 } |
| |
1270 else |
| |
1271 { |
| |
1272 gtk_widget_set_sensitive(GTK_WIDGET(select->message), TRUE); |
| |
1273 gtk_widget_set_sensitive(GTK_WIDGET(select->toolbar), TRUE); |
| |
1274 } |
| |
1275 } |
| |
1276 |
| |
1277 static gboolean |
| |
1278 status_editor_find_account_in_treemodel(GtkTreeIter *iter, |
| |
1279 StatusEditor *status_editor, |
| |
1280 PurpleAccount *account) |
| |
1281 { |
| |
1282 GtkTreeModel *model; |
| |
1283 PurpleAccount *cur; |
| |
1284 |
| |
1285 g_return_val_if_fail(status_editor != NULL, FALSE); |
| |
1286 g_return_val_if_fail(account != NULL, FALSE); |
| |
1287 |
| |
1288 model = GTK_TREE_MODEL(status_editor->model); |
| |
1289 |
| |
1290 if (!gtk_tree_model_get_iter_first(model, iter)) |
| |
1291 return FALSE; |
| |
1292 |
| |
1293 do { |
| |
1294 gtk_tree_model_get(model, iter, STATUS_EDITOR_COLUMN_ACCOUNT, &cur, -1); |
| |
1295 if (cur == account) |
| |
1296 return TRUE; |
| |
1297 } while (gtk_tree_model_iter_next(model, iter)); |
| |
1298 |
| |
1299 return FALSE; |
| |
1300 } |
| |
1301 |
| |
1302 static void |
| |
1303 substatus_editor_remove_dialog(SubStatusEditor *dialog) |
| |
1304 { |
| |
1305 GtkTreeIter iter; |
| |
1306 |
| |
1307 if (status_editor_find_account_in_treemodel(&iter, dialog->status_editor, dialog->account)) |
| |
1308 { |
| |
1309 /* Remove the reference to this dialog from our parent's list store */ |
| |
1310 gtk_list_store_set(dialog->status_editor->model, &iter, |
| |
1311 STATUS_EDITOR_COLUMN_WINDOW, NULL, |
| |
1312 -1); |
| |
1313 } |
| |
1314 } |
| |
1315 |
| |
1316 static gboolean |
| |
1317 substatus_editor_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) |
| |
1318 { |
| |
1319 SubStatusEditor *dialog = user_data; |
| |
1320 |
| |
1321 substatus_editor_remove_dialog(dialog); |
| |
1322 g_free(dialog); |
| |
1323 |
| |
1324 return FALSE; |
| |
1325 } |
| |
1326 |
| |
1327 static void |
| |
1328 substatus_editor_cancel_cb(GtkButton *button, gpointer user_data) |
| |
1329 { |
| |
1330 SubStatusEditor *dialog = user_data; |
| |
1331 |
| |
1332 substatus_editor_remove_dialog(dialog); |
| |
1333 gtk_widget_destroy(dialog->window); |
| |
1334 g_free(dialog); |
| |
1335 } |
| |
1336 |
| |
1337 |
| |
1338 static void |
| |
1339 substatus_editor_ok_cb(GtkButton *button, gpointer user_data) |
| |
1340 { |
| |
1341 SubStatusEditor *dialog = user_data; |
| |
1342 StatusEditor *status_editor; |
| |
1343 GtkTreeIter iter; |
| |
1344 PurpleStatusType *type; |
| |
1345 char *id = NULL; |
| |
1346 char *message = NULL; |
| |
1347 const char *name = NULL; |
| |
1348 |
| |
1349 if (!gtk_combo_box_get_active_iter(dialog->box, &iter)) |
| |
1350 { |
| |
1351 gtk_widget_destroy(dialog->window); |
| |
1352 g_free(dialog); |
| |
1353 return; |
| |
1354 } |
| |
1355 |
| |
1356 gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, |
| |
1357 SUBSTATUS_COLUMN_STATUS_ID, &id, |
| |
1358 -1); |
| |
1359 type = purple_account_get_status_type(dialog->account, id); |
| |
1360 if (purple_status_type_get_attr(type, "message") != NULL) |
| |
1361 message = gtk_imhtml_get_text(GTK_IMHTML(dialog->message), NULL, NULL); |
| |
1362 name = purple_status_type_get_name(type); |
| |
1363 |
| |
1364 status_editor = dialog->status_editor; |
| |
1365 |
| |
1366 if (status_editor_find_account_in_treemodel(&iter, status_editor, dialog->account)) |
| |
1367 { |
| |
1368 gtk_list_store_set(status_editor->model, &iter, |
| |
1369 STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, TRUE, |
| |
1370 STATUS_EDITOR_COLUMN_STATUS_ID, id, |
| |
1371 STATUS_EDITOR_COLUMN_STATUS_NAME, name, |
| |
1372 STATUS_EDITOR_COLUMN_STATUS_MESSAGE, message, |
| |
1373 STATUS_EDITOR_COLUMN_WINDOW, NULL, |
| |
1374 -1); |
| |
1375 } |
| |
1376 |
| |
1377 gtk_widget_destroy(dialog->window); |
| |
1378 g_free(id); |
| |
1379 g_free(message); |
| |
1380 g_free(dialog); |
| |
1381 } |
| |
1382 |
| |
1383 static void |
| |
1384 edit_substatus(StatusEditor *status_editor, PurpleAccount *account) |
| |
1385 { |
| |
1386 char *tmp; |
| |
1387 SubStatusEditor *dialog; |
| |
1388 GtkSizeGroup *sg; |
| |
1389 GtkWidget *bbox; |
| |
1390 GtkWidget *button; |
| |
1391 GtkWidget *combo; |
| |
1392 GtkWidget *hbox; |
| |
1393 GtkWidget *frame; |
| |
1394 GtkWidget *label; |
| |
1395 GtkWidget *text; |
| |
1396 GtkWidget *toolbar; |
| |
1397 GtkWidget *vbox; |
| |
1398 GtkWidget *win; |
| |
1399 GtkTreeIter iter; |
| |
1400 GtkCellRenderer *rend; |
| |
1401 const char *status_id = NULL; |
| |
1402 const GList *list; |
| |
1403 gboolean select = FALSE; |
| |
1404 |
| |
1405 g_return_if_fail(status_editor != NULL); |
| |
1406 g_return_if_fail(account != NULL); |
| |
1407 |
| |
1408 status_editor_find_account_in_treemodel(&iter, status_editor, account); |
| |
1409 gtk_tree_model_get(GTK_TREE_MODEL(status_editor->model), &iter, |
| |
1410 STATUS_EDITOR_COLUMN_WINDOW, &dialog, |
| |
1411 -1); |
| |
1412 if (dialog != NULL) |
| |
1413 { |
| |
1414 gtk_window_present(GTK_WINDOW(dialog->window)); |
| |
1415 return; |
| |
1416 } |
| |
1417 |
| |
1418 dialog = g_new0(SubStatusEditor, 1); |
| |
1419 gtk_list_store_set(status_editor->model, &iter, |
| |
1420 STATUS_EDITOR_COLUMN_WINDOW, dialog, |
| |
1421 -1); |
| |
1422 dialog->status_editor = status_editor; |
| |
1423 dialog->account = account; |
| |
1424 |
| |
1425 dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
| |
1426 gtk_window_set_role(GTK_WINDOW(win), "substatus"); |
| |
1427 tmp = g_strdup_printf(_("Status for %s"), purple_account_get_username(account)); |
| |
1428 gtk_window_set_title(GTK_WINDOW(win), tmp); |
| |
1429 g_free(tmp); |
| |
1430 gtk_window_set_resizable(GTK_WINDOW(win), FALSE); |
| |
1431 gtk_container_set_border_width(GTK_CONTAINER(win), PIDGIN_HIG_BORDER); |
| |
1432 |
| |
1433 g_signal_connect(G_OBJECT(win), "delete_event", |
| |
1434 G_CALLBACK(substatus_editor_destroy_cb), dialog); |
| |
1435 |
| |
1436 /* Setup the vbox */ |
| |
1437 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); |
| |
1438 gtk_container_add(GTK_CONTAINER(win), vbox); |
| |
1439 |
| |
1440 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); |
| |
1441 |
| |
1442 /* Status type */ |
| |
1443 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); |
| |
1444 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
| |
1445 |
| |
1446 label = gtk_label_new_with_mnemonic(_("_Status:")); |
| |
1447 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
1448 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
| |
1449 gtk_size_group_add_widget(sg, label); |
| |
1450 |
| |
1451 dialog->model = gtk_list_store_new(SUBSTATUS_NUM_COLUMNS, |
| |
1452 GDK_TYPE_PIXBUF, |
| |
1453 G_TYPE_STRING, |
| |
1454 G_TYPE_STRING); |
| |
1455 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(dialog->model)); |
| |
1456 dialog->box = GTK_COMBO_BOX(combo); |
| |
1457 |
| |
1458 rend = GTK_CELL_RENDERER(gtk_cell_renderer_pixbuf_new()); |
| |
1459 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), rend, FALSE); |
| |
1460 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), rend, |
| |
1461 "pixbuf", SUBSTATUS_COLUMN_ICON, NULL); |
| |
1462 |
| |
1463 rend = GTK_CELL_RENDERER(gtk_cell_renderer_text_new()); |
| |
1464 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), rend, TRUE); |
| |
1465 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), rend, |
| |
1466 "text", SUBSTATUS_COLUMN_STATUS_NAME, NULL); |
| |
1467 |
| |
1468 g_signal_connect(G_OBJECT(combo), "changed", |
| |
1469 G_CALLBACK(substatus_selection_changed_cb), dialog); |
| |
1470 |
| |
1471 gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0); |
| |
1472 |
| |
1473 /* Status mesage */ |
| |
1474 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); |
| |
1475 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
| |
1476 |
| |
1477 label = gtk_label_new_with_mnemonic(_("_Message:")); |
| |
1478 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
1479 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
| |
1480 gtk_size_group_add_widget(sg, label); |
| |
1481 |
| |
1482 frame = pidgin_create_imhtml(TRUE, &text, &toolbar, NULL); |
| |
1483 dialog->message = GTK_IMHTML(text); |
| |
1484 dialog->toolbar = GTK_IMHTMLTOOLBAR(toolbar); |
| |
1485 gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); |
| |
1486 |
| |
1487 /* Button box */ |
| |
1488 bbox = gtk_hbutton_box_new(); |
| |
1489 gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); |
| |
1490 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); |
| |
1491 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); |
| |
1492 |
| |
1493 /* Cancel button */ |
| |
1494 button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); |
| |
1495 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
1496 |
| |
1497 g_signal_connect(G_OBJECT(button), "clicked", |
| |
1498 G_CALLBACK(substatus_editor_cancel_cb), dialog); |
| |
1499 |
| |
1500 /* OK button */ |
| |
1501 button = gtk_button_new_from_stock(GTK_STOCK_OK); |
| |
1502 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
1503 |
| |
1504 g_signal_connect(G_OBJECT(button), "clicked", |
| |
1505 G_CALLBACK(substatus_editor_ok_cb), dialog); |
| |
1506 |
| |
1507 /* Seed the input widgets with the current values */ |
| |
1508 /* TODO: Get the current values from our parent's list store, not the saved_status! */ |
| |
1509 if (status_editor->original_title != NULL) |
| |
1510 { |
| |
1511 PurpleSavedStatus *saved_status = NULL; |
| |
1512 PurpleSavedStatusSub *substatus = NULL; |
| |
1513 |
| |
1514 saved_status = purple_savedstatus_find(status_editor->original_title); |
| |
1515 if (saved_status != NULL) |
| |
1516 substatus = purple_savedstatus_get_substatus(saved_status, account); |
| |
1517 |
| |
1518 if (substatus != NULL) |
| |
1519 { |
| |
1520 gtk_imhtml_append_text(dialog->message, |
| |
1521 purple_savedstatus_substatus_get_message(substatus), |
| |
1522 0); |
| |
1523 status_id = purple_status_type_get_id(purple_savedstatus_substatus_get_type(substatus)); |
| |
1524 } |
| |
1525 /* TODO: Else get the generic status type from our parent */ |
| |
1526 } |
| |
1527 |
| |
1528 for (list = purple_account_get_status_types(account); list; list = list->next) |
| |
1529 { |
| |
1530 PurpleStatusType *status_type; |
| |
1531 GdkPixbuf *pixbuf; |
| |
1532 const char *id, *name; |
| |
1533 |
| |
1534 status_type = list->data; |
| |
1535 |
| |
1536 /* Only allow users to select statuses that are flagged as "user settable" */ |
| |
1537 if (!purple_status_type_is_user_settable(status_type)) |
| |
1538 continue; |
| |
1539 |
| |
1540 id = purple_status_type_get_id(status_type); |
| |
1541 pixbuf = pidgin_create_status_icon(purple_status_type_get_primitive(status_type), combo, PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); |
| |
1542 name = purple_status_type_get_name(status_type); |
| |
1543 |
| |
1544 gtk_list_store_append(dialog->model, &iter); |
| |
1545 gtk_list_store_set(dialog->model, &iter, |
| |
1546 SUBSTATUS_COLUMN_ICON, pixbuf, |
| |
1547 SUBSTATUS_COLUMN_STATUS_ID, id, |
| |
1548 SUBSTATUS_COLUMN_STATUS_NAME, name, |
| |
1549 -1); |
| |
1550 if (pixbuf != NULL) |
| |
1551 g_object_unref(pixbuf); |
| |
1552 if ((status_id != NULL) && !strcmp(status_id, id)) |
| |
1553 { |
| |
1554 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); |
| |
1555 select = TRUE; |
| |
1556 } |
| |
1557 } |
| |
1558 |
| |
1559 if (!select) |
| |
1560 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); |
| |
1561 |
| |
1562 gtk_widget_show_all(win); |
| |
1563 } |
| |
1564 |
| |
1565 |
| |
1566 /************************************************************************** |
| |
1567 * Utilities * |
| |
1568 **************************************************************************/ |
| |
1569 |
| |
1570 enum { |
| |
1571 SS_MENU_ENTRY_TYPE_PRIMITIVE, |
| |
1572 SS_MENU_ENTRY_TYPE_SAVEDSTATUS |
| |
1573 }; |
| |
1574 |
| |
1575 enum { |
| |
1576 /** _SSMenuEntryType */ |
| |
1577 SS_MENU_TYPE_COLUMN, |
| |
1578 |
| |
1579 /** |
| |
1580 * This is a GdkPixbuf (the other columns are strings). |
| |
1581 * This column is visible. |
| |
1582 */ |
| |
1583 SS_MENU_ICON_COLUMN, |
| |
1584 |
| |
1585 /** The text displayed on the status box. This column is visible. */ |
| |
1586 SS_MENU_TEXT_COLUMN, |
| |
1587 |
| |
1588 /* |
| |
1589 * This value depends on SS_MENU_TYPE_COLUMN. For _SAVEDSTATUS types, |
| |
1590 * this is the creation time. For _PRIMITIVE types, |
| |
1591 * this is the PurpleStatusPrimitive. |
| |
1592 */ |
| |
1593 SS_MENU_DATA_COLUMN, |
| |
1594 |
| |
1595 SS_MENU_NUM_COLUMNS |
| |
1596 }; |
| |
1597 |
| |
1598 static void |
| |
1599 status_menu_cb(GtkComboBox *widget, void(*callback)(PurpleSavedStatus*)) |
| |
1600 { |
| |
1601 GtkTreeIter iter; |
| |
1602 int type; |
| |
1603 gpointer data; |
| |
1604 PurpleSavedStatus *status = NULL; |
| |
1605 |
| |
1606 if (!gtk_combo_box_get_active_iter(widget, &iter)) |
| |
1607 return; |
| |
1608 |
| |
1609 gtk_tree_model_get(gtk_combo_box_get_model(widget), &iter, |
| |
1610 SS_MENU_TYPE_COLUMN, &type, |
| |
1611 SS_MENU_DATA_COLUMN, &data, |
| |
1612 -1); |
| |
1613 |
| |
1614 if (type == SS_MENU_ENTRY_TYPE_PRIMITIVE) |
| |
1615 { |
| |
1616 PurpleStatusPrimitive primitive = GPOINTER_TO_INT(data); |
| |
1617 status = purple_savedstatus_find_transient_by_type_and_message(primitive, NULL); |
| |
1618 if (status == NULL) |
| |
1619 status = purple_savedstatus_new(NULL, primitive); |
| |
1620 } |
| |
1621 else if (type == SS_MENU_ENTRY_TYPE_SAVEDSTATUS) |
| |
1622 status = purple_savedstatus_find_by_creation_time(GPOINTER_TO_INT(data)); |
| |
1623 |
| |
1624 callback(status); |
| |
1625 } |
| |
1626 |
| |
1627 static gint |
| |
1628 saved_status_sort_alphabetically_func(gconstpointer a, gconstpointer b) |
| |
1629 { |
| |
1630 const PurpleSavedStatus *saved_status_a = a; |
| |
1631 const PurpleSavedStatus *saved_status_b = b; |
| |
1632 return g_utf8_collate(purple_savedstatus_get_title(saved_status_a), |
| |
1633 purple_savedstatus_get_title(saved_status_b)); |
| |
1634 } |
| |
1635 |
| |
1636 static gboolean pidgin_status_menu_add_primitive(GtkListStore *model, GtkWidget *w, PurpleStatusPrimitive primitive, |
| |
1637 PurpleSavedStatus *current_status) |
| |
1638 { |
| |
1639 GtkTreeIter iter; |
| |
1640 gboolean currently_selected = FALSE; |
| |
1641 GdkPixbuf *pixbuf = pidgin_create_status_icon(primitive, w, PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); |
| |
1642 |
| |
1643 gtk_list_store_append(model, &iter); |
| |
1644 gtk_list_store_set(model, &iter, |
| |
1645 SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_PRIMITIVE, |
| |
1646 SS_MENU_ICON_COLUMN, pixbuf, |
| |
1647 SS_MENU_TEXT_COLUMN, purple_primitive_get_name_from_type(primitive), |
| |
1648 SS_MENU_DATA_COLUMN, GINT_TO_POINTER(primitive), |
| |
1649 -1); |
| |
1650 if (pixbuf != NULL) |
| |
1651 g_object_unref(pixbuf); |
| |
1652 |
| |
1653 if (purple_savedstatus_is_transient(current_status) |
| |
1654 && !purple_savedstatus_has_substatuses(current_status) |
| |
1655 && purple_savedstatus_get_type(current_status) == primitive) |
| |
1656 currently_selected = TRUE; |
| |
1657 |
| |
1658 return currently_selected; |
| |
1659 } |
| |
1660 |
| |
1661 GtkWidget *pidgin_status_menu(PurpleSavedStatus *current_status, GCallback callback) |
| |
1662 { |
| |
1663 GtkWidget *combobox; |
| |
1664 GtkListStore *model; |
| |
1665 GList *sorted, *cur; |
| |
1666 int i = 0; |
| |
1667 int index = -1; |
| |
1668 GdkPixbuf *pixbuf, *emblem; |
| |
1669 GtkTreeIter iter; |
| |
1670 GtkCellRenderer *text_rend; |
| |
1671 GtkCellRenderer *icon_rend; |
| |
1672 |
| |
1673 model = gtk_list_store_new(SS_MENU_NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER); |
| |
1674 |
| |
1675 combobox = gtk_combo_box_new(); |
| |
1676 |
| |
1677 if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_AVAILABLE, current_status)) |
| |
1678 index = i; |
| |
1679 i++; |
| |
1680 |
| |
1681 if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_AWAY, current_status)) |
| |
1682 index = i; |
| |
1683 i++; |
| |
1684 |
| |
1685 if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_INVISIBLE, current_status)) |
| |
1686 index = i; |
| |
1687 i++; |
| |
1688 |
| |
1689 if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_OFFLINE, current_status)) |
| |
1690 index = i; |
| |
1691 i++; |
| |
1692 |
| |
1693 sorted = g_list_copy((GList *)purple_savedstatuses_get_all()); |
| |
1694 sorted = g_list_sort(sorted, saved_status_sort_alphabetically_func); |
| |
1695 for (cur = sorted; cur; cur = cur->next) |
| |
1696 { |
| |
1697 PurpleSavedStatus *status = (PurpleSavedStatus *) cur->data; |
| |
1698 if (!purple_savedstatus_is_transient(status)) |
| |
1699 { |
| |
1700 /* Get an appropriate status icon */ |
| |
1701 pixbuf = pidgin_create_status_icon(purple_savedstatus_get_type(status), |
| |
1702 combobox, PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); |
| |
1703 |
| |
1704 /* Overlay a disk in the bottom left corner */ |
| |
1705 emblem = gtk_widget_render_icon(GTK_WIDGET(combobox), |
| |
1706 GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU, "PidginStatusMenu"); |
| |
1707 if (emblem != NULL) |
| |
1708 { |
| |
1709 /* copy the pixbuf so we're not modifying the stock image data when we overlay the disk */ |
| |
1710 GdkPixbuf *pixbuf2 = gdk_pixbuf_copy(pixbuf); |
| |
1711 int width = gdk_pixbuf_get_width(pixbuf) / 2; |
| |
1712 int height = gdk_pixbuf_get_height(pixbuf) / 2; |
| |
1713 |
| |
1714 g_object_unref(G_OBJECT(pixbuf)); |
| |
1715 pixbuf = pixbuf2; |
| |
1716 |
| |
1717 gdk_pixbuf_composite(emblem, pixbuf, 0, height, |
| |
1718 width, height, 0, height, |
| |
1719 0.5, 0.5, GDK_INTERP_BILINEAR, 255); |
| |
1720 g_object_unref(G_OBJECT(emblem)); |
| |
1721 } |
| |
1722 |
| |
1723 gtk_list_store_append(model, &iter); |
| |
1724 gtk_list_store_set(model, &iter, |
| |
1725 SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_SAVEDSTATUS, |
| |
1726 SS_MENU_ICON_COLUMN, pixbuf, |
| |
1727 SS_MENU_TEXT_COLUMN, purple_savedstatus_get_title(status), |
| |
1728 SS_MENU_DATA_COLUMN, GINT_TO_POINTER(purple_savedstatus_get_creation_time(status)), |
| |
1729 -1); |
| |
1730 g_object_unref(G_OBJECT(pixbuf)); |
| |
1731 |
| |
1732 if (status == current_status) |
| |
1733 index = i; |
| |
1734 i++; |
| |
1735 } |
| |
1736 } |
| |
1737 g_list_free(sorted); |
| |
1738 |
| |
1739 gtk_combo_box_set_model(GTK_COMBO_BOX(combobox), GTK_TREE_MODEL(model)); |
| |
1740 |
| |
1741 text_rend = gtk_cell_renderer_text_new(); |
| |
1742 icon_rend = gtk_cell_renderer_pixbuf_new(); |
| |
1743 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), icon_rend, FALSE); |
| |
1744 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), text_rend, TRUE); |
| |
1745 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), icon_rend, "pixbuf", SS_MENU_ICON_COLUMN, NULL); |
| |
1746 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), text_rend, "markup", SS_MENU_TEXT_COLUMN, NULL); |
| |
1747 |
| |
1748 |
| |
1749 gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), index); |
| |
1750 g_signal_connect(G_OBJECT(combobox), "changed", G_CALLBACK(status_menu_cb), callback); |
| |
1751 |
| |
1752 return combobox; |
| |
1753 } |
| |
1754 |
| |
1755 |
| |
1756 /************************************************************************** |
| |
1757 * GTK+ saved status glue |
| |
1758 **************************************************************************/ |
| |
1759 |
| |
1760 void * |
| |
1761 pidgin_status_get_handle(void) |
| |
1762 { |
| |
1763 static int handle; |
| |
1764 |
| |
1765 return &handle; |
| |
1766 } |
| |
1767 |
| |
1768 void |
| |
1769 pidgin_status_init(void) |
| |
1770 { |
| |
1771 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/status"); |
| |
1772 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/status/dialog"); |
| |
1773 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/status/dialog/width", 550); |
| |
1774 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/status/dialog/height", 250); |
| |
1775 } |
| |
1776 |
| |
1777 void |
| |
1778 pidgin_status_uninit(void) |
| |
1779 { |
| |
1780 pidgin_status_window_hide(); |
| |
1781 } |