Mercurial > pidgin.yaz
annotate src/gtksavedstatuses.c @ 11719:109ee3bfeac5
[gaim-migrate @ 14010]
SF Patch #1333770 from corfe83
"Many times in gaim we use the function
g_slist_remove(list,node->data) to remove an element
from a GSList. If we already have the pointer to the
node we want to delete, it is faster to send it the
pointer to the node to delete rather than the data of
the node (we can do this by calling
g_slist_delete_link(list,node)). This change was made
while looking at glib's documentation and the code in
glib's gslist.c.
This is because as the remove/delete function traverses
each node in the list, it doesn't need to spend an
extra memory access to retrieve the data for each
element in the node it is traversing and then compare,
it can simply compare the pointer. In my tests outside
of gaim, this makes a big difference if the node you
are deleting is at a high index in the list. However,
even if you're deleting the first node, it about breaks
even.
So, I've found each case in gaim where we are calling
g_slist_remove, and we already have the pointer to the
appropriate node to delete (this is often the case when
we're doing a for or while loop on a GSList). I've then
replaced it with the appropriate call to
g_slist_delete_link. I, however, didn't do this in
situations where we are explicitly removing the first
element in the list, because in those situations it is
an unnecessary change.
There should be no difference in behavior, but just in
case I've tried running it with valgrind, which reports
the same number of memory leaks after my patch as
before my patch. Of course, I can't guarantee that my
normal behavior on gaim is hitting all the functions
I've changed, but in general testing it Works For Me (tm)."
As with the last patch, this one may not have a practical performance impact (or maybe it does, I have no idea), but it's not worse for any case. Given two ways of doing things where one is always at least as fast and may be faster under some cases, I like to prefer that faster way. This doesn't make the code any uglier, so I'm applying.
committer: Tailor Script <tailor@pidgin.im>
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Sat, 22 Oct 2005 20:48:18 +0000 |
parents | 723487d07aa0 |
children | 8d7c99f20e4c |
rev | line source |
---|---|
10418 | 1 /** |
2 * @file gtksavedstatus.c GTK+ Saved Status Editor UI | |
3 * @ingroup gtkui | |
4 * | |
5 * gaim | |
6 * | |
7 * Gaim 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" | |
10420 | 28 #include "notify.h" |
10418 | 29 #include "request.h" |
30 #include "savedstatuses.h" | |
31 #include "status.h" | |
32 #include "util.h" | |
33 | |
34 #include "gtkblist.h" | |
35 #include "gtkgaim.h" | |
36 #include "gtkgaim-disclosure.h" | |
37 #include "gtkimhtml.h" | |
38 #include "gtkimhtmltoolbar.h" | |
39 #include "gtksavedstatuses.h" | |
40 #include "gtkstock.h" | |
41 #include "gtkutils.h" | |
42 | |
43 enum | |
44 { | |
10421 | 45 STATUS_WINDOW_COLUMN_TITLE, |
46 STATUS_WINDOW_COLUMN_TYPE, | |
47 STATUS_WINDOW_COLUMN_MESSAGE, | |
48 STATUS_WINDOW_NUM_COLUMNS | |
49 }; | |
50 | |
51 enum | |
52 { | |
10418 | 53 STATUS_EDITOR_COLUMN_CUSTOM_STATUS, |
54 STATUS_EDITOR_COLUMN_ICON, | |
55 STATUS_EDITOR_COLUMN_SCREENNAME, | |
56 STATUS_EDITOR_NUM_COLUMNS | |
57 }; | |
58 | |
10421 | 59 typedef struct |
10418 | 60 { |
10421 | 61 GtkWidget *window; |
62 GtkListStore *model; | |
63 GtkWidget *treeview; | |
64 GtkWidget *modify_button; | |
65 GtkWidget *delete_button; | |
66 } StatusWindow; | |
10418 | 67 |
68 typedef struct | |
69 { | |
70 GtkWidget *window; | |
71 GtkListStore *model; | |
72 GtkWidget *treeview; | |
10420 | 73 GtkWidget *save_button; |
74 | |
75 gchar *original_title; | |
76 GtkEntry *title; | |
77 GtkOptionMenu *type; | |
78 GtkIMHtml *message; | |
10418 | 79 } StatusEditor; |
80 | |
81 static StatusWindow *status_window = NULL; | |
82 | |
10421 | 83 |
84 /************************************************************************** | |
85 * Status window | |
86 **************************************************************************/ | |
87 | |
88 static gboolean | |
89 status_window_find_savedstatus(GtkTreeIter *iter, const char *title) | |
90 { | |
91 GtkTreeModel *model = GTK_TREE_MODEL(status_window->model); | |
10439 | 92 char *cur; |
10421 | 93 |
94 if (!gtk_tree_model_get_iter_first(model, iter)) | |
95 return FALSE; | |
96 | |
97 gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &cur, -1); | |
98 if (!strcmp(title, cur)) | |
10439 | 99 { |
100 g_free(cur); | |
10421 | 101 return TRUE; |
10439 | 102 } |
103 g_free(cur); | |
10421 | 104 |
105 while (gtk_tree_model_iter_next(model, iter)) | |
106 { | |
107 gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &cur, -1); | |
108 if (!strcmp(title, cur)) | |
10439 | 109 { |
110 g_free(cur); | |
10421 | 111 return TRUE; |
10439 | 112 } |
113 g_free(cur); | |
10421 | 114 } |
115 | |
116 return FALSE; | |
117 } | |
118 | |
119 static gboolean | |
120 status_window_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) | |
121 { | |
122 StatusWindow *dialog = user_data; | |
123 | |
124 dialog->window = NULL; | |
125 gaim_gtk_status_window_hide(); | |
126 | |
127 return FALSE; | |
128 } | |
129 | |
130 static void | |
131 status_window_add_cb(GtkButton *button, gpointer user_data) | |
132 { | |
133 gaim_gtk_status_editor_show(NULL); | |
134 } | |
135 | |
136 static void | |
137 status_window_modify_foreach(GtkTreeModel *model, GtkTreePath *path, | |
138 GtkTreeIter *iter, gpointer user_data) | |
139 { | |
10439 | 140 char *title; |
10421 | 141 GaimSavedStatus *status; |
142 | |
143 gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1); | |
144 | |
145 status = gaim_savedstatus_find(title); | |
10439 | 146 g_free(title); |
10421 | 147 gaim_gtk_status_editor_show(status); |
148 } | |
149 | |
150 static void | |
151 status_window_modify_cb(GtkButton *button, gpointer user_data) | |
152 { | |
153 StatusWindow *dialog = user_data; | |
154 GtkTreeSelection *selection; | |
155 | |
156 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); | |
157 | |
158 gtk_tree_selection_selected_foreach(selection, status_window_modify_foreach, user_data); | |
159 } | |
160 | |
161 static void | |
162 status_window_delete_confirm_cb(char *title) | |
163 { | |
164 GtkTreeIter iter; | |
165 | |
166 if (status_window_find_savedstatus(&iter, title)) | |
167 gtk_list_store_remove(status_window->model, &iter); | |
168 | |
169 gaim_savedstatus_delete(title); | |
170 | |
171 g_free(title); | |
172 } | |
173 | |
174 static void | |
175 status_window_delete_foreach(GtkTreeModel *model, GtkTreePath *path, | |
176 GtkTreeIter *iter, gpointer user_data) | |
177 { | |
10439 | 178 char *title; |
10435 | 179 char *title_escaped, *buf; |
10421 | 180 |
181 gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1); | |
182 | |
10732
c4cb90065e1d
[gaim-migrate @ 12334]
Luke Schierer <lschiere@pidgin.im>
parents:
10591
diff
changeset
|
183 title_escaped = g_markup_escape_text(title, -1); |
10435 | 184 buf = g_strdup_printf(_("Are you sure you want to delete %s?"), title_escaped); |
185 free(title_escaped); | |
10439 | 186 gaim_request_action(NULL, NULL, buf, NULL, 0, title, 2, |
10421 | 187 _("Delete"), status_window_delete_confirm_cb, |
188 _("Cancel"), g_free); | |
189 g_free(buf); | |
190 } | |
191 | |
192 static void | |
193 status_window_delete_cb(GtkButton *button, gpointer user_data) | |
194 { | |
195 StatusWindow *dialog = user_data; | |
196 GtkTreeSelection *selection; | |
197 | |
198 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); | |
199 | |
200 gtk_tree_selection_selected_foreach(selection, status_window_delete_foreach, user_data); | |
201 } | |
202 | |
203 static void | |
204 status_window_close_cb(GtkButton *button, gpointer user_data) | |
205 { | |
206 gaim_gtk_status_window_hide(); | |
207 } | |
208 | |
209 static void | |
210 status_selected_cb(GtkTreeSelection *sel, gpointer user_data) | |
211 { | |
212 StatusWindow *dialog = user_data; | |
213 gboolean selected = FALSE; | |
214 | |
215 #if GTK_CHECK_VERSION(2,2,0) | |
216 selected = (gtk_tree_selection_count_selected_rows(sel) > 0); | |
217 #else | |
218 gtk_tree_selection_selected_foreach(sel, get_selected_helper, &selected); | |
219 #endif | |
220 | |
221 gtk_widget_set_sensitive(dialog->modify_button, selected); | |
222 gtk_widget_set_sensitive(dialog->delete_button, selected); | |
223 } | |
224 | |
225 static void | |
226 add_status_to_saved_status_list(GtkListStore *model, GaimSavedStatus *saved_status) | |
227 { | |
228 GtkTreeIter iter; | |
229 const char *title; | |
230 const char *type; | |
231 char *message; | |
232 | |
11651 | 233 if (gaim_savedstatus_is_transient(saved_status)) |
234 return; | |
235 | |
10421 | 236 title = gaim_savedstatus_get_title(saved_status); |
237 type = gaim_primitive_get_name_from_type(gaim_savedstatus_get_type(saved_status)); | |
238 message = gaim_markup_strip_html(gaim_savedstatus_get_message(saved_status)); | |
239 | |
240 gtk_list_store_append(model, &iter); | |
241 gtk_list_store_set(model, &iter, | |
242 STATUS_WINDOW_COLUMN_TITLE, title, | |
243 STATUS_WINDOW_COLUMN_TYPE, type, | |
244 STATUS_WINDOW_COLUMN_MESSAGE, message, | |
245 -1); | |
246 free(message); | |
247 } | |
248 | |
249 static void | |
250 populate_saved_status_list(StatusWindow *dialog) | |
251 { | |
252 const GList *saved_statuses; | |
253 | |
254 gtk_list_store_clear(dialog->model); | |
255 | |
256 for (saved_statuses = gaim_savedstatuses_get_all(); saved_statuses != NULL; | |
257 saved_statuses = g_list_next(saved_statuses)) | |
258 { | |
259 add_status_to_saved_status_list(dialog->model, saved_statuses->data); | |
260 } | |
261 } | |
262 | |
10435 | 263 static gboolean |
264 search_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer search_data) | |
265 { | |
10439 | 266 gboolean result; |
267 char *haystack; | |
10435 | 268 |
269 gtk_tree_model_get(model, iter, column, &haystack, -1); | |
270 | |
10439 | 271 result = (gaim_strcasestr(haystack, key) == NULL); |
272 | |
273 g_free(haystack); | |
274 | |
275 return result; | |
10435 | 276 } |
277 | |
10421 | 278 static GtkWidget * |
279 create_saved_status_list(StatusWindow *dialog) | |
280 { | |
281 GtkWidget *sw; | |
282 GtkWidget *treeview; | |
283 GtkTreeSelection *sel; | |
284 GtkTreeViewColumn *column; | |
285 GtkCellRenderer *renderer; | |
286 | |
287 /* Create the scrolled window */ | |
288 sw = gtk_scrolled_window_new(0, 0); | |
289 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), | |
290 GTK_POLICY_AUTOMATIC, | |
291 GTK_POLICY_ALWAYS); | |
292 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), | |
293 GTK_SHADOW_IN); | |
294 gtk_widget_show(sw); | |
295 | |
296 /* Create the list model */ | |
297 dialog->model = gtk_list_store_new(STATUS_WINDOW_NUM_COLUMNS, | |
298 G_TYPE_STRING, | |
299 G_TYPE_STRING, | |
300 G_TYPE_STRING); | |
301 | |
302 /* Create the treeview */ | |
303 treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); | |
304 dialog->treeview = treeview; | |
305 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); | |
306 | |
307 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); | |
308 gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); | |
309 g_signal_connect(G_OBJECT(sel), "changed", | |
310 G_CALLBACK(status_selected_cb), dialog); | |
311 | |
312 gtk_container_add(GTK_CONTAINER(sw), treeview); | |
313 gtk_widget_show(treeview); | |
314 | |
315 /* Add columns */ | |
316 column = gtk_tree_view_column_new(); | |
317 gtk_tree_view_column_set_title(column, _("Title")); | |
318 gtk_tree_view_column_set_resizable(column, TRUE); | |
11548 | 319 gtk_tree_view_column_set_min_width(column, 100); |
10421 | 320 gtk_tree_view_column_set_sort_column_id(column, |
321 STATUS_WINDOW_COLUMN_TITLE); | |
322 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); | |
323 renderer = gtk_cell_renderer_text_new(); | |
324 gtk_tree_view_column_pack_start(column, renderer, TRUE); | |
325 gtk_tree_view_column_add_attribute(column, renderer, "text", | |
326 STATUS_WINDOW_COLUMN_TITLE); | |
11548 | 327 #if GTK_CHECK_VERSION(2,6,0) |
328 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); | |
329 #endif | |
10421 | 330 |
331 column = gtk_tree_view_column_new(); | |
332 gtk_tree_view_column_set_title(column, _("Type")); | |
333 gtk_tree_view_column_set_resizable(column, TRUE); | |
334 gtk_tree_view_column_set_sort_column_id(column, | |
335 STATUS_WINDOW_COLUMN_TYPE); | |
336 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); | |
337 renderer = gtk_cell_renderer_text_new(); | |
338 gtk_tree_view_column_pack_start(column, renderer, TRUE); | |
339 gtk_tree_view_column_add_attribute(column, renderer, "text", | |
340 STATUS_WINDOW_COLUMN_TYPE); | |
341 | |
342 column = gtk_tree_view_column_new(); | |
343 gtk_tree_view_column_set_title(column, _("Message")); | |
344 gtk_tree_view_column_set_resizable(column, TRUE); | |
345 gtk_tree_view_column_set_sort_column_id(column, | |
346 STATUS_WINDOW_COLUMN_MESSAGE); | |
347 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); | |
348 renderer = gtk_cell_renderer_text_new(); | |
349 gtk_tree_view_column_pack_start(column, renderer, TRUE); | |
350 gtk_tree_view_column_add_attribute(column, renderer, "text", | |
351 STATUS_WINDOW_COLUMN_MESSAGE); | |
10435 | 352 #if GTK_CHECK_VERSION(2,6,0) |
10502 | 353 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); |
10435 | 354 #endif |
355 | |
356 /* Enable CTRL+F searching */ | |
357 gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), STATUS_WINDOW_COLUMN_TITLE); | |
358 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), search_func, NULL, NULL); | |
10421 | 359 |
10422 | 360 /* Sort the title column by default */ |
361 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dialog->model), | |
362 STATUS_WINDOW_COLUMN_TITLE, | |
363 GTK_SORT_ASCENDING); | |
364 | |
10421 | 365 /* Populate list */ |
366 populate_saved_status_list(dialog); | |
367 | |
368 return sw; | |
369 } | |
370 | |
371 static gboolean | |
372 configure_cb(GtkWidget *widget, GdkEventConfigure *event, StatusWindow *dialog) | |
373 { | |
374 if (GTK_WIDGET_VISIBLE(widget)) { | |
375 gaim_prefs_set_int("/gaim/gtk/status/dialog/width", event->width); | |
376 gaim_prefs_set_int("/gaim/gtk/status/dialog/height", event->height); | |
377 } | |
378 | |
379 return FALSE; | |
380 } | |
381 | |
382 void | |
383 gaim_gtk_status_window_show(void) | |
384 { | |
385 StatusWindow *dialog; | |
386 GtkWidget *bbox; | |
387 GtkWidget *button; | |
388 GtkWidget *list; | |
389 GtkWidget *vbox; | |
390 GtkWidget *win; | |
391 int width, height; | |
392 | |
393 if (status_window != NULL) { | |
394 gtk_window_present(GTK_WINDOW(status_window->window)); | |
395 return; | |
396 } | |
397 | |
398 status_window = dialog = g_new0(StatusWindow, 1); | |
399 | |
400 width = gaim_prefs_get_int("/gaim/gtk/status/dialog/width"); | |
401 height = gaim_prefs_get_int("/gaim/gtk/status/dialog/height"); | |
402 | |
403 dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
404 gtk_window_set_default_size(GTK_WINDOW(win), width, height); | |
405 gtk_window_set_role(GTK_WINDOW(win), "statuses"); | |
406 gtk_window_set_title(GTK_WINDOW(win), _("Saved Statuses")); | |
11243 | 407 gtk_container_set_border_width(GTK_CONTAINER(win), GAIM_HIG_BORDER); |
10421 | 408 |
409 g_signal_connect(G_OBJECT(win), "delete_event", | |
410 G_CALLBACK(status_window_destroy_cb), dialog); | |
411 g_signal_connect(G_OBJECT(win), "configure_event", | |
412 G_CALLBACK(configure_cb), dialog); | |
413 | |
414 /* Setup the vbox */ | |
11243 | 415 vbox = gtk_vbox_new(FALSE, GAIM_HIG_BORDER); |
10421 | 416 gtk_container_add(GTK_CONTAINER(win), vbox); |
417 gtk_widget_show(vbox); | |
418 | |
419 /* List of saved status states */ | |
420 list = create_saved_status_list(dialog); | |
421 gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0); | |
422 | |
423 /* Button box. */ | |
424 bbox = gtk_hbutton_box_new(); | |
11243 | 425 gtk_box_set_spacing(GTK_BOX(bbox), GAIM_HIG_BOX_SPACE); |
10421 | 426 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); |
427 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); | |
428 gtk_widget_show(bbox); | |
429 | |
430 /* Add button */ | |
431 button = gtk_button_new_from_stock(GTK_STOCK_ADD); | |
432 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); | |
433 gtk_widget_show(button); | |
434 | |
435 g_signal_connect(G_OBJECT(button), "clicked", | |
436 G_CALLBACK(status_window_add_cb), dialog); | |
437 | |
438 /* Modify button */ | |
439 button = gtk_button_new_from_stock(GAIM_STOCK_MODIFY); | |
440 dialog->modify_button = button; | |
441 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); | |
442 gtk_widget_set_sensitive(button, FALSE); | |
443 gtk_widget_show(button); | |
444 | |
445 g_signal_connect(G_OBJECT(button), "clicked", | |
446 G_CALLBACK(status_window_modify_cb), dialog); | |
447 | |
448 /* Delete button */ | |
449 button = gtk_button_new_from_stock(GTK_STOCK_DELETE); | |
450 dialog->delete_button = button; | |
451 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); | |
452 gtk_widget_set_sensitive(button, FALSE); | |
453 gtk_widget_show(button); | |
454 | |
455 g_signal_connect(G_OBJECT(button), "clicked", | |
456 G_CALLBACK(status_window_delete_cb), dialog); | |
457 | |
458 /* Close button */ | |
459 button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); | |
460 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); | |
461 gtk_widget_show(button); | |
462 | |
463 g_signal_connect(G_OBJECT(button), "clicked", | |
464 G_CALLBACK(status_window_close_cb), dialog); | |
465 | |
466 gtk_widget_show(win); | |
467 } | |
468 | |
469 void | |
470 gaim_gtk_status_window_hide(void) | |
471 { | |
472 if (status_window == NULL) | |
473 return; | |
474 | |
475 if (status_window->window != NULL) | |
476 gtk_widget_destroy(status_window->window); | |
477 | |
478 g_free(status_window); | |
479 status_window = NULL; | |
480 } | |
481 | |
482 | |
483 /************************************************************************** | |
484 * Status editor | |
485 **************************************************************************/ | |
10420 | 486 |
10418 | 487 static gboolean |
488 status_editor_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) | |
489 { | |
10574 | 490 StatusEditor *dialog = user_data; |
10418 | 491 |
10574 | 492 g_free(dialog->original_title); |
10418 | 493 g_free(dialog); |
494 | |
495 return FALSE; | |
496 } | |
497 | |
498 static void | |
499 status_editor_cancel_cb(GtkButton *button, gpointer user_data) | |
500 { | |
10574 | 501 StatusEditor *dialog = user_data; |
10418 | 502 |
503 gtk_widget_destroy(dialog->window); | |
504 | |
10574 | 505 g_free(dialog->original_title); |
10418 | 506 g_free(dialog); |
507 } | |
508 | |
509 static void | |
510 status_editor_save_cb(GtkButton *button, gpointer user_data) | |
511 { | |
10420 | 512 StatusEditor *dialog = user_data; |
513 const char *title; | |
514 GaimStatusPrimitive type; | |
515 char *message, *unformatted; | |
516 GaimSavedStatus *status; | |
517 | |
518 title = gtk_entry_get_text(dialog->title); | |
519 | |
520 /* | |
521 * If the title is already taken then show an error dialog and | |
522 * don't do anything. | |
523 */ | |
524 if ((gaim_savedstatus_find(title) != NULL) && | |
525 ((dialog->original_title == NULL) || (strcmp(title, dialog->original_title)))) | |
526 { | |
527 gaim_notify_error(NULL, NULL, _("Title already in use. You must " | |
528 "choose a unique title."), NULL); | |
529 return; | |
530 } | |
531 | |
532 type = gtk_option_menu_get_history(dialog->type); | |
533 message = gtk_imhtml_get_markup(dialog->message); | |
534 unformatted = gaim_markup_strip_html(message); | |
535 | |
536 /* | |
537 * If we're editing an existing status, remove the old one to | |
538 * make way for the modified one. | |
539 */ | |
540 if (dialog->original_title != NULL) | |
541 { | |
542 GtkTreeIter iter; | |
543 | |
544 gaim_savedstatus_delete(dialog->original_title); | |
545 | |
546 if (status_window_find_savedstatus(&iter, dialog->original_title)) | |
547 { | |
548 gtk_list_store_remove(status_window->model, &iter); | |
549 } | |
550 } | |
551 | |
552 status = gaim_savedstatus_new(title, type); | |
553 if (*unformatted != '\0') | |
554 gaim_savedstatus_set_message(status, message); | |
555 g_free(message); | |
556 g_free(unformatted); | |
557 | |
558 gtk_widget_destroy(dialog->window); | |
10574 | 559 g_free(dialog->original_title); |
10420 | 560 g_free(dialog); |
561 | |
562 add_status_to_saved_status_list(status_window->model, status); | |
563 } | |
564 | |
565 static void | |
566 status_editor_custom_status_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpointer data) | |
567 { | |
568 /* StatusEditor *dialog = (StatusEditor *)data; */ | |
569 | |
570 /* TODO: Need to allow user to set a custom status for the highlighted account, somehow */ | |
571 } | |
572 | |
573 static void | |
574 editor_title_changed_cb(GtkWidget *widget, gpointer user_data) | |
575 { | |
576 StatusEditor *dialog = user_data; | |
577 const gchar *text; | |
578 | |
579 text = gtk_entry_get_text(dialog->title); | |
580 | |
581 gtk_widget_set_sensitive(dialog->save_button, (*text != '\0')); | |
10418 | 582 } |
583 | |
584 static GtkWidget * | |
10419 | 585 create_status_type_menu(GaimStatusPrimitive type) |
10418 | 586 { |
10419 | 587 int i; |
10418 | 588 GtkWidget *dropdown; |
589 GtkWidget *menu; | |
590 GtkWidget *item; | |
591 | |
592 dropdown = gtk_option_menu_new(); | |
593 menu = gtk_menu_new(); | |
594 | |
10419 | 595 for (i = 0; i < GAIM_STATUS_NUM_PRIMITIVES; i++) |
596 { | |
597 item = gtk_menu_item_new_with_label(gaim_primitive_get_name_from_type(i)); | |
598 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); | |
599 gtk_widget_show(item); | |
600 } | |
10418 | 601 |
10419 | 602 gtk_menu_set_active(GTK_MENU(menu), type); |
10418 | 603 gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu); |
604 gtk_widget_show(menu); | |
605 | |
606 return dropdown; | |
607 } | |
608 | |
609 static void | |
610 status_editor_add_columns(StatusEditor *dialog) | |
611 { | |
612 GtkCellRenderer *renderer; | |
613 GtkTreeViewColumn *column; | |
614 | |
615 /* Custom status column */ | |
616 renderer = gtk_cell_renderer_toggle_new(); | |
617 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(dialog->treeview), | |
618 -1, _("Custom status"), | |
619 renderer, | |
620 "active", STATUS_EDITOR_COLUMN_CUSTOM_STATUS, | |
621 NULL); | |
622 column = gtk_tree_view_get_column(GTK_TREE_VIEW(dialog->treeview), 1); | |
623 g_signal_connect(G_OBJECT(renderer), "toggled", | |
624 G_CALLBACK(status_editor_custom_status_cb), dialog); | |
625 | |
626 /* Screen Name column */ | |
627 column = gtk_tree_view_column_new(); | |
628 gtk_tree_view_column_set_resizable(column, TRUE); | |
629 gtk_tree_view_column_set_title(column, _("Screen Name")); | |
630 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1); | |
631 gtk_tree_view_column_set_resizable(column, TRUE); | |
632 | |
633 /* Icon */ | |
634 renderer = gtk_cell_renderer_pixbuf_new(); | |
635 gtk_tree_view_column_pack_start(column, renderer, FALSE); | |
636 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", | |
637 STATUS_EDITOR_COLUMN_ICON); | |
638 | |
639 /* Screen Name */ | |
640 renderer = gtk_cell_renderer_text_new(); | |
641 gtk_tree_view_column_pack_start(column, renderer, TRUE); | |
642 gtk_tree_view_column_add_attribute(column, renderer, "text", | |
643 STATUS_EDITOR_COLUMN_SCREENNAME); | |
644 } | |
645 | |
646 static void | |
647 status_editor_set_account(GtkListStore *store, GaimAccount *account, GtkTreeIter *iter) | |
648 { | |
649 GdkPixbuf *pixbuf; | |
650 GdkPixbuf *scale; | |
651 | |
652 scale = NULL; | |
653 | |
10884 | 654 pixbuf = gaim_gtk_create_prpl_icon(account); |
10418 | 655 |
656 if (pixbuf != NULL) | |
657 { | |
658 scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR); | |
659 | |
11251 | 660 if (gaim_account_is_disconnected(account)) |
10418 | 661 gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); |
662 } | |
663 | |
664 gtk_list_store_set(store, iter, | |
665 STATUS_EDITOR_COLUMN_ICON, scale, | |
666 STATUS_EDITOR_COLUMN_SCREENNAME, gaim_account_get_username(account), | |
667 -1); | |
668 | |
669 if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf)); | |
670 if (scale != NULL) g_object_unref(G_OBJECT(scale)); | |
671 } | |
672 | |
673 static void | |
674 status_editor_add_account(StatusEditor *dialog, GaimAccount *account) | |
675 { | |
676 GtkTreeIter iter; | |
677 | |
678 gtk_list_store_append(dialog->model, &iter); | |
679 | |
680 status_editor_set_account(dialog->model, account, &iter); | |
681 } | |
682 | |
683 static void | |
684 status_editor_populate_list(StatusEditor *dialog) | |
685 { | |
686 GList *l; | |
687 | |
688 gtk_list_store_clear(dialog->model); | |
689 | |
690 for (l = gaim_accounts_get_all(); l != NULL; l = l->next) | |
691 status_editor_add_account(dialog, (GaimAccount *)l->data); | |
692 } | |
693 | |
694 void | |
10419 | 695 gaim_gtk_status_editor_show(GaimSavedStatus *status) |
10418 | 696 { |
697 StatusEditor *dialog; | |
698 GtkSizeGroup *sg; | |
699 GtkWidget *bbox; | |
700 GtkWidget *button; | |
701 GtkWidget *dbox; | |
702 GtkWidget *disclosure; | |
703 GtkWidget *dropdown; | |
704 GtkWidget *entry; | |
705 GtkWidget *frame; | |
706 GtkWidget *hbox; | |
707 GtkWidget *label; | |
708 GtkWidget *sw; | |
709 GtkWidget *text; | |
710 GtkWidget *toolbar; | |
711 GtkWidget *vbox; | |
712 GtkWidget *win; | |
713 | |
714 dialog = g_new0(StatusEditor, 1); | |
715 | |
10420 | 716 if (status != NULL) |
717 dialog->original_title = g_strdup(gaim_savedstatus_get_title(status)); | |
718 | |
10418 | 719 dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
720 gtk_window_set_role(GTK_WINDOW(win), "status"); | |
721 gtk_window_set_title(GTK_WINDOW(win), _("Status")); | |
722 gtk_window_set_resizable(GTK_WINDOW(win), FALSE); | |
11243 | 723 gtk_container_set_border_width(GTK_CONTAINER(win), GAIM_HIG_BORDER); |
10418 | 724 |
725 g_signal_connect(G_OBJECT(win), "delete_event", | |
726 G_CALLBACK(status_editor_destroy_cb), dialog); | |
727 | |
728 /* Setup the vbox */ | |
11243 | 729 vbox = gtk_vbox_new(FALSE, GAIM_HIG_BORDER); |
10418 | 730 gtk_container_add(GTK_CONTAINER(win), vbox); |
731 gtk_widget_show(vbox); | |
732 | |
733 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); | |
734 | |
735 /* Title */ | |
11243 | 736 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
10418 | 737 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
738 gtk_widget_show(hbox); | |
739 | |
740 label = gtk_label_new_with_mnemonic(_("_Title:")); | |
741 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
742 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); | |
743 gtk_widget_show(label); | |
744 gtk_size_group_add_widget(sg, label); | |
745 | |
746 entry = gtk_entry_new(); | |
10420 | 747 dialog->title = GTK_ENTRY(entry); |
10419 | 748 if (status != NULL) |
10420 | 749 gtk_entry_set_text(GTK_ENTRY(entry), dialog->original_title); |
10419 | 750 else |
751 gtk_entry_set_text(GTK_ENTRY(entry), _("Out of the office")); | |
10418 | 752 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); |
753 gtk_widget_show(entry); | |
10420 | 754 g_signal_connect(G_OBJECT(entry), "changed", |
755 G_CALLBACK(editor_title_changed_cb), dialog); | |
10418 | 756 |
757 /* Status type */ | |
11243 | 758 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
10418 | 759 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
760 gtk_widget_show(hbox); | |
761 | |
762 label = gtk_label_new_with_mnemonic(_("_Status:")); | |
763 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
764 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); | |
765 gtk_widget_show(label); | |
766 gtk_size_group_add_widget(sg, label); | |
767 | |
10419 | 768 if (status != NULL) |
769 dropdown = create_status_type_menu(gaim_savedstatus_get_type(status)); | |
770 else | |
771 dropdown = create_status_type_menu(GAIM_STATUS_AWAY); | |
10420 | 772 dialog->type = GTK_OPTION_MENU(dropdown); |
10418 | 773 gtk_box_pack_start(GTK_BOX(hbox), dropdown, TRUE, TRUE, 0); |
774 gtk_widget_show(dropdown); | |
775 | |
776 /* Status message */ | |
11243 | 777 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
10418 | 778 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
779 gtk_widget_show(hbox); | |
780 | |
781 label = gtk_label_new_with_mnemonic(_("_Message:")); | |
782 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
783 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); | |
784 gtk_widget_show(label); | |
785 gtk_size_group_add_widget(sg, label); | |
786 | |
787 frame = gaim_gtk_create_imhtml(TRUE, &text, &toolbar); | |
10420 | 788 dialog->message = GTK_IMHTML(text); |
10418 | 789 gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); |
790 gtk_widget_show(frame); | |
791 | |
10574 | 792 if ((status != NULL) && (gaim_savedstatus_get_message(status) != NULL)) |
10419 | 793 gtk_imhtml_append_text(GTK_IMHTML(text), |
794 gaim_savedstatus_get_message(status), 0); | |
795 | |
10418 | 796 /* Custom status message disclosure */ |
797 disclosure = gaim_disclosure_new(_("Use a different status for some accounts"), | |
798 _("Use a different status for some accounts")); | |
799 gtk_box_pack_start(GTK_BOX(vbox), disclosure, FALSE, FALSE, 0); | |
800 gtk_widget_show(disclosure); | |
801 | |
802 /* Setup the box that the disclosure will cover */ | |
11243 | 803 dbox = gtk_vbox_new(FALSE, GAIM_HIG_CAT_SPACE); |
10418 | 804 gtk_box_pack_start(GTK_BOX(vbox), dbox, FALSE, FALSE, 0); |
805 gaim_disclosure_set_container(GAIM_DISCLOSURE(disclosure), dbox); | |
806 | |
807 /* Custom status message treeview */ | |
808 sw = gtk_scrolled_window_new(NULL, NULL); | |
809 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), | |
810 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); | |
811 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), | |
812 GTK_SHADOW_IN); | |
813 gtk_box_pack_start(GTK_BOX(dbox), sw, TRUE, TRUE, 0); | |
814 gtk_widget_show(sw); | |
815 | |
816 /* Create the list model */ | |
817 dialog->model = gtk_list_store_new(STATUS_EDITOR_NUM_COLUMNS, | |
818 G_TYPE_BOOLEAN, | |
819 GDK_TYPE_PIXBUF, G_TYPE_STRING); | |
820 | |
821 /* Create the treeview */ | |
822 dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); | |
823 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(dialog->treeview), TRUE); | |
824 gtk_widget_set_size_request(dialog->treeview, 400, 250); | |
825 gtk_container_add(GTK_CONTAINER(sw), dialog->treeview); | |
826 gtk_widget_show(dialog->treeview); | |
827 | |
828 /* Add columns */ | |
829 status_editor_add_columns(dialog); | |
830 | |
831 /* Populate list */ | |
832 status_editor_populate_list(dialog); | |
833 | |
834 /* Button box */ | |
835 bbox = gtk_hbutton_box_new(); | |
11243 | 836 gtk_box_set_spacing(GTK_BOX(bbox), GAIM_HIG_BOX_SPACE); |
10418 | 837 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); |
838 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); | |
839 gtk_widget_show(bbox); | |
840 | |
841 /* Cancel button */ | |
842 button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); | |
843 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); | |
844 gtk_widget_show(button); | |
845 | |
846 g_signal_connect(G_OBJECT(button), "clicked", | |
847 G_CALLBACK(status_editor_cancel_cb), dialog); | |
848 | |
849 /* Save button */ | |
850 button = gtk_button_new_from_stock(GTK_STOCK_SAVE); | |
10420 | 851 dialog->save_button = button; |
10418 | 852 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
853 gtk_widget_show(button); | |
854 | |
855 g_signal_connect(G_OBJECT(button), "clicked", | |
856 G_CALLBACK(status_editor_save_cb), dialog); | |
857 | |
858 gtk_widget_show(win); | |
859 } | |
860 | |
861 | |
10421 | 862 /************************************************************************** |
10814
364a2ef907ae
[gaim-migrate @ 12468]
Luke Schierer <lschiere@pidgin.im>
parents:
10732
diff
changeset
|
863 * GTK+ saved status glue |
10421 | 864 **************************************************************************/ |
10418 | 865 |
866 void * | |
867 gaim_gtk_status_get_handle() | |
868 { | |
869 static int handle; | |
870 | |
871 return &handle; | |
872 } | |
873 | |
874 void | |
875 gaim_gtk_status_init(void) | |
876 { | |
877 gaim_prefs_add_none("/gaim/gtk/status"); | |
878 gaim_prefs_add_none("/gaim/gtk/status/dialog"); | |
879 gaim_prefs_add_int("/gaim/gtk/status/dialog/width", 550); | |
880 gaim_prefs_add_int("/gaim/gtk/status/dialog/height", 250); | |
881 } | |
882 | |
883 void | |
884 gaim_gtk_status_uninit(void) | |
885 { | |
886 gaim_gtk_status_window_hide(); | |
887 } |