# HG changeset patch # User Sadrul Habib Chowdhury # Date 1154777514 0 # Node ID 27182f83b79bc8e59bb359efa46eaabd6aa4ade4 # Parent 80891029f5dd99ed60be061897bde2c06d2cf913 [gaim-migrate @ 16647] Statusbox comes in. It's now possible to change the account status. There's nothing yet for creating custom statuses. It's also possible now to delete accounts. committer: Tailor Script diff -r 80891029f5dd -r 27182f83b79b console/gntaccount.c --- a/console/gntaccount.c Sat Aug 05 09:19:14 2006 +0000 +++ b/console/gntaccount.c Sat Aug 05 11:31:54 2006 +0000 @@ -49,6 +49,9 @@ GntWidget *remember; } AccountEditDialog; +/* This is necessary to close an edit-dialog when an account is deleted */ +static GList *accountdialogs; + static void account_add(GaimAccount *account) { @@ -64,6 +67,7 @@ static void edit_dialog_destroy(AccountEditDialog *dialog) { + accountdialogs = g_list_remove(accountdialogs, dialog); g_list_free(dialog->prpl_entries); g_list_free(dialog->split_entries); g_free(dialog); @@ -143,7 +147,6 @@ gaim_account_set_password(account, NULL); /* Mail notification */ - /* XXX: Only if the protocol has anything to do with emails */ gaim_account_set_check_mail(account, gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->newmail))); @@ -426,7 +429,19 @@ GList *list, *iter; AccountEditDialog *dialog; + if (account) + { + GList *iter; + for (iter = accountdialogs; iter; iter = iter->next) + { + AccountEditDialog *dlg = iter->data; + if (dlg->account == account) + return; + } + } + dialog = g_new0(AccountEditDialog, 1); + accountdialogs = g_list_prepend(accountdialogs, dialog); dialog->window = window = gnt_box_new(FALSE, TRUE); dialog->account = account; @@ -534,10 +549,38 @@ } static void +really_delete_account(GaimAccount *account) +{ + GList *iter; + for (iter = accountdialogs; iter; iter = iter->next) + { + AccountEditDialog *dlg = iter->data; + if (dlg->account == account) + { + gnt_widget_destroy(dlg->window); + break; + } + } + gaim_accounts_delete(account); +} + +static void delete_account_cb(GntWidget *widget, GntTree *tree) { - /* XXX: After the request-api is complete */ - /* Note: remove the modify-dialog for the account */ + GaimAccount *account; + char *prompt; + + account = gnt_tree_get_selection_data(tree); + if (!account) + return; + + prompt = g_strdup_printf(_("Are you sure you want to delete %s?"), + gaim_account_get_username(account)); + + gaim_request_close_with_handle(account); /* Close any other opened delete window */ + gaim_request_action(account, _("Delete Account"), prompt, NULL, 0, account, 2, + _("Delete"), really_delete_account, _("Cancel"), NULL); + g_free(prompt); } static void diff -r 80891029f5dd -r 27182f83b79b console/gntblist.c --- a/console/gntblist.c Sat Aug 05 09:19:14 2006 +0000 +++ b/console/gntblist.c Sat Aug 05 11:31:54 2006 +0000 @@ -1,19 +1,25 @@ #include #include #include +#include #include #include +#include #include #include "gntgaim.h" #include "gntbox.h" +#include "gntcombobox.h" +#include "gntentry.h" #include "gntlabel.h" +#include "gntline.h" #include "gnttree.h" #include "gntblist.h" #include #define PREF_ROOT "/gaim/gnt/blist" +#define TYPING_TIMEOUT 4000 typedef struct { @@ -25,8 +31,29 @@ GntWidget *context; GaimBlistNode *cnode; + + /* XXX: I am KISSing */ + GntWidget *status; /* Dropdown with the statuses */ + GntWidget *statustext; /* Status message */ + int typing; } GGBlist; +typedef enum +{ + STATUS_PRIMITIVE = 0, + STATUS_SAVED +} StatusType; + +typedef struct +{ + StatusType type; + union + { + GaimStatusPrimitive prim; + GaimSavedStatus *saved; + } u; +} StatusBoxItem; + GGBlist *ggblist; static void add_buddy(GaimBuddy *buddy, GGBlist *ggblist); @@ -34,6 +61,7 @@ static void add_chat(GaimChat *chat, GGBlist *ggblist); static void add_node(GaimBlistNode *node, GGBlist *ggblist); static void draw_tooltip(GGBlist *ggblist); +static void remove_typing_cb(gpointer null); static void remove_peripherals(GGBlist *ggblist); static const char * get_display_name(GaimBlistNode *node); @@ -113,7 +141,7 @@ { } -static GaimBlistUiOps blist_ui_ops = +static GaimBlistUiOps blist_ui_ops = { new_list, new_node, @@ -202,6 +230,8 @@ GaimBlistNode *node = (GaimBlistNode *)chat; if (node->ui_data) return; + if (!gaim_account_is_connected(chat->account)) + return; group = gaim_chat_get_group(chat); add_node((GaimBlistNode*)group, ggblist); @@ -290,7 +320,7 @@ GList *list; if (action == NULL) return; - + gnt_tree_add_row_after(tree, action, gnt_tree_create_row(tree, action->label), parent, NULL); for (list = action->children; list; list = list->next) @@ -367,7 +397,7 @@ add_custom_action(tree, _("View Log"), GAIM_CALLBACK(gg_blist_view_log_cb)), buddy); #endif - + /* Protocol actions */ append_proto_menu(tree, gaim_account_get_connection(gaim_buddy_get_account(buddy)), @@ -436,9 +466,9 @@ name = ((GaimGroup*)node)->name; else g_return_if_reached(); - + prompt = g_strdup_printf(_("Please enter the new name for %s"), name); - + gaim_request_input(node, _("Rename"), prompt, _("Enter empty string to reset the name."), name, FALSE, FALSE, NULL, _("Rename"), G_CALLBACK(rename_blist_node), _("Cancel"), NULL, node); @@ -682,7 +712,7 @@ ret = gnt_widget_key_pressed(ggblist->context, text); stop = TRUE; } - + if (text[0] == 27) { if (strcmp(text + 1, GNT_KEY_POPUP) == 0) @@ -760,7 +790,8 @@ node->ui_data = NULL; node = gaim_blist_node_next(node, TRUE); } - + + remove_typing_cb(NULL); remove_peripherals(ggblist); g_free(ggblist); ggblist = NULL; @@ -777,10 +808,55 @@ while (node) { node_update(list, node); - node = gaim_blist_node_next(node, TRUE); + node = gaim_blist_node_next(node, FALSE); } } +static void +destroy_status_list(GList *list) +{ + g_list_foreach(list, (GFunc)g_free, NULL); + g_list_free(list); +} + +static void +populate_status_dropdown() +{ + int i; + GList *iter; + GList *items = NULL; + + /* First the primitives */ + GaimStatusPrimitive prims[] = {GAIM_STATUS_AVAILABLE, GAIM_STATUS_AWAY, + GAIM_STATUS_INVISIBLE, GAIM_STATUS_OFFLINE, GAIM_STATUS_UNSET}; + + for (i = 0; prims[i] != GAIM_STATUS_UNSET; i++) + { + StatusBoxItem *item = g_new0(StatusBoxItem, 1); + item->type = STATUS_PRIMITIVE; + item->u.prim = prims[i]; + items = g_list_prepend(items, item); + gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item, + gaim_primitive_get_name_from_type(prims[i])); + } + + /* Now the popular statuses */ + for (iter = gaim_savedstatuses_get_popular(6); iter; iter = iter->next) + { + StatusBoxItem *item = g_new0(StatusBoxItem, 1); + item->type = STATUS_SAVED; + item->u.saved = iter->data; + items = g_list_prepend(items, item); + gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item, + gaim_savedstatus_get_title(iter->data)); + } + + /* The keys for the combobox are created here, and never used + * anywhere else. So make sure the keys are freed when the widget + * is destroyed. */ + g_object_set_data_full(G_OBJECT(ggblist->status), "list of statuses", items, (GDestroyNotify)destroy_status_list); +} + void gg_blist_init() { gaim_prefs_add_none(PREF_ROOT); @@ -796,6 +872,120 @@ return; } +static void +remove_typing_cb(gpointer null) +{ + GaimSavedStatus *current; + const char *message, *newmessage; + GaimStatusPrimitive prim, newprim; + StatusBoxItem *item; + + current = gaim_savedstatus_get_current(); + message = gaim_savedstatus_get_message(current); + prim = gaim_savedstatus_get_type(current); + + newmessage = gnt_entry_get_text(GNT_ENTRY(ggblist->statustext)); + item = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(ggblist->status)); + g_return_if_fail(item->type == STATUS_PRIMITIVE); + newprim = item->u.prim; + + if (newprim != prim || ((message && !newmessage) || + (!message && newmessage) || + (message && newmessage && g_utf8_collate(message, newmessage) != 0))) + { + GaimSavedStatus *status = gaim_savedstatus_find_transient_by_type_and_message(newprim, newmessage); + /* Holy Crap! That's a LAWNG function name */ + if (status == NULL) + { + status = gaim_savedstatus_new(NULL, newprim); + gaim_savedstatus_set_message(status, newmessage); + } + + gaim_savedstatus_activate(status); + } + + gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree); + g_source_remove(ggblist->typing); + ggblist->typing = 0; +} + +static void +status_selection_changed(GntComboBox *box, StatusBoxItem *old, StatusBoxItem *now, gpointer null) +{ + gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), NULL); + if (now->type == STATUS_SAVED) + { + /* Set the status immediately */ + gaim_savedstatus_activate(now->u.saved); + } + else if (now->type == STATUS_PRIMITIVE) + { + /* Move the focus to the entry box */ + gnt_box_move_focus(GNT_BOX(ggblist->window), 1); + ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL); + } + else + g_return_if_reached(); +} + +static gboolean +status_text_changed(GntEntry *entry, const char *text, gpointer null) +{ + if (text[0] == 27 && ggblist->typing == 0) + return FALSE; + + g_source_remove(ggblist->typing); + ggblist->typing = 0; + + if (text[0] == '\r' && text[1] == 0) + { + /* Set the status only after you press 'Enter' */ + remove_typing_cb(NULL); + return TRUE; + } + + ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL); + return FALSE; +} + +static void +savedstatus_changed(GaimSavedStatus *now, GaimSavedStatus *old) +{ + /* Block the signals we don't want to emit */ + GList *list; + GaimStatusPrimitive prim; + const char *message; + + if (!ggblist) + return; + + g_signal_handlers_block_matched(ggblist->status, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, status_selection_changed, NULL); + g_signal_handlers_block_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, status_text_changed, NULL); + + prim = gaim_savedstatus_get_type(now); + message = gaim_savedstatus_get_message(now); + + list = g_object_get_data(G_OBJECT(ggblist->status), "list of statuses"); + for (; list; list = list->next) + { + StatusBoxItem *item = list->data; + if (item->type == STATUS_PRIMITIVE && item->u.prim == prim) + { + gnt_combo_box_set_selected(GNT_COMBO_BOX(ggblist->status), item); + gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), message); + gnt_widget_draw(ggblist->status); + break; + } + } + + g_signal_handlers_unblock_matched(ggblist->status, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, status_selection_changed, NULL); + g_signal_handlers_unblock_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, status_text_changed, NULL); +} + void gg_blist_show() { if (ggblist) @@ -805,7 +995,7 @@ gaim_get_blist()->ui_data = ggblist; - ggblist->window = gnt_box_new(FALSE, FALSE); + ggblist->window = gnt_vbox_new(FALSE); gnt_widget_set_name(ggblist->window, "buddylist"); gnt_box_set_toplevel(GNT_BOX(ggblist->window), TRUE); gnt_box_set_title(GNT_BOX(ggblist->window), _("Buddy List")); @@ -820,13 +1010,23 @@ gaim_prefs_get_int(PREF_ROOT "/position/y")); gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->tree); + + gnt_box_add_widget(GNT_BOX(ggblist->window), gnt_hline_new()); + + ggblist->status = gnt_combo_box_new(); + gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->status); + ggblist->statustext = gnt_entry_new(NULL); + gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->statustext); + + populate_status_dropdown(); + gnt_widget_show(ggblist->window); gaim_signal_connect(gaim_blist_get_handle(), "buddy-status-changed", gg_blist_get_handle(), GAIM_CALLBACK(buddy_status_changed), ggblist); gaim_signal_connect(gaim_blist_get_handle(), "buddy-idle-changed", gg_blist_get_handle(), GAIM_CALLBACK(buddy_idle_changed), ggblist); - + #if 0 gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", gg_blist_get_handle(), GAIM_CALLBACK(buddy_signed_on), ggblist); @@ -854,7 +1054,17 @@ g_signal_connect(G_OBJECT(ggblist->window), "position_set", G_CALLBACK(save_position_cb), NULL); g_signal_connect(G_OBJECT(ggblist->window), "destroy", G_CALLBACK(reset_blist_window), NULL); + /* Status signals */ + gaim_signal_connect(gaim_savedstatuses_get_handle(), "savedstatus-changed", gg_blist_get_handle(), + GAIM_CALLBACK(savedstatus_changed), NULL); + g_signal_connect(G_OBJECT(ggblist->status), "selection_changed", + G_CALLBACK(status_selection_changed), NULL); + g_signal_connect(G_OBJECT(ggblist->statustext), "key_pressed", + G_CALLBACK(status_text_changed), NULL); + populate_buddylist(); + + savedstatus_changed(gaim_savedstatus_get_current(), NULL); } void gg_blist_uninit() diff -r 80891029f5dd -r 27182f83b79b console/libgnt/gntbox.c --- a/console/libgnt/gntbox.c Sat Aug 05 09:19:14 2006 +0000 +++ b/console/libgnt/gntbox.c Sat Aug 05 11:31:54 2006 +0000 @@ -245,6 +245,28 @@ } while (box->active != last); } +static void +find_prev_focus(GntBox *box) +{ + gpointer last = box->active; + + if (!box->focus) + return; + + do + { + GList *iter = g_list_find(box->focus, box->active); + if (!iter) + box->active = box->focus->data; + else if (!iter->prev) + box->active = g_list_last(box->focus)->data; + else + box->active = iter->prev->data; + if (!GNT_WIDGET_IS_FLAG_SET(box->active, GNT_WIDGET_INVISIBLE)) + break; + } while (box->active != last); +} + static gboolean gnt_box_key_pressed(GntWidget *widget, const char *text) { @@ -263,15 +285,7 @@ { if (strcmp(text+1, GNT_KEY_LEFT) == 0) { - GList *iter = g_list_find(box->focus, box->active); - if ((!iter || !iter->prev) && box->focus) - { - box->active = box->focus->data; - } - else - { - box->active = iter->prev->data; - } + find_prev_focus(box); } else if (strcmp(text+1, GNT_KEY_RIGHT) == 0) { @@ -689,3 +703,46 @@ box->fill = fill; } +void gnt_box_move_focus(GntBox *box, int dir) +{ + GntWidget *now; + + if (box->active == NULL) + { + find_focusable_widget(box); + return; + } + + now = box->active; + + if (dir == 1) + find_next_focus(box); + else if (dir == -1) + find_prev_focus(box); + + if (now && now != box->active) + { + gnt_widget_set_focus(now, FALSE); + gnt_widget_set_focus(box->active, TRUE); + } + + if (GNT_WIDGET(box)->window) + gnt_widget_draw(GNT_WIDGET(box)); +} + +void gnt_box_give_focus_to_child(GntBox *box, GntWidget *widget) +{ + GList *find = g_list_find(box->focus, widget); + gpointer now = box->active; + if (find) + box->active = widget; + if (now && now != box->active) + { + gnt_widget_set_focus(now, FALSE); + gnt_widget_set_focus(box->active, TRUE); + } + + if (GNT_WIDGET(box)->window) + gnt_widget_draw(GNT_WIDGET(box)); +} + diff -r 80891029f5dd -r 27182f83b79b console/libgnt/gntbox.h --- a/console/libgnt/gntbox.h Sat Aug 05 09:19:14 2006 +0000 +++ b/console/libgnt/gntbox.h Sat Aug 05 11:31:54 2006 +0000 @@ -88,6 +88,10 @@ void gnt_box_set_fill(GntBox *box, gboolean fill); +void gnt_box_move_focus(GntBox *box, int dir); /* +1 to move forward, -1 for backward */ + +void gnt_box_give_focus_to_child(GntBox *box, GntWidget *widget); + G_END_DECLS #endif /* GNT_BOX_H */ diff -r 80891029f5dd -r 27182f83b79b console/libgnt/gntcombobox.c --- a/console/libgnt/gntcombobox.c Sat Aug 05 09:19:14 2006 +0000 +++ b/console/libgnt/gntcombobox.c Sat Aug 05 11:31:54 2006 +0000 @@ -23,11 +23,11 @@ /* XXX: make sure the key actually does exist */ gpointer old = box->selected; box->selected = key; - g_signal_emit(box, signals[SIG_SELECTION_CHANGED], 0, old, key); if (GNT_WIDGET(box)->window) gnt_widget_draw(GNT_WIDGET(box)); if (box->dropdown) gnt_tree_set_selected(GNT_TREE(box->dropdown), key); + g_signal_emit(box, signals[SIG_SELECTION_CHANGED], 0, old, key); } } @@ -65,7 +65,7 @@ } mvwprintw(widget->window, 1, 1, text); - whline(widget->window, '\0' | COLOR_PAIR(type), widget->priv.width - 4 - len); + whline(widget->window, ' ' | COLOR_PAIR(type), widget->priv.width - 4 - len); mvwaddch(widget->window, 1, widget->priv.width - 3, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL)); mvwaddch(widget->window, 1, widget->priv.width - 2, ACS_DARROW | COLOR_PAIR(GNT_COLOR_NORMAL)); diff -r 80891029f5dd -r 27182f83b79b console/libgnt/gntentry.c --- a/console/libgnt/gntentry.c Sat Aug 05 09:19:14 2006 +0000 +++ b/console/libgnt/gntentry.c Sat Aug 05 11:31:54 2006 +0000 @@ -16,8 +16,9 @@ { GntEntry *entry = GNT_ENTRY(widget); int stop; + gboolean focus; - if (gnt_widget_has_focus(widget)) + if ((focus = gnt_widget_has_focus(widget))) wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_TEXT_NORMAL)); else wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D)); @@ -34,8 +35,9 @@ if (stop < widget->priv.width) mvwhline(widget->window, 0, stop, ENTRY_CHAR, widget->priv.width - stop); - mvwchgat(widget->window, 0, g_utf8_pointer_to_offset(entry->scroll, entry->cursor), 1, - A_REVERSE, COLOR_PAIR(GNT_COLOR_TEXT_NORMAL), NULL); + if (focus) + mvwchgat(widget->window, 0, g_utf8_pointer_to_offset(entry->scroll, entry->cursor), + 1, A_REVERSE, COLOR_PAIR(GNT_COLOR_TEXT_NORMAL), NULL); DEBUG; } diff -r 80891029f5dd -r 27182f83b79b console/libgnt/test/tv.c --- a/console/libgnt/test/tv.c Sat Aug 05 09:19:14 2006 +0000 +++ b/console/libgnt/test/tv.c Sat Aug 05 11:31:54 2006 +0000 @@ -16,7 +16,8 @@ GNT_TEXT_FLAG_HIGHLIGHT); gnt_text_view_next_line(GNT_TEXT_VIEW(view)); gnt_entry_clear(GNT_ENTRY(w)); - gnt_text_view_scroll(GNT_TEXT_VIEW(view), 0); + if (gnt_text_view_get_lines_below(GNT_TEXT_VIEW(view)) <= 1) + gnt_text_view_scroll(GNT_TEXT_VIEW(view), 0); return TRUE; }