| 3095 { |
3085 { |
| 3096 if(purple_strequal(pref_name, PIDGIN_PREFS_ROOT "/blist/sort_type")) |
3086 if(purple_strequal(pref_name, PIDGIN_PREFS_ROOT "/blist/sort_type")) |
| 3097 pidgin_blist_sort_method_set(val); |
3087 pidgin_blist_sort_method_set(val); |
| 3098 } |
3088 } |
| 3099 |
3089 |
| 3100 /***********************************/ |
|
| 3101 /* Connection error handling stuff */ |
|
| 3102 /***********************************/ |
|
| 3103 |
|
| 3104 #define OBJECT_DATA_KEY_ACCOUNT "account" |
|
| 3105 #define DO_NOT_CLEAR_ERROR "do-not-clear-error" |
|
| 3106 |
|
| 3107 static gboolean |
|
| 3108 find_account_widget(GObject *widget, |
|
| 3109 PurpleAccount *account) |
|
| 3110 { |
|
| 3111 if (g_object_get_data(widget, OBJECT_DATA_KEY_ACCOUNT) == account) |
|
| 3112 return 0; /* found */ |
|
| 3113 else |
|
| 3114 return 1; |
|
| 3115 } |
|
| 3116 |
|
| 3117 static void |
|
| 3118 pack_protocol_icon_start(GtkWidget *box, |
|
| 3119 PurpleAccount *account) |
|
| 3120 { |
|
| 3121 GdkPixbuf *pixbuf; |
|
| 3122 GtkWidget *image; |
|
| 3123 |
|
| 3124 pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); |
|
| 3125 if (pixbuf != NULL) { |
|
| 3126 image = gtk_image_new_from_pixbuf(pixbuf); |
|
| 3127 g_object_unref(pixbuf); |
|
| 3128 |
|
| 3129 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0); |
|
| 3130 } |
|
| 3131 } |
|
| 3132 |
|
| 3133 static void |
|
| 3134 add_error_dialog(PidginBuddyList *gtkblist, |
|
| 3135 GtkWidget *dialog) |
|
| 3136 { |
|
| 3137 PidginBuddyListPrivate *priv = |
|
| 3138 pidgin_buddy_list_get_instance_private(gtkblist); |
|
| 3139 gtk_container_add(GTK_CONTAINER(priv->error_scrollbook), dialog); |
|
| 3140 } |
|
| 3141 |
|
| 3142 static GtkWidget * |
|
| 3143 find_child_widget_by_account(GtkContainer *container, |
|
| 3144 PurpleAccount *account) |
|
| 3145 { |
|
| 3146 GList *l = NULL; |
|
| 3147 GList *children = NULL; |
|
| 3148 GtkWidget *ret = NULL; |
|
| 3149 /* XXX: Workaround for the currently incomplete implementation of PidginScrollBook */ |
|
| 3150 if(PIDGIN_IS_SCROLL_BOOK(container)) { |
|
| 3151 PidginScrollBook *scroll_book = PIDGIN_SCROLL_BOOK(container); |
|
| 3152 GtkWidget *notebook = pidgin_scroll_book_get_notebook(scroll_book); |
|
| 3153 container = GTK_CONTAINER(notebook); |
|
| 3154 } |
|
| 3155 children = gtk_container_get_children(container); |
|
| 3156 l = g_list_find_custom(children, account, (GCompareFunc) find_account_widget); |
|
| 3157 if (l) |
|
| 3158 ret = GTK_WIDGET(l->data); |
|
| 3159 g_list_free(children); |
|
| 3160 return ret; |
|
| 3161 } |
|
| 3162 |
|
| 3163 static void |
|
| 3164 remove_child_widget_by_account(GtkContainer *container, |
|
| 3165 PurpleAccount *account) |
|
| 3166 { |
|
| 3167 GtkWidget *widget = find_child_widget_by_account(container, account); |
|
| 3168 if(widget) { |
|
| 3169 /* Since we are destroying the widget in response to a change in |
|
| 3170 * error, we should not clear the error. |
|
| 3171 */ |
|
| 3172 g_object_set_data(G_OBJECT(widget), DO_NOT_CLEAR_ERROR, |
|
| 3173 GINT_TO_POINTER(TRUE)); |
|
| 3174 gtk_widget_destroy(widget); |
|
| 3175 } |
|
| 3176 } |
|
| 3177 |
|
| 3178 /* Generic error buttons */ |
|
| 3179 |
|
| 3180 static void |
|
| 3181 generic_account_connect_cb(G_GNUC_UNUSED PidginMiniDialog *mini_dialog, |
|
| 3182 G_GNUC_UNUSED GtkButton *button, |
|
| 3183 gpointer user_data) |
|
| 3184 { |
|
| 3185 PurpleAccount *account = user_data; |
|
| 3186 purple_account_connect(account); |
|
| 3187 } |
|
| 3188 |
|
| 3189 static void |
|
| 3190 generic_error_modify_cb(G_GNUC_UNUSED PidginMiniDialog *mini_dialog, |
|
| 3191 G_GNUC_UNUSED GtkButton *button, |
|
| 3192 gpointer user_data) |
|
| 3193 { |
|
| 3194 PurpleAccount *account = user_data; |
|
| 3195 purple_account_clear_current_error(account); |
|
| 3196 pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account); |
|
| 3197 } |
|
| 3198 |
|
| 3199 static void |
|
| 3200 generic_error_enable_cb(G_GNUC_UNUSED PidginMiniDialog *mini_dialog, |
|
| 3201 G_GNUC_UNUSED GtkButton *button, |
|
| 3202 gpointer user_data) |
|
| 3203 { |
|
| 3204 PurpleAccount *account = user_data; |
|
| 3205 purple_account_clear_current_error(account); |
|
| 3206 purple_account_set_enabled(account, TRUE); |
|
| 3207 } |
|
| 3208 |
|
| 3209 static void |
|
| 3210 generic_error_destroy_cb(GtkWidget *dialog, |
|
| 3211 PurpleAccount *account) |
|
| 3212 { |
|
| 3213 /* If the error dialog is being destroyed in response to the |
|
| 3214 * account-error-changed signal, we don't want to clear the current |
|
| 3215 * error. |
|
| 3216 */ |
|
| 3217 if (g_object_get_data(G_OBJECT(dialog), DO_NOT_CLEAR_ERROR) == NULL) |
|
| 3218 purple_account_clear_current_error(account); |
|
| 3219 } |
|
| 3220 |
|
| 3221 #define SSL_FAQ_URI "https://developer.pidgin.im/wiki/FAQssl" |
|
| 3222 |
|
| 3223 static void |
|
| 3224 ssl_faq_clicked_cb(PidginMiniDialog *mini_dialog, |
|
| 3225 GtkButton *button, |
|
| 3226 gpointer ignored) |
|
| 3227 { |
|
| 3228 purple_notify_uri(NULL, SSL_FAQ_URI); |
|
| 3229 } |
|
| 3230 |
|
| 3231 static void |
|
| 3232 add_generic_error_dialog(PurpleAccount *account, |
|
| 3233 const PurpleConnectionErrorInfo *err) |
|
| 3234 { |
|
| 3235 GtkWidget *mini_dialog; |
|
| 3236 const char *username = purple_account_get_username(account); |
|
| 3237 gboolean enabled = purple_account_get_enabled(account); |
|
| 3238 char *primary; |
|
| 3239 |
|
| 3240 if (enabled) |
|
| 3241 primary = g_strdup_printf(_("%s disconnected"), username); |
|
| 3242 else |
|
| 3243 primary = g_strdup_printf(_("%s disabled"), username); |
|
| 3244 |
|
| 3245 mini_dialog = pidgin_mini_dialog_new_with_buttons( |
|
| 3246 primary, err->description, "dialog-error", account, |
|
| 3247 enabled ? _("Reconnect") : _("Re-enable"), |
|
| 3248 enabled ? generic_account_connect_cb : generic_error_enable_cb, |
|
| 3249 _("Modify Account"), generic_error_modify_cb, |
|
| 3250 NULL); |
|
| 3251 |
|
| 3252 g_free(primary); |
|
| 3253 |
|
| 3254 g_object_set_data(G_OBJECT(mini_dialog), OBJECT_DATA_KEY_ACCOUNT, |
|
| 3255 account); |
|
| 3256 |
|
| 3257 if(err->type == PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT) |
|
| 3258 pidgin_mini_dialog_add_non_closing_button(PIDGIN_MINI_DIALOG(mini_dialog), |
|
| 3259 _("SSL FAQs"), ssl_faq_clicked_cb, NULL); |
|
| 3260 |
|
| 3261 g_signal_connect_after(mini_dialog, "destroy", |
|
| 3262 (GCallback)generic_error_destroy_cb, |
|
| 3263 account); |
|
| 3264 |
|
| 3265 add_error_dialog(gtkblist, mini_dialog); |
|
| 3266 } |
|
| 3267 |
|
| 3268 static void |
|
| 3269 remove_generic_error_dialog(PurpleAccount *account) |
|
| 3270 { |
|
| 3271 PidginBuddyListPrivate *priv = |
|
| 3272 pidgin_buddy_list_get_instance_private(gtkblist); |
|
| 3273 remove_child_widget_by_account( |
|
| 3274 GTK_CONTAINER(priv->error_scrollbook), account); |
|
| 3275 } |
|
| 3276 |
|
| 3277 |
|
| 3278 static void |
|
| 3279 update_generic_error_message(PurpleAccount *account, |
|
| 3280 const char *description) |
|
| 3281 { |
|
| 3282 PidginBuddyListPrivate *priv = |
|
| 3283 pidgin_buddy_list_get_instance_private(gtkblist); |
|
| 3284 GtkWidget *mini_dialog = find_child_widget_by_account( |
|
| 3285 GTK_CONTAINER(priv->error_scrollbook), account); |
|
| 3286 pidgin_mini_dialog_set_description(PIDGIN_MINI_DIALOG(mini_dialog), |
|
| 3287 description); |
|
| 3288 } |
|
| 3289 |
|
| 3290 |
|
| 3291 /* Notifications about accounts which were disconnected with |
|
| 3292 * PURPLE_CONNECTION_ERROR_NAME_IN_USE |
|
| 3293 */ |
|
| 3294 |
|
| 3295 typedef void (*AccountFunction)(PurpleAccount *); |
|
| 3296 |
|
| 3297 static void |
|
| 3298 elsewhere_foreach_account(PidginMiniDialog *mini_dialog, |
|
| 3299 AccountFunction f) |
|
| 3300 { |
|
| 3301 PurpleAccount *account; |
|
| 3302 GList *labels = gtk_container_get_children( |
|
| 3303 GTK_CONTAINER(mini_dialog->contents)); |
|
| 3304 GList *l; |
|
| 3305 |
|
| 3306 for (l = labels; l; l = l->next) { |
|
| 3307 account = g_object_get_data(G_OBJECT(l->data), OBJECT_DATA_KEY_ACCOUNT); |
|
| 3308 if (account) |
|
| 3309 f(account); |
|
| 3310 else |
|
| 3311 purple_debug_warning("gtkblist", "mini_dialog's child " |
|
| 3312 "didn't have an account stored in it!"); |
|
| 3313 } |
|
| 3314 g_list_free(labels); |
|
| 3315 } |
|
| 3316 |
|
| 3317 static void |
|
| 3318 enable_account(PurpleAccount *account) |
|
| 3319 { |
|
| 3320 purple_account_set_enabled(account, TRUE); |
|
| 3321 } |
|
| 3322 |
|
| 3323 static void |
|
| 3324 reconnect_elsewhere_accounts(PidginMiniDialog *mini_dialog, |
|
| 3325 GtkButton *button, |
|
| 3326 gpointer unused) |
|
| 3327 { |
|
| 3328 elsewhere_foreach_account(mini_dialog, enable_account); |
|
| 3329 } |
|
| 3330 |
|
| 3331 static void |
|
| 3332 clear_elsewhere_errors(PidginMiniDialog *mini_dialog, |
|
| 3333 gpointer unused) |
|
| 3334 { |
|
| 3335 elsewhere_foreach_account(mini_dialog, purple_account_clear_current_error); |
|
| 3336 } |
|
| 3337 |
|
| 3338 static void |
|
| 3339 ensure_signed_on_elsewhere_minidialog(PidginBuddyList *gtkblist) |
|
| 3340 { |
|
| 3341 PidginBuddyListPrivate *priv = |
|
| 3342 pidgin_buddy_list_get_instance_private(gtkblist); |
|
| 3343 PidginMiniDialog *mini_dialog; |
|
| 3344 |
|
| 3345 if(priv->signed_on_elsewhere) |
|
| 3346 return; |
|
| 3347 |
|
| 3348 mini_dialog = priv->signed_on_elsewhere = |
|
| 3349 pidgin_mini_dialog_new(_("Welcome back!"), NULL, "pidgin-disconnect"); |
|
| 3350 |
|
| 3351 pidgin_mini_dialog_add_button(mini_dialog, _("Re-enable"), |
|
| 3352 reconnect_elsewhere_accounts, NULL); |
|
| 3353 |
|
| 3354 /* Make dismissing the dialog clear the errors. The "destroy" signal |
|
| 3355 * does not appear to fire at quit, which is fortunate! |
|
| 3356 */ |
|
| 3357 g_signal_connect(G_OBJECT(mini_dialog), "destroy", |
|
| 3358 (GCallback) clear_elsewhere_errors, NULL); |
|
| 3359 |
|
| 3360 add_error_dialog(gtkblist, GTK_WIDGET(mini_dialog)); |
|
| 3361 |
|
| 3362 /* Set priv->signed_on_elsewhere to NULL when the dialog is destroyed */ |
|
| 3363 g_signal_connect(G_OBJECT(mini_dialog), "destroy", |
|
| 3364 (GCallback) gtk_widget_destroyed, &(priv->signed_on_elsewhere)); |
|
| 3365 } |
|
| 3366 |
|
| 3367 static void |
|
| 3368 update_signed_on_elsewhere_minidialog_title(void) |
|
| 3369 { |
|
| 3370 PidginBuddyListPrivate *priv = |
|
| 3371 pidgin_buddy_list_get_instance_private(gtkblist); |
|
| 3372 PidginMiniDialog *mini_dialog = priv->signed_on_elsewhere; |
|
| 3373 guint accounts; |
|
| 3374 char *title; |
|
| 3375 |
|
| 3376 if (mini_dialog == NULL) |
|
| 3377 return; |
|
| 3378 |
|
| 3379 accounts = pidgin_mini_dialog_get_num_children(mini_dialog); |
|
| 3380 if (accounts == 0) { |
|
| 3381 gtk_widget_destroy(GTK_WIDGET(mini_dialog)); |
|
| 3382 return; |
|
| 3383 } |
|
| 3384 |
|
| 3385 title = g_strdup_printf( |
|
| 3386 ngettext("%d account was disabled because you signed on from another location:", |
|
| 3387 "%d accounts were disabled because you signed on from another location:", |
|
| 3388 accounts), |
|
| 3389 accounts); |
|
| 3390 pidgin_mini_dialog_set_description(mini_dialog, title); |
|
| 3391 g_free(title); |
|
| 3392 } |
|
| 3393 |
|
| 3394 static GtkWidget * |
|
| 3395 create_account_label(PurpleAccount *account) |
|
| 3396 { |
|
| 3397 GtkWidget *hbox, *label; |
|
| 3398 const char *username = purple_account_get_username(account); |
|
| 3399 char *markup; |
|
| 3400 char *description; |
|
| 3401 |
|
| 3402 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); |
|
| 3403 g_object_set_data(G_OBJECT(hbox), OBJECT_DATA_KEY_ACCOUNT, account); |
|
| 3404 |
|
| 3405 pack_protocol_icon_start(hbox, account); |
|
| 3406 |
|
| 3407 label = gtk_label_new(NULL); |
|
| 3408 markup = g_strdup_printf("<span size=\"smaller\">%s</span>", username); |
|
| 3409 gtk_label_set_markup(GTK_LABEL(label), markup); |
|
| 3410 g_free(markup); |
|
| 3411 gtk_label_set_xalign(GTK_LABEL(label), 0); |
|
| 3412 gtk_label_set_yalign(GTK_LABEL(label), 0); |
|
| 3413 g_object_set(G_OBJECT(label), "ellipsize", PANGO_ELLIPSIZE_END, NULL); |
|
| 3414 description = purple_account_get_current_error(account)->description; |
|
| 3415 if (description != NULL && *description != '\0') |
|
| 3416 gtk_widget_set_tooltip_text(label, description); |
|
| 3417 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); |
|
| 3418 |
|
| 3419 return hbox; |
|
| 3420 } |
|
| 3421 |
|
| 3422 static void |
|
| 3423 add_to_signed_on_elsewhere(PurpleAccount *account) |
|
| 3424 { |
|
| 3425 PidginBuddyListPrivate *priv = |
|
| 3426 pidgin_buddy_list_get_instance_private(gtkblist); |
|
| 3427 PidginMiniDialog *mini_dialog; |
|
| 3428 GtkWidget *account_label; |
|
| 3429 |
|
| 3430 ensure_signed_on_elsewhere_minidialog(gtkblist); |
|
| 3431 mini_dialog = priv->signed_on_elsewhere; |
|
| 3432 |
|
| 3433 if(find_child_widget_by_account(GTK_CONTAINER(mini_dialog->contents), account)) |
|
| 3434 return; |
|
| 3435 |
|
| 3436 account_label = create_account_label(account); |
|
| 3437 gtk_box_pack_start(mini_dialog->contents, account_label, FALSE, FALSE, 0); |
|
| 3438 gtk_widget_show_all(account_label); |
|
| 3439 |
|
| 3440 update_signed_on_elsewhere_minidialog_title(); |
|
| 3441 } |
|
| 3442 |
|
| 3443 static void |
|
| 3444 remove_from_signed_on_elsewhere(PurpleAccount *account) |
|
| 3445 { |
|
| 3446 PidginBuddyListPrivate *priv = |
|
| 3447 pidgin_buddy_list_get_instance_private(gtkblist); |
|
| 3448 PidginMiniDialog *mini_dialog = priv->signed_on_elsewhere; |
|
| 3449 if(mini_dialog == NULL) |
|
| 3450 return; |
|
| 3451 |
|
| 3452 remove_child_widget_by_account(GTK_CONTAINER(mini_dialog->contents), account); |
|
| 3453 |
|
| 3454 update_signed_on_elsewhere_minidialog_title(); |
|
| 3455 } |
|
| 3456 |
|
| 3457 |
|
| 3458 static void |
|
| 3459 update_signed_on_elsewhere_tooltip(PurpleAccount *account, |
|
| 3460 const char *description) |
|
| 3461 { |
|
| 3462 PidginBuddyListPrivate *priv = |
|
| 3463 pidgin_buddy_list_get_instance_private(gtkblist); |
|
| 3464 GtkContainer *c = GTK_CONTAINER(priv->signed_on_elsewhere->contents); |
|
| 3465 GtkWidget *label = find_child_widget_by_account(c, account); |
|
| 3466 gtk_widget_set_tooltip_text(label, description); |
|
| 3467 } |
|
| 3468 |
|
| 3469 |
|
| 3470 /* Call appropriate error notification code based on error types */ |
|
| 3471 static void |
|
| 3472 update_account_error_state(PurpleAccount *account, |
|
| 3473 const PurpleConnectionErrorInfo *old, |
|
| 3474 const PurpleConnectionErrorInfo *new, |
|
| 3475 PidginBuddyList *gtkblist) |
|
| 3476 { |
|
| 3477 gboolean descriptions_differ; |
|
| 3478 const char *desc; |
|
| 3479 |
|
| 3480 if (old == NULL && new == NULL) |
|
| 3481 return; |
|
| 3482 |
|
| 3483 if (old != NULL && new == NULL) { |
|
| 3484 if(old->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE) |
|
| 3485 remove_from_signed_on_elsewhere(account); |
|
| 3486 else |
|
| 3487 remove_generic_error_dialog(account); |
|
| 3488 return; |
|
| 3489 } |
|
| 3490 |
|
| 3491 if (old == NULL && new != NULL) { |
|
| 3492 if(new->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE) |
|
| 3493 add_to_signed_on_elsewhere(account); |
|
| 3494 else |
|
| 3495 add_generic_error_dialog(account, new); |
|
| 3496 return; |
|
| 3497 } |
|
| 3498 |
|
| 3499 /* else, new and old are both non-NULL */ |
|
| 3500 |
|
| 3501 descriptions_differ = !purple_strequal(old->description, new->description); |
|
| 3502 desc = new->description; |
|
| 3503 |
|
| 3504 switch (new->type) { |
|
| 3505 case PURPLE_CONNECTION_ERROR_NAME_IN_USE: |
|
| 3506 if (old->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE |
|
| 3507 && descriptions_differ) { |
|
| 3508 update_signed_on_elsewhere_tooltip(account, desc); |
|
| 3509 } else { |
|
| 3510 remove_generic_error_dialog(account); |
|
| 3511 add_to_signed_on_elsewhere(account); |
|
| 3512 } |
|
| 3513 break; |
|
| 3514 default: |
|
| 3515 if (old->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE) { |
|
| 3516 remove_from_signed_on_elsewhere(account); |
|
| 3517 add_generic_error_dialog(account, new); |
|
| 3518 } else if (descriptions_differ) { |
|
| 3519 update_generic_error_message(account, desc); |
|
| 3520 } |
|
| 3521 break; |
|
| 3522 } |
|
| 3523 } |
|
| 3524 |
|
| 3525 /* In case accounts are loaded before the blist (which they currently are), |
|
| 3526 * let's call update_account_error_state ourselves on every account's current |
|
| 3527 * state when the blist starts. |
|
| 3528 */ |
|
| 3529 static void |
|
| 3530 show_initial_account_errors(PidginBuddyList *gtkblist) |
|
| 3531 { |
|
| 3532 PurpleAccountManager *manager = NULL; |
|
| 3533 GList *l = NULL; |
|
| 3534 PurpleAccount *account; |
|
| 3535 const PurpleConnectionErrorInfo *err; |
|
| 3536 |
|
| 3537 manager = purple_account_manager_get_default(); |
|
| 3538 l = purple_account_manager_get_all(manager); |
|
| 3539 for(; l; l = l->next) { |
|
| 3540 account = l->data; |
|
| 3541 err = purple_account_get_current_error(account); |
|
| 3542 |
|
| 3543 update_account_error_state(account, NULL, err, gtkblist); |
|
| 3544 } |
|
| 3545 } |
|
| 3546 |
|
| 3547 /* This assumes there are not things like groupless buddies or multi-leveled groups. |
3090 /* This assumes there are not things like groupless buddies or multi-leveled groups. |
| 3548 * I'm sure other things in this code assumes that also. |
3091 * I'm sure other things in this code assumes that also. |
| 3549 */ |
3092 */ |
| 3550 static void |
3093 static void |
| 3551 treeview_style_set(GtkWidget *widget, |
3094 treeview_style_set(GtkWidget *widget, |