Mercurial > pidgin
changeset 19136:6b85cbfc80b7
merge of '6a14f373f6f5ea48a0816197f7cfbfdc7600e74e'
and 'dfaaf8344b932efe96fe2abe4b0608a02634b2cd'
author | Eric Polino <aluink@pidgin.im> |
---|---|
date | Thu, 05 Jul 2007 18:21:41 +0000 |
parents | 0e4eeb6f6284 (diff) 3ec3d55a1e37 (current diff) |
children | 51772173a916 |
files | finch/gntsound.c |
diffstat | 61 files changed, 1694 insertions(+), 510 deletions(-) [+] |
line wrap: on
line diff
--- a/COPYRIGHT Thu Jul 05 18:15:32 2007 +0000 +++ b/COPYRIGHT Thu Jul 05 18:21:41 2007 +0000 @@ -325,6 +325,7 @@ Joe Shaw Scott Shedden Dossy Shiobara +Michael Shkutkov Ettore Simone John Silvestri Craig Slusher
--- a/ChangeLog Thu Jul 05 18:15:32 2007 +0000 +++ b/ChangeLog Thu Jul 05 18:21:41 2007 +0000 @@ -11,12 +11,24 @@ notifications in chats * With the HTML logger, images in conversations are now saved. NOTE: Saved images are not yet displayed when loading logs. + * Added support for QIP logs to the Log Reader plugin (Michael Shkutkov) Pidgin: * Ensure only one copy of Pidgin is running with a given configuration directory. The net effect of this is that trying to start Pidgin a second time will raise the buddy list. (Gabriel Schulhof) * Undo capability in the conversation window + * The formatting toolbar has been reorganized to be more concise. + * A new status area has been added to the top of conversations to + provide additional detail about the buddy, including buddy icon, + protocol and status message. + + Finch: + * There's support for workspaces now (details in the manpage) + * There's a new custom window manager, Irssi + * Some improvements for tab-completion, tooltip and the password entries + * Some bugs regarding search results fixed + * A new DBus-script to create a docklet for finch version 2.0.2 (06/14/2007): Pidgin:
--- a/ChangeLog.API Thu Jul 05 18:15:32 2007 +0000 +++ b/ChangeLog.API Thu Jul 05 18:21:41 2007 +0000 @@ -73,6 +73,7 @@ * pidgin_menu_position_func_helper * pidgin_blist_get_name_markup, returns the buddy list markup text for a given buddy. + * pidgin_themes_remove_smiley_theme Changed: * pidgin_append_menu_action returns the menuitem added to the menu. @@ -88,10 +89,28 @@ Added: * finch_retrieve_user_info - Changed: - * gnt_tree_get_rows() now returns a GList* instead of a const - GList*, as const is not very useful with GLists. The - returned value still must not be modified or freed. + libgnt: + Added: + * GntWS for workspaces + * gnt_tree_set_column_title + * GntSlider widget + * "completion" signal for GntEntry + * "terminal-refresh" signal for GntWM, with a corresponding entry + in GntWMClass + * New flags for GntTextView to decide whether to word-wrap or show + scrollbars (GntTextViewFlag) which can be set by + gnt_text_view_set_flag + * gnt_style_get_from_name + * gnt_window_present + * gnt_tree_set_column_width_ratio + * gnt_tree_set_column_resizable + + Changed: + * gnt_tree_get_rows() now returns a GList* instead of a const + GList*, as const is not very useful with GLists. The + returned value still must not be modified or freed. + * Instead of keeping an 'invisible' item, the GntTreeColumns now + maintain 'flags' with the appropriate flags set version 2.0.2 (6/14/2007): Pidgin:
--- a/doc/finch.1.in Thu Jul 05 18:15:32 2007 +0000 +++ b/doc/finch.1.in Thu Jul 05 18:21:41 2007 +0000 @@ -110,14 +110,26 @@ Bring up the menu (if there is one) for a window. Note that currently only the buddylist has a menu. .TP -.B Alt \+ Shift \+ . +.B Alt \+ / +Show a list of available key-bindings for the current widget in focus. +.TP +.B Alt \+ \> Switch to the next workspace .TP -.B Alt \+ Shift \+ , +.B Alt \+ \< Switch to the previous workspace .TP +.B Alt \+ t +Tag (or untag) the current window +.TP +.B Alt \+ T +Attached all the tag windows to the current workspace +.TP .B Alt \+ s Show the workspace list +.TP +.B F9 +Create a new workspace and switch to it .SH FILES \fI~/.gntrc\fR: configuration file for gnt applications. @@ -135,7 +147,9 @@ .br # To use some custom window-manager .br -wm = /usr/local/lib/purple/s.so +wm = /usr/local/lib/gnt/s.so +.br +# There's also a custom window manager called irssi.so .br # Remember window-positions based on the titles (on by default) .br @@ -308,6 +322,8 @@ .br pagedown = page-down .br +backspace = move-parent +.br # Following is the default binding for the context-menu .br menu = context-menu @@ -377,6 +393,16 @@ .br a-l = refresh-screen .br +a-s = workspace-list +.br +a-t = window-tag +.br +a-T = place-tagged +.br +a-C = toggle-clipboard +.br +a-/ = help-for-widget +.br # The following action is still incomplete, and doesn't have a default binding .br # switch-window-n @@ -387,8 +413,18 @@ [GntS::binding] .br a-b = toggle-buddylist + +# For the irssi window manager .br -a-C = toggle-clipboard +[Irssi::binding] +.br +a-L = move-right +.br +a-H = move-left +.br +a-J = move-down +.br +a-K = move-up .SH Conversation Commands There are a few helpful commands in addition to the regular commands. You can
--- a/finch/finch.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/finch.c Thu Jul 05 18:21:41 2007 +0000 @@ -381,6 +381,7 @@ { signal(SIGPIPE, SIG_IGN); + g_set_prgname("Finch"); g_set_application_name(_("Finch")); /* Initialize the libpurple stuff */
--- a/finch/gntaccount.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/gntaccount.c Thu Jul 05 18:21:41 2007 +0000 @@ -472,6 +472,14 @@ } } + list = purple_plugins_get_protocols(); + if (list == NULL) { + purple_notify_error(NULL, _("Error"), + _("There's no protocol plugins installed."), + _("(You probably forgot to 'make install'.)")); + return; + } + dialog = g_new0(AccountEditDialog, 1); accountdialogs = g_list_prepend(accountdialogs, dialog); @@ -489,7 +497,6 @@ gnt_box_add_widget(GNT_BOX(window), hbox); dialog->protocol = combo = gnt_combo_box_new(); - list = purple_plugins_get_protocols(); for (iter = list; iter; iter = iter->next) { gnt_combo_box_add_data(GNT_COMBO_BOX(combo), iter->data,
--- a/finch/gntblist.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/gntblist.c Thu Jul 05 18:21:41 2007 +0000 @@ -590,6 +590,9 @@ if (node->ui_data) return; + if (!purple_account_is_connected(buddy->account)) + return; + contact = (PurpleContact*)node->parent; if (!contact) /* When a new buddy is added and show-offline is set */ return; @@ -2135,7 +2138,7 @@ purple_prefs_set_string(PREF_ROOT "/sort_type", n); } -/* XXX: send_im_select* -- Xerox */ +/* send_im_select* -- Xerox */ static void send_im_select_cb(gpointer data, PurpleRequestFields *fields) {
--- a/finch/gntconv.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/gntconv.c Thu Jul 05 18:21:41 2007 +0000 @@ -140,13 +140,6 @@ break; } g_free(error); -#if 0 - gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), - _("Commands are not supported yet. Message was NOT sent."), - GNT_TEXT_FLAG_DIM | GNT_TEXT_FLAG_UNDERLINE); - gnt_text_view_next_line(GNT_TEXT_VIEW(ggconv->tv)); - gnt_text_view_scroll(GNT_TEXT_VIEW(ggconv->tv), 0); -#endif } else { @@ -451,6 +444,13 @@ } static void +completion_cb(GntEntry *entry, const char *start, const char *end) +{ + if (start == entry->start) + gnt_widget_key_pressed(GNT_WIDGET(entry), ": "); +} + +static void finch_create_conversation(PurpleConversation *conv) { FinchConv *ggc = conv->ui_data; @@ -541,7 +541,10 @@ gnt_entry_set_always_suggest(GNT_ENTRY(ggc->entry), FALSE); gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(ggc->tv), ggc->entry); + gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(ggc->tv), ggc->entry); + g_signal_connect_after(G_OBJECT(ggc->entry), "key_pressed", G_CALLBACK(entry_key_pressed), ggc); + g_signal_connect(G_OBJECT(ggc->entry), "completion", G_CALLBACK(completion_cb), NULL); g_signal_connect(G_OBJECT(ggc->window), "destroy", G_CALLBACK(closing_window), ggc); gnt_widget_set_position(ggc->window, purple_prefs_get_int(PREF_ROOT "/position/x"),
--- a/finch/gntdebug.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/gntdebug.c Thu Jul 05 18:21:41 2007 +0000 @@ -274,6 +274,7 @@ g_signal_connect(G_OBJECT(debug.window), "destroy", G_CALLBACK(reset_debug_win), NULL); gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(debug.tview), debug.window); + gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(debug.tview), debug.window); gnt_widget_show(debug.window); }
--- a/finch/gntft.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/gntft.c Thu Jul 05 18:21:41 2007 +0000 @@ -178,6 +178,7 @@ GntWidget *button; GntWidget *checkbox; GntWidget *tree; + int widths[] = {8, 12, 8, 8, 8, 8, -1}; if (!xfer_dialog) xfer_dialog = g_new0(PurpleGntXferDialog, 1); @@ -195,12 +196,12 @@ xfer_dialog->tree = tree = gnt_tree_new_with_columns(NUM_COLUMNS); gnt_tree_set_column_titles(GNT_TREE(tree), _("Progress"), _("Filename"), _("Size"), _("Speed"), _("Remaining"), _("Status")); - gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_PROGRESS, 8); - gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_FILENAME, 8); - gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_SIZE, 10); - gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_SPEED, 10); - gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_REMAINING, 10); - gnt_tree_set_col_width(GNT_TREE(tree), COLUMN_STATUS, 10); + gnt_tree_set_column_width_ratio(GNT_TREE(tree), widths); + gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_PROGRESS, FALSE); + gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_SIZE, FALSE); + gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_SPEED, FALSE); + gnt_tree_set_column_resizable(GNT_TREE(tree), COLUMN_REMAINING, FALSE); + gnt_widget_set_size(tree, 70, -1); gnt_tree_set_show_title(GNT_TREE(tree), TRUE); gnt_box_add_widget(GNT_BOX(window), tree);
--- a/finch/gntprefs.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/gntprefs.c Thu Jul 05 18:21:41 2007 +0000 @@ -52,6 +52,10 @@ purple_prefs_add_none("/finch/conversations"); purple_prefs_add_bool("/finch/conversations/timestamps", TRUE); purple_prefs_add_bool("/finch/conversations/notify_typing", FALSE); + + purple_prefs_add_none("/finch/filelocations"); + purple_prefs_add_path("/finch/filelocations/last_save_folder", ""); + purple_prefs_add_path("/finch/filelocations/last_save_folder", ""); } void finch_prefs_update_old()
--- a/finch/gntrequest.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/gntrequest.c Thu Jul 05 18:21:41 2007 +0000 @@ -42,6 +42,7 @@ void *user_data; GntWidget *dialog; GCallback *cbs; + gboolean save; } PurpleGntFileRequest; static GntWidget * @@ -581,7 +582,7 @@ } static void -file_cancel_cb(GntWidget *wid, gpointer fq) +file_cancel_cb(gpointer fq, GntWidget *wid) { PurpleGntFileRequest *data = fq; if (data->cbs[1] != NULL) @@ -591,13 +592,17 @@ } static void -file_ok_cb(GntWidget *wid, gpointer fq) +file_ok_cb(gpointer fq, GntWidget *widget) { PurpleGntFileRequest *data = fq; char *file = gnt_file_sel_get_selected_file(GNT_FILE_SEL(data->dialog)); + char *dir = g_path_get_dirname(file); if (data->cbs[0] != NULL) ((PurpleRequestFileCb)data->cbs[0])(data->user_data, file); g_free(file); + purple_prefs_set_path(data->save ? "/finch/filelocations/last_save_folder" : + "/finch/filelocations/last_open_folder", dir); + g_free(dir); purple_request_close(PURPLE_REQUEST_FILE, data->dialog); } @@ -619,23 +624,34 @@ GntWidget *window = gnt_file_sel_new(); GntFileSel *sel = GNT_FILE_SEL(window); PurpleGntFileRequest *data = g_new0(PurpleGntFileRequest, 1); + const char *path; data->user_data = user_data; data->cbs = g_new0(GCallback, 2); data->cbs[0] = ok_cb; data->cbs[1] = cancel_cb; data->dialog = window; + data->save = savedialog; gnt_box_set_title(GNT_BOX(window), title ? title : (savedialog ? _("Save File...") : _("Open File..."))); - gnt_file_sel_set_current_location(sel, purple_home_dir()); /* XXX: */ + + path = purple_prefs_get_path(savedialog ? "/finch/filelocations/last_save_folder" : "/finch/filelocations/last_open_folder"); + gnt_file_sel_set_current_location(sel, (path && *path) ? path : purple_home_dir()); + if (savedialog) gnt_file_sel_set_suggested_filename(sel, filename); + g_signal_connect(G_OBJECT(sel->cancel), "activate", + G_CALLBACK(action_performed), window); + g_signal_connect(G_OBJECT(sel->select), "activate", + G_CALLBACK(action_performed), window); + g_signal_connect_swapped(G_OBJECT(sel->cancel), "activate", G_CALLBACK(file_cancel_cb), data); - g_signal_connect(G_OBJECT(sel->select), "activate", + g_signal_connect_swapped(G_OBJECT(sel->select), "activate", G_CALLBACK(file_ok_cb), data); - g_signal_connect_swapped(G_OBJECT(window), "destroy", - G_CALLBACK(file_request_destroy), data); + setup_default_callback(window, file_cancel_cb, data); + g_object_set_data_full(G_OBJECT(window), "filerequestdata", data, + (GDestroyNotify)file_request_destroy); gnt_widget_show(window); return window;
--- a/finch/gntsound.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/gntsound.c Thu Jul 05 18:21:41 2007 +0000 @@ -51,12 +51,15 @@ #include "gntcheckbox.h" #include "gntline.h" #include "gntslider.h" +#include "gnttree.h" +#include "gntfilesel.h" -struct finch_sound_event { +typedef struct { char *label; char *pref; char *def; -}; + char *file; +} FinchSoundEvent; typedef struct { GntWidget *method; @@ -64,6 +67,7 @@ GntWidget *conv_focus; GntWidget *while_status; GntWidget *volume; + GntWidget *events; GntWidget *window; } SoundPrefDialog; @@ -78,19 +82,19 @@ static gboolean gst_init_failed; #endif /* USE_GSTREAMER */ -static struct finch_sound_event sounds[PURPLE_NUM_SOUNDS] = { - {N_("Buddy logs in"), "login", "login.wav"}, - {N_("Buddy logs out"), "logout", "logout.wav"}, - {N_("Message received"), "im_recv", "receive.wav"}, - {N_("Message received begins conversation"), "first_im_recv", "receive.wav"}, - {N_("Message sent"), "send_im", "send.wav"}, - {N_("Person enters chat"), "join_chat", "login.wav"}, - {N_("Person leaves chat"), "left_chat", "logout.wav"}, - {N_("You talk in chat"), "send_chat_msg", "send.wav"}, - {N_("Others talk in chat"), "chat_msg_recv", "receive.wav"}, +static FinchSoundEvent sounds[PURPLE_NUM_SOUNDS] = { + {N_("Buddy logs in"), "login", "login.wav",NULL}, + {N_("Buddy logs out"), "logout", "logout.wav",NULL}, + {N_("Message received"), "im_recv", "receive.wav",NULL}, + {N_("Message received begins conversation"), "first_im_recv", "receive.wav",NULL}, + {N_("Message sent"), "send_im", "send.wav",NULL}, + {N_("Person enters chat"), "join_chat", "login.wav",NULL}, + {N_("Person leaves chat"), "left_chat", "logout.wav",NULL}, + {N_("You talk in chat"), "send_chat_msg", "send.wav",NULL}, + {N_("Others talk in chat"), "chat_msg_recv", "receive.wav",NULL}, /* this isn't a terminator, it's the buddy pounce default sound event ;-) */ - {NULL, "pounce_default", "alert.wav"}, - {N_("Someone says your screen name in chat"), "nick_said", "alert.wav"} + {NULL, "pounce_default", "alert.wav",NULL}, + {N_("Someone says your screen name in chat"), "nick_said", "alert.wav",NULL} }; static gboolean @@ -569,24 +573,97 @@ g_free(file_pref); } -static gboolean -save_cb(GntBindable *data, gpointer *win) +static void +save_cb(GntWidget *button, gpointer win) { - + GList * itr; + + purple_prefs_set_string(FINCH_PREFS_ROOT "/sound/method", gnt_combo_box_get_selected_data(GNT_COMBO_BOX(pref_dialog->method))); + purple_prefs_set_path(FINCH_PREFS_ROOT "/sound/command", gnt_entry_get_text(GNT_ENTRY(pref_dialog->command))); + purple_prefs_set_bool(FINCH_PREFS_ROOT "/sound/conv_focus",gnt_check_box_get_checked(GNT_CHECK_BOX(pref_dialog->conv_focus))); + purple_prefs_set_int("/purple/sound/while_status",GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(pref_dialog->while_status)))); + purple_prefs_set_int(FINCH_PREFS_ROOT "/sound/volume",gnt_slider_get_value(GNT_SLIDER(pref_dialog->volume))); + + for(itr = gnt_tree_get_rows(GNT_TREE(pref_dialog->events));itr;itr = itr->next){ + FinchSoundEvent * event = &sounds[GPOINTER_TO_INT(itr->data)]; + char * filepref = g_strdup_printf("%s/sound/file/%s",FINCH_PREFS_ROOT,event->pref); + char * boolpref = g_strdup_printf(FINCH_PREFS_ROOT "/sound/enabled/%s",event->pref); + purple_prefs_set_bool(boolpref,gnt_tree_get_choice(GNT_TREE(pref_dialog->events),itr->data)); + purple_prefs_set_path(filepref,event->file ? event->file : ""); + g_free(filepref); + } gnt_widget_destroy(GNT_WIDGET(win)); - return TRUE; +} +static void +file_cb(GntWidget *w, gpointer data) +{ + GntFileSel *sel = GNT_FILE_SEL(data); + const char * file = gnt_file_sel_get_selected_file(sel); + FinchSoundEvent * event = &sounds[GPOINTER_TO_INT(gnt_tree_get_selection_data(GNT_TREE(pref_dialog->events)))]; + + g_free(event->file); + event->file = g_strdup(file); + + gnt_widget_destroy(GNT_WIDGET(data)); } -static gboolean -cancel_cb(GntBindable *data, gpointer win) +static void +test_cb(GntWidget *button, gpointer null) { - gnt_widget_destroy(GNT_WIDGET(win)); - return TRUE; + PurpleSoundEventID id = GPOINTER_TO_INT(gnt_tree_get_selection_data(GNT_TREE(pref_dialog->events))); + FinchSoundEvent * event = &sounds[id]; + char *pref; + gboolean temp_value; + + pref = g_strdup_printf(FINCH_PREFS_ROOT "/sound/enabled/%s", + event->pref); + + temp_value = purple_prefs_get_bool(pref); + + if (!temp_value) purple_prefs_set_bool(pref, TRUE); + + purple_sound_play_event(id, NULL); + + if (!temp_value) purple_prefs_set_bool(pref, FALSE); + + g_free(pref); +} + +static void +reset_cb(GntWidget *button,gpointer null) +{ + /* Don't dereference this pointer ! */ + gpointer key = gnt_tree_get_selection_data(GNT_TREE(pref_dialog->events)); + + FinchSoundEvent * event = &sounds[GPOINTER_TO_INT(key)]; + g_free(event->file); + event->file = NULL; + gnt_tree_change_text(GNT_TREE(pref_dialog->events),key,1,""); +} + +static void +choose_cb(GntWidget *button, gpointer null) +{ + GntWidget *w = gnt_file_sel_new(); + + GntFileSel *sel = GNT_FILE_SEL(w); + gnt_file_sel_set_current_location(sel,purple_home_dir()); + + g_signal_connect_swapped(G_OBJECT(sel->cancel),"activate",G_CALLBACK(gnt_widget_destroy),sel); + g_signal_connect(G_OBJECT(sel->select),"activate",G_CALLBACK(file_cb),sel); + + gnt_widget_show(w); } static void release_pref_dialog(GntBindable *data, gpointer null) { + GList * itr; + for(itr = gnt_tree_get_rows(GNT_TREE(pref_dialog->events));itr;itr = itr->next){ + PurpleSoundEventID id = GPOINTER_TO_INT(itr->data); + FinchSoundEvent * e = &sounds[id]; + g_free(e->file); + } g_free(pref_dialog); pref_dialog = NULL; } @@ -601,8 +678,10 @@ GntWidget *chkbox; GntWidget *button; GntWidget *label; + GntWidget *tree; GntWidget *win; + gint i; if(pref_dialog){ gnt_window_present(pref_dialog->window); @@ -618,16 +697,19 @@ gnt_box_set_toplevel(GNT_BOX(win), TRUE); gnt_box_set_title(GNT_BOX(win),_("Sound Preferences")); gnt_box_set_fill(GNT_BOX(win),TRUE); - gnt_box_set_alignment(GNT_BOX(win),GNT_ALIGN_MID); + gnt_box_set_alignment(GNT_BOX(win),GNT_ALIGN_LEFT); pref_dialog->method = cmbox = gnt_combo_box_new(); + gnt_tree_set_hash_fns(GNT_TREE(GNT_COMBO_BOX(cmbox)->dropdown), g_str_hash, g_str_equal, NULL); gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),"automatic",_("Automatic")); gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),"alsa","ALSA"); gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),"esd","ESD"); gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),"beep",_("Console Beep")); gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),"custom",_("Command")); gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),"nosound",_("No Sound")); - gnt_combo_box_set_selected(GNT_COMBO_BOX(cmbox),(gchar *)purple_prefs_get_string(FINCH_PREFS_ROOT "/sound/method")); + buf = g_strdup(purple_prefs_get_string(FINCH_PREFS_ROOT "/sound/method")); + gnt_combo_box_set_selected(GNT_COMBO_BOX(cmbox),buf); + g_free(buf); label = gnt_label_new_with_format(_("Sound Method"),GNT_TEXT_FLAG_BOLD); gnt_box_add_widget(GNT_BOX(win),label); @@ -642,14 +724,14 @@ gnt_box_set_pad(GNT_BOX(box),0); gnt_box_set_fill(GNT_BOX(box),FALSE); gnt_box_add_widget(GNT_BOX(box),gnt_label_new(_("Sound Command\n(%s for filename)"))); - pref_dialog->command = entry = gnt_entry_new(purple_prefs_get_string(FINCH_PREFS_ROOT "/sound/method")); + pref_dialog->command = entry = gnt_entry_new(purple_prefs_get_path(FINCH_PREFS_ROOT "/sound/command")); gnt_box_add_widget(GNT_BOX(box),entry); gnt_box_add_widget(GNT_BOX(win),box); gnt_box_add_widget(GNT_BOX(win), gnt_line_new(FALSE)); gnt_box_add_widget(GNT_BOX(win),gnt_label_new_with_format(_("Sound Options"),GNT_TEXT_FLAG_BOLD)); - pref_dialog->conv_focus = chkbox = gnt_check_box_new("Sounds when conversation has focus"); + pref_dialog->conv_focus = chkbox = gnt_check_box_new(_("Sounds when conversation has focus")); gnt_check_box_set_checked(GNT_CHECK_BOX(chkbox),purple_prefs_get_bool(FINCH_PREFS_ROOT "/sound/conv_focus")); gnt_box_add_widget(GNT_BOX(win),chkbox); @@ -658,21 +740,17 @@ gnt_box_set_fill(GNT_BOX(box),FALSE); gnt_box_add_widget(GNT_BOX(box),gnt_label_new("Enable Sounds:")); pref_dialog->while_status = cmbox = gnt_combo_box_new(); - gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),"always","Always"); - gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),"available","Only when available"); - gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),"away","Only when not available"); - switch(purple_prefs_get_int(FINCH_PREFS_ROOT "/sound/while_status")){ - case 1:gnt_combo_box_set_selected(GNT_COMBO_BOX(cmbox),"available");break; - case 2:gnt_combo_box_set_selected(GNT_COMBO_BOX(cmbox),"away");break; - default:gnt_combo_box_set_selected(GNT_COMBO_BOX(cmbox),"always");break; - } + gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),GINT_TO_POINTER(3),_("Always")); + gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),GINT_TO_POINTER(1),_("Only when available")); + gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox),GINT_TO_POINTER(2),_("Only when not available")); + gnt_combo_box_set_selected(GNT_COMBO_BOX(cmbox),GINT_TO_POINTER(purple_prefs_get_int("/purple" "/sound/while_status"))); gnt_box_add_widget(GNT_BOX(box),cmbox); gnt_box_add_widget(GNT_BOX(win),box); box = gnt_hbox_new(TRUE); gnt_box_set_pad(GNT_BOX(box),0); gnt_box_set_fill(GNT_BOX(box),FALSE); - gnt_box_add_widget(GNT_BOX(box),gnt_label_new("Volume(0-100):")); + gnt_box_add_widget(GNT_BOX(box),gnt_label_new(_("Volume(0-100):"))); pref_dialog->volume = slider = gnt_slider_new(FALSE,100,0); gnt_slider_set_step(GNT_SLIDER(slider),5); @@ -687,16 +765,54 @@ gnt_box_add_widget(GNT_BOX(win), gnt_line_new(FALSE)); gnt_box_add_widget(GNT_BOX(win),gnt_label_new_with_format(_("Sound Events"),GNT_TEXT_FLAG_BOLD)); - /* Put events tree here */ + pref_dialog->events = tree = gnt_tree_new_with_columns(2); + gnt_tree_set_column_titles(GNT_TREE(tree),_("Event"),_("File")); + gnt_tree_set_show_title(GNT_TREE(tree),TRUE); + + for(i = 0;i < PURPLE_NUM_SOUNDS;i++){ + FinchSoundEvent * event = &sounds[i]; + gchar *boolpref; + gchar *filepref; + + boolpref = g_strdup_printf(FINCH_PREFS_ROOT "/sound/enabled/%s", event->pref); + + filepref = g_strdup_printf(FINCH_PREFS_ROOT "/sound/file/%s", event->pref); + event->file = g_strdup(purple_prefs_get_path(filepref)); + if(event->label == NULL){ + continue; + } + + gnt_tree_add_choice(GNT_TREE(tree), GINT_TO_POINTER(i), + gnt_tree_create_row(GNT_TREE(tree),event->label,event->file[0] ? g_path_get_basename(event->file) : ""), + NULL, NULL); + gnt_tree_set_choice(GNT_TREE(tree),GINT_TO_POINTER(i),purple_prefs_get_bool(boolpref)); + g_free(boolpref); + g_free(filepref); + } + + gnt_tree_adjust_columns(GNT_TREE(tree)); + gnt_box_add_widget(GNT_BOX(win),tree); + + box = gnt_hbox_new(TRUE); + button = gnt_button_new("Test"); + g_signal_connect(G_OBJECT(button),"activate",G_CALLBACK(test_cb),NULL); + gnt_box_add_widget(GNT_BOX(box),button); + button = gnt_button_new("Reset"); + g_signal_connect(G_OBJECT(button),"activate",G_CALLBACK(reset_cb),NULL); + gnt_box_add_widget(GNT_BOX(box),button); + button = gnt_button_new("Choose..."); + g_signal_connect(G_OBJECT(button),"activate",G_CALLBACK(choose_cb),NULL); + gnt_box_add_widget(GNT_BOX(box),button); + gnt_box_add_widget(GNT_BOX(win),box); box = gnt_hbox_new(TRUE); gnt_box_set_pad(GNT_BOX(box),0); gnt_box_set_fill(GNT_BOX(box),TRUE); button = gnt_button_new("Save"); - g_signal_connect(G_OBJECT(button),"activate",G_CALLBACK(save_cb),NULL); + g_signal_connect(G_OBJECT(button),"activate",G_CALLBACK(save_cb),win); gnt_box_add_widget(GNT_BOX(box),button); button = gnt_button_new("Cancel"); - g_signal_connect(G_OBJECT(button),"activate",G_CALLBACK(cancel_cb),NULL); + g_signal_connect_swapped(G_OBJECT(button),"activate",G_CALLBACK(gnt_widget_destroy),win); gnt_box_add_widget(GNT_BOX(box),button); gnt_box_add_widget(GNT_BOX(win),box);
--- a/finch/gntstatus.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/gntstatus.c Thu Jul 05 18:21:41 2007 +0000 @@ -163,6 +163,7 @@ void finch_savedstatus_show_all() { GntWidget *window, *tree, *box, *button; + int widths[] = {25, 12, 35}; if (statuses.window) { gnt_window_present(statuses.window); return; @@ -179,9 +180,8 @@ statuses.tree = tree = gnt_tree_new_with_columns(3); gnt_tree_set_column_titles(GNT_TREE(tree), _("Title"), _("Type"), _("Message")); gnt_tree_set_show_title(GNT_TREE(tree), TRUE); - gnt_tree_set_col_width(GNT_TREE(tree), 0, 25); - gnt_tree_set_col_width(GNT_TREE(tree), 1, 12); - gnt_tree_set_col_width(GNT_TREE(tree), 2, 35); + gnt_tree_set_column_width_ratio(GNT_TREE(tree), widths); + gnt_widget_set_size(tree, 72, 0); gnt_box_add_widget(GNT_BOX(window), tree); populate_statuses(GNT_TREE(tree));
--- a/finch/libgnt/gnt.h Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gnt.h Thu Jul 05 18:21:41 2007 +0000 @@ -112,7 +112,7 @@ /** * * @param label - * @param callback)() + * @param callback */ void gnt_register_action(const char *label, void (*callback)()); @@ -148,3 +148,11 @@ * @param string */ void gnt_set_clipboard_string(gchar *string); + +/** + * Spawn a different application that will consume the console. + */ +gboolean gnt_giveup_console(const char *wd, char **argv, char **envp, + gint *stin, gint *stout, gint *sterr); + +gboolean gnt_is_refugee(void);
--- a/finch/libgnt/gntbindable.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gntbindable.c Thu Jul 05 18:21:41 2007 +0000 @@ -53,16 +53,15 @@ g_free(rebind_info.okeys); } -static gboolean -gnt_bindable_rebinding_cancel(GntBindable *bindable, gpointer data) +static void +gnt_bindable_rebinding_cancel(GntWidget *button, gpointer data) { gnt_bindable_free_rebind_info(); gnt_widget_destroy(GNT_WIDGET(data)); - return TRUE; } -static gboolean -gnt_bindable_rebinding_rebind(GntBindable *bindable, gpointer data) +static void +gnt_bindable_rebinding_rebind(GntWidget *button, gpointer data) { if(rebind_info.keys) { @@ -78,8 +77,6 @@ gnt_bindable_free_rebind_info(); gnt_widget_destroy(GNT_WIDGET(data)); - - return TRUE; } static gboolean
--- a/finch/libgnt/gntentry.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gntentry.c Thu Jul 05 18:21:41 2007 +0000 @@ -25,6 +25,7 @@ #include "gntbox.h" #include "gntentry.h" +#include "gntmarshal.h" #include "gntstyle.h" #include "gnttree.h" #include "gntutils.h" @@ -32,6 +33,7 @@ enum { SIG_TEXT_CHANGED, + SIG_COMPLETION, SIGS, }; static guint signals[SIGS] = { 0 }; @@ -69,9 +71,12 @@ complete_suggest(GntEntry *entry, const char *text) { gboolean changed = FALSE; + int offstart = 0, offend = 0; + if (entry->word) { char *s = get_beginning_of_word(entry); const char *iter = text; + offstart = g_utf8_pointer_to_offset(entry->start, s); while (*iter && toupper(*s) == toupper(*iter)) { if (*s != *iter) changed = TRUE; @@ -81,10 +86,17 @@ gnt_entry_key_pressed(GNT_WIDGET(entry), iter); changed = TRUE; } + offend = g_utf8_pointer_to_offset(entry->start, entry->cursor); } else { + offstart = 0; gnt_entry_set_text_internal(entry, text); changed = TRUE; + offend = g_utf8_strlen(text, -1); } + + if (changed) + g_signal_emit(G_OBJECT(entry), signals[SIG_COMPLETION], 0, + entry->start + offstart, entry->start + offend); return changed; } @@ -687,6 +699,14 @@ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + signals[SIG_COMPLETION] = + g_signal_new("completion", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + gnt_closure_marshal_VOID__POINTER_POINTER, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER); + gnt_bindable_class_register_action(bindable, "cursor-home", move_start, GNT_KEY_CTRL_A, NULL); gnt_bindable_register_binding(bindable, "cursor-home", GNT_KEY_HOME, NULL);
--- a/finch/libgnt/gntfilesel.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gntfilesel.c Thu Jul 05 18:21:41 2007 +0000 @@ -448,6 +448,9 @@ GntFileSel *sel = GNT_FILE_SEL(widget); GntWidget *hbox, *vbox; + if (sel->current == NULL) + gnt_file_sel_set_current_location(sel, g_get_home_dir()); + vbox = gnt_vbox_new(FALSE); gnt_box_set_pad(GNT_BOX(vbox), 0); gnt_box_set_alignment(GNT_BOX(vbox), GNT_ALIGN_MID);
--- a/finch/libgnt/gntkeys.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gntkeys.c Thu Jul 05 18:21:41 2007 +0000 @@ -153,7 +153,7 @@ const char *gnt_key_translate(const char *name) { - return g_hash_table_lookup(specials, name); + return name ? g_hash_table_lookup(specials, name) : NULL; } typedef struct {
--- a/finch/libgnt/gntmain.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gntmain.c Thu Jul 05 18:21:41 2007 +0000 @@ -65,6 +65,7 @@ */ static GIOChannel *channel = NULL; +static int channel_read_callback; static gboolean ascii_only; static gboolean mouse_enabled; @@ -220,8 +221,13 @@ io_invoke(GIOChannel *source, GIOCondition cond, gpointer null) { char keys[256]; - int rd = read(STDIN_FILENO, keys + HOLDING_ESCAPE, sizeof(keys) - 1 - HOLDING_ESCAPE); + int rd; char *k; + + if (wm->mode == GNT_KP_MODE_WAIT_ON_CHILD) + return FALSE; + + rd = read(STDIN_FILENO, keys + HOLDING_ESCAPE, sizeof(keys) - 1 - HOLDING_ESCAPE); if (rd < 0) { int ch = getch(); /* This should return ERR, but let's see what it really returns */ @@ -294,7 +300,7 @@ g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL ); #endif - result = g_io_add_watch_full(channel, G_PRIORITY_HIGH, + channel_read_callback = result = g_io_add_watch_full(channel, G_PRIORITY_HIGH, (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI), io_invoke, NULL, NULL); @@ -628,7 +634,51 @@ { return clipboard; } + gchar *gnt_get_clipboard_string() { return gnt_clipboard_get_string(clipboard); } + +#if GLIB_CHECK_VERSION(2,4,0) +static void +reap_child(GPid pid, gint status, gpointer data) +{ + wm->mode = GNT_KP_MODE_NORMAL; + clear(); + setup_io(); + refresh_screen(); +} +#endif + +gboolean gnt_giveup_console(const char *wd, char **argv, char **envp, + gint *stin, gint *stout, gint *sterr) +{ +#if GLIB_CHECK_VERSION(2,4,0) + GPid pid = 0; + + if (!g_spawn_async_with_pipes(wd, argv, envp, + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, + (GSpawnChildSetupFunc)endwin, NULL, + &pid, stin, stout, sterr, NULL)) + return FALSE; + + g_source_remove(channel_read_callback); + wm->mode = GNT_KP_MODE_WAIT_ON_CHILD; + g_child_watch_add(pid, reap_child, NULL); + + return TRUE; +#else + return FALSE; +#endif +} + +gboolean gnt_is_refugee() +{ +#if GLIB_CHECK_VERSION(2,4,0) + return (wm && wm->mode == GNT_KP_MODE_WAIT_ON_CHILD); +#else + return FALSE; +#endif +} +
--- a/finch/libgnt/gntmenu.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gntmenu.c Thu Jul 05 18:21:41 2007 +0000 @@ -326,6 +326,7 @@ GNT_TREE(widget)->show_separator = FALSE; _gnt_tree_init_internals(GNT_TREE(widget), 2); gnt_tree_set_col_width(GNT_TREE(widget), 1, 1); /* The second column is to indicate that it has a submenu */ + gnt_tree_set_column_resizable(GNT_TREE(widget), 1, FALSE); GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_NO_BORDER); }
--- a/finch/libgnt/gntstyle.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gntstyle.c Thu Jul 05 18:21:41 2007 +0000 @@ -270,7 +270,8 @@ { GError *error = NULL; gsize nkeys; - char **keys = g_key_file_get_keys(kfile, "general", &nkeys, &error); + const char *prgname = g_get_prgname(); + char **keys = NULL; int i; struct { @@ -283,6 +284,14 @@ {"remember_position", GNT_STYLE_REMPOS}, {NULL, 0}}; + if (prgname && *prgname) + keys = g_key_file_get_keys(kfile, prgname, &nkeys, NULL); + + if (keys == NULL) { + prgname = "general"; + keys = g_key_file_get_keys(kfile, prgname, &nkeys, &error); + } + if (error) { g_printerr("GntStyle: %s\n", error->message); @@ -293,7 +302,7 @@ for (i = 0; styles[i].style; i++) { str_styles[styles[i].en] = - g_key_file_get_string(kfile, "general", styles[i].style, NULL); + g_key_file_get_string(kfile, prgname, styles[i].style, NULL); } } g_strfreev(keys);
--- a/finch/libgnt/gnttextview.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gnttextview.c Thu Jul 05 18:21:41 2007 +0000 @@ -20,9 +20,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "gntstyle.h" #include "gnttextview.h" #include "gntutils.h" +#include <stdlib.h> #include <string.h> enum @@ -793,3 +795,43 @@ view->flags |= flag; } +static gboolean +check_for_pager_cb(GntWidget *widget, const char *key, GntTextView *view) +{ + static const char *combin = NULL; + char *argv[] = {NULL, NULL, NULL}; + static char path[1024]; + static int len = -1; + FILE *file; + + if (combin == NULL) { + combin = gnt_key_translate(gnt_style_get_from_name("pager", "key")); + if (combin == NULL) + combin = "\033" "v"; + len = g_snprintf(path, sizeof(path), "%s" G_DIR_SEPARATOR_S "gnt", g_get_tmp_dir()); + } else { + g_snprintf(path + len, sizeof(path) - len, "XXXXXX"); + } + + if (strcmp(key, combin)) { + return FALSE; + } + + file = fdopen(g_mkstemp(path), "wb"); + if (!file) + return FALSE; + + fprintf(file, "%s", view->string->str); + fclose(file); + argv[0] = gnt_style_get_from_name("pager", "path"); + argv[0] = argv[0] ? argv[0] : getenv("PAGER"); + argv[0] = argv[0] ? argv[0] : "less"; + argv[1] = path; + return gnt_giveup_console(NULL, argv, NULL, NULL, NULL, NULL); +} + +void gnt_text_view_attach_pager_widget(GntTextView *view, GntWidget *pager) +{ + g_signal_connect(pager, "key_pressed", G_CALLBACK(check_for_pager_cb), view); +} +
--- a/finch/libgnt/gnttextview.h Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gnttextview.h Thu Jul 05 18:21:41 2007 +0000 @@ -184,6 +184,13 @@ void gnt_text_view_attach_scroll_widget(GntTextView *view, GntWidget *widget); /** + * + * @param view + * @param widget + */ +void gnt_text_view_attach_pager_widget(GntTextView *view, GntWidget *pager); + +/** * Set a GntTextViewFlag for the textview widget. * * @param view The textview widget
--- a/finch/libgnt/gnttree.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gnttree.c Thu Jul 05 18:21:41 2007 +0000 @@ -31,6 +31,9 @@ #define SEARCH_TIMEOUT 4000 /* 4 secs */ #define SEARCHING(tree) (tree->search && tree->search->len > 0) +#define COLUMN_INVISIBLE(tree, index) (tree->columns[index].flags & GNT_TREE_COLUMN_INVISIBLE) +#define BINARY_DATA(tree, index) (tree->columns[index].flags & GNT_TREE_COLUMN_BINARY_DATA) + enum { SIG_SELECTION_CHANGED, @@ -67,6 +70,7 @@ struct _GntTreeCol { char *text; + gboolean isbinary; int span; /* How many columns does it span? */ }; @@ -75,6 +79,36 @@ static GntWidgetClass *parent_class = NULL; static guint signals[SIGS] = { 0 }; +static void +readjust_columns(GntTree *tree) +{ + int i, col, total; + int width; +#define WIDTH(i) (tree->columns[i].width_ratio ? tree->columns[i].width_ratio : tree->columns[i].width) + gnt_widget_get_size(GNT_WIDGET(tree), &width, NULL); + for (i = 0, total = 0; i < tree->ncol ; i++) { + if (tree->columns[i].flags & GNT_TREE_COLUMN_INVISIBLE) + continue; + if (tree->columns[i].flags & GNT_TREE_COLUMN_FIXED_SIZE) + width -= WIDTH(i); + else + total += WIDTH(i); + } + + if (total == 0) + return; + + for (i = 0; i < tree->ncol; i++) { + if (tree->columns[i].flags & GNT_TREE_COLUMN_INVISIBLE) + continue; + if (tree->columns[i].flags & GNT_TREE_COLUMN_FIXED_SIZE) + col = WIDTH(i); + else + col = (WIDTH(i) * width) / total; + gnt_tree_set_col_width(GNT_TREE(tree), i, col); + } +} + /* Move the item at position old to position new */ static GList * g_list_reposition_child(GList *list, int old, int new) @@ -104,6 +138,8 @@ { GntTree *t = row->tree; if (t->search && t->search->len > 0) { + /* XXX: Allow setting the search column. And make sure the search column + * doesn't contain binary data. */ char *one = g_utf8_casefold(((GntTreeCol*)row->columns->data)->text, -1); char *two = g_utf8_casefold(t->search->str, -1); char *z = strstr(one, two); @@ -249,21 +285,29 @@ gboolean notfirst = FALSE; int lastvisible = tree->ncol; - while (lastvisible && tree->columns[lastvisible].invisible) + while (lastvisible && COLUMN_INVISIBLE(tree, lastvisible)) lastvisible--; for (i = 0, iter = row->columns; i < tree->ncol && iter; i++, iter = iter->next) { GntTreeCol *col = iter->data; const char *text; - int len = gnt_util_onscreen_width(col->text, NULL); + int len; int fl = 0; gboolean cut = FALSE; int width; + const char *display; - if (tree->columns[i].invisible) + if (COLUMN_INVISIBLE(tree, i)) continue; + if (BINARY_DATA(tree, i)) + display = ""; + else + display = col->text; + + len = gnt_util_onscreen_width(display, NULL); + if (i == lastvisible) width = GNT_WIDGET(tree)->priv.width - gnt_util_onscreen_width(string->str, NULL); else @@ -307,8 +351,8 @@ len = width - 1; cut = TRUE; } - text = gnt_util_onscreen_width_to_pointer(col->text, len - fl, NULL); - string = g_string_append_len(string, col->text, text - col->text); + text = gnt_util_onscreen_width_to_pointer(display, len - fl, NULL); + string = g_string_append_len(string, display, text - display); if (cut) { /* ellipsis */ if (gnt_ascii_only()) g_string_append_c(string, '~'); @@ -335,11 +379,11 @@ for (i = 0; i < tree->ncol - 1; i++) { - if (!tree->columns[i].invisible) { + if (!COLUMN_INVISIBLE(tree, i)) { notfirst = TRUE; NEXT_X; } - if (!tree->columns[i+1].invisible && notfirst) + if (!COLUMN_INVISIBLE(tree, i+1) && notfirst) mvwaddch(widget->window, y, x, type); } } @@ -383,7 +427,7 @@ for (i = 0; i < tree->ncol; i++) { - if (tree->columns[i].invisible) { + if (COLUMN_INVISIBLE(tree, i)) { continue; } mvwaddstr(widget->window, pos, x + 1, tree->columns[i].title); @@ -555,7 +599,7 @@ GntTree *tree = GNT_TREE(widget); int i, width = 0; for (i = 0; i < tree->ncol; i++) - if (!tree->columns[i].invisible) + if (!COLUMN_INVISIBLE(tree, i)) width += tree->columns[i].width + 1; widget->priv.width = width; } @@ -829,16 +873,10 @@ gnt_tree_size_changed(GntWidget *widget, int w, int h) { GntTree *tree = GNT_TREE(widget); - int i; - int n = 0; if (widget->priv.width <= 0) return; - for (i = 0; i < tree->ncol; ++i) - n += tree->columns[i].width; - if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) - tree->columns[tree->ncol - 1].width += widget->priv.width - n - 1 * tree->ncol; - else - tree->columns[tree->ncol - 1].width += widget->priv.width - n - 2 - 1 * tree->ncol; + + readjust_columns(tree); } static gboolean @@ -977,8 +1015,8 @@ free_tree_col(gpointer data) { GntTreeCol *col = data; - - g_free(col->text); + if (col->isbinary) + g_free(col->text); g_free(col); } @@ -1364,8 +1402,12 @@ if (row) { col = g_list_nth_data(row->columns, colno); - g_free(col->text); - col->text = g_strdup(text ? text : ""); + if (BINARY_DATA(tree, colno)) { + col->text = (gpointer)text; + } else { + g_free(col->text); + col->text = g_strdup(text ? text : ""); + } if (get_distance(tree->top, row) >= 0 && get_distance(row, tree->bottom) >= 0) redraw_tree(tree); @@ -1491,7 +1533,13 @@ { GntTreeCol *col = g_new0(GntTreeCol, 1); col->span = 1; - col->text = g_strdup(iter->data ? iter->data : ""); + if (BINARY_DATA(tree, i)) { + col->text = iter->data; + col->isbinary = TRUE; + } else { + col->text = g_strdup(iter->data ? iter->data : ""); + col->isbinary = FALSE; + } row->columns = g_list_append(row->columns, col); } @@ -1524,6 +1572,8 @@ g_return_if_fail(col < tree->ncol); tree->columns[col].width = width; + if (tree->columns[col].width_ratio == 0) + tree->columns[col].width_ratio = width; } void gnt_tree_set_column_title(GntTree *tree, int index, const char *title) @@ -1598,7 +1648,7 @@ twidth = 1 + 2 * (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER)); for (i = 0; i < tree->ncol; i++) { gnt_tree_set_col_width(tree, i, widths[i]); - if (!tree->columns[i].invisible) + if (!COLUMN_INVISIBLE(tree, i)) twidth += widths[i] + (tree->show_separator ? 1 : 0) + 1; } g_free(widths); @@ -1613,9 +1663,38 @@ tree->hash = g_hash_table_new_full(hash, eq, kd, free_tree_row); } +static void +set_column_flag(GntTree *tree, int col, GntTreeColumnFlag flag, gboolean set) +{ + if (set) + tree->columns[col].flags |= flag; + else + tree->columns[col].flags &= ~flag; +} + void gnt_tree_set_column_visible(GntTree *tree, int col, gboolean vis) { g_return_if_fail(col < tree->ncol); - tree->columns[col].invisible = !vis; + set_column_flag(tree, col, GNT_TREE_COLUMN_INVISIBLE, !vis); +} + +void gnt_tree_set_column_resizable(GntTree *tree, int col, gboolean res) +{ + g_return_if_fail(col < tree->ncol); + set_column_flag(tree, col, GNT_TREE_COLUMN_FIXED_SIZE, !res); } +void gnt_tree_set_column_is_binary(GntTree *tree, int col, gboolean bin) +{ + g_return_if_fail(col < tree->ncol); + set_column_flag(tree, col, GNT_TREE_COLUMN_FIXED_SIZE, bin); +} + +void gnt_tree_set_column_width_ratio(GntTree *tree, int cols[]) +{ + int i; + for (i = 0; i < tree->ncol && cols[i]; i++) { + tree->columns[i].width_ratio = cols[i]; + } +} +
--- a/finch/libgnt/gnttree.h Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gnttree.h Thu Jul 05 18:21:41 2007 +0000 @@ -40,10 +40,6 @@ #define GNT_IS_TREE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_TREE)) #define GNT_TREE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_TREE, GntTreeClass)) -#define GNT_TREE_FLAGS(obj) (GNT_TREE(obj)->priv.flags) -#define GNT_TREE_SET_FLAGS(obj, flags) (GNT_TREE_FLAGS(obj) |= flags) -#define GNT_TREE_UNSET_FLAGS(obj, flags) (GNT_TREE_FLAGS(obj) &= ~(flags)) - typedef struct _GntTree GntTree; typedef struct _GntTreePriv GntTreePriv; typedef struct _GntTreeClass GntTreeClass; @@ -51,6 +47,12 @@ typedef struct _GntTreeRow GntTreeRow; typedef struct _GntTreeCol GntTreeCol; +typedef enum { + GNT_TREE_COLUMN_INVISIBLE = 1 << 0, + GNT_TREE_COLUMN_FIXED_SIZE = 1 << 1, + GNT_TREE_COLUMN_BINARY_DATA = 1 << 2, +} GntTreeColumnFlag; + struct _GntTree { GntWidget parent; @@ -74,7 +76,8 @@ { int width; char *title; - gboolean invisible; + int width_ratio; + GntTreeColumnFlag flags; } *columns; /* Would a GList be better? */ gboolean show_title; gboolean show_separator; /* Whether to show column separators */ @@ -101,203 +104,253 @@ G_BEGIN_DECLS /** - * - * - * @return + * @return The GType for GntTree */ GType gnt_tree_get_gtype(void); /** - * + * Create a tree with one column. * - * @return + * @return The newly created tree + * + * @see gnt_tree_new_with_columns */ GntWidget * gnt_tree_new(void); - /* A tree with just one column */ - /** - * - * @param columns + * Create a tree with a specified number of columns. + * + * @param columns Number of columns * - * @return + * @return The newly created tree + * + * @see gnt_tree_new */ GntWidget * gnt_tree_new_with_columns(int columns); /** - * - * @param tree - * @param rows + * The number of rows the tree should display at a time. + * + * @param tree The tree + * @param rows The number of rows */ void gnt_tree_set_visible_rows(GntTree *tree, int rows); /** - * - * @param tree + * Get the number visible rows. * - * @return + * @param tree The tree + * + * @return The number of visible rows */ int gnt_tree_get_visible_rows(GntTree *tree); /** - * - * @param tree - * @param count + * Scroll the contents of the tree. + * + * @param tree The tree + * @param count If positive, the tree will be scrolled down by count rows, + * otherwise, it will be scrolled up by count rows. */ void gnt_tree_scroll(GntTree *tree, int count); /** - * - * @param tree - * @param key - * @param row - * @param parent - * @param bigbro + * Insert a row in the tree. * - * @return + * @param tree The tree + * @param key The key for the row + * @param row The row to insert + * @param parent The key for the parent row + * @param bigbro The key for the row to insert the new row after. + * + * @return The inserted row + * + * @see gnt_tree_create_row + * @see gnt_tree_add_row_last + * @see gnt_tree_add_choice */ GntTreeRow * gnt_tree_add_row_after(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro); /** - * - * @param tree - * @param key - * @param row - * @param parent + * Insert a row at the end of the tree. * - * @return + * @param tree The tree + * @param key The key for the row + * @param row The row to insert + * @param parent The key for the parent row + * + * @return The inserted row + * + * @see gnt_tree_create_row + * @see gnt_tree_add_row_after + * @see gnt_tree_add_choice */ GntTreeRow * gnt_tree_add_row_last(GntTree *tree, void *key, GntTreeRow *row, void *parent); /** - * - * @param tree + * Get the key for the selected row. * - * @return + * @param tree The tree + * + * @return The key for the selected row */ gpointer gnt_tree_get_selection_data(GntTree *tree); -/* Returned string needs to be freed */ /** - * - * @param tree + * Get the text displayed for the selected row. * - * @return + * @param tree The tree + * + * @return The text, which needs to be freed by the caller */ char * gnt_tree_get_selection_text(GntTree *tree); /** - * - * @param tree + * Get a list of text of the current row. * - * @return + * @param tree The tree + * + * @return A list of texts of the currently selected row. The list + * and its data should be freed by the caller. */ GList * gnt_tree_get_selection_text_list(GntTree *tree); /** + * Returns the list of rows in the tree. * - * @param tree + * @param tree The tree * - * @constreturn + * @return The list of the rows. The list should not be modified by the caller. */ GList *gnt_tree_get_rows(GntTree *tree); /** - * - * @param tree - * @param key + * Remove a row from the tree. + * + * @param tree The tree + * @param key The key for the row to remove */ void gnt_tree_remove(GntTree *tree, gpointer key); /** - * - * @param tree + * Remove all the item from the tree. + * + * @param tree The tree */ void gnt_tree_remove_all(GntTree *tree); -/* Returns the visible line number of the selected row */ /** - * - * @param tree + * Get the visible line number of the selected row. * - * @return + * @param tree The tree + * + * @return The line number of the currently selected row */ int gnt_tree_get_selection_visible_line(GntTree *tree); /** - * - * @param tree - * @param key - * @param colno - * @param text + * Change the text of a column in a row. + * + * @param tree The tree + * @param key The key for the row + * @param colno The index of the column + * @param text The new text */ void gnt_tree_change_text(GntTree *tree, gpointer key, int colno, const char *text); /** - * - * @param tree - * @param key - * @param row - * @param parent - * @param bigbro + * Add a checkable item in the tree. * - * @return + * @param tree The tree + * @param key The key for the row + * @param row The row to add + * @param parent The parent of the row, or @c NULL + * @param bigbro The row to insert after, or @c NULL + * + * @return The row inserted. + * + * @see gnt_tree_create_row + * @see gnt_tree_create_row_from_list + * @see gnt_tree_add_row_last + * @see gnt_tree_add_row_after */ GntTreeRow * gnt_tree_add_choice(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro); /** - * - * @param tree - * @param key - * @param set + * Set whether a checkable item is checked or not. + * + * @param tree The tree + * @param key The key for the row + * @param set @c TRUE if the item should be checked, @c FALSE if not */ void gnt_tree_set_choice(GntTree *tree, void *key, gboolean set); /** - * - * @param tree - * @param key + * Return whether a row is selected or not, where the row is a checkable item. * - * @return + * @param tree The tree + * @param key The key for the row + * + * @return @c TRUE if the row is checked, @c FALSE otherwise. */ gboolean gnt_tree_get_choice(GntTree *tree, void *key); /** - * - * @param tree - * @param key - * @param flags + * Set flags for the text in a row in the tree. + * + * @param tree The tree + * @param key The key for the row + * @param flags The flags to set */ void gnt_tree_set_row_flags(GntTree *tree, void *key, GntTextFormatFlags flags); /** - * - * @param key + * Select a row. + * + * @param tree The tree + * @param key The key of the row to select */ void gnt_tree_set_selected(GntTree *tree , void *key); /** - * - * @param tree + * Create a row to insert in the tree. + * + * @param tree The tree + * @param ... A string for each column in the tree * - * @return + * @return The row + * + * @see gnt_tree_create_row_from_list + * @see gnt_tree_add_row_after + * @see gnt_tree_add_row_last + * @see gnt_tree_add_choice */ GntTreeRow * gnt_tree_create_row(GntTree *tree, ...); /** - * - * @param tree - * @param list + * Create a row from a list of text. + * + * @param tree The tree + * @param list The list containing the text for each column * - * @return + * @return The row + * + * @see gnt_tree_create_row + * @see gnt_tree_add_row_after + * @see gnt_tree_add_row_last + * @see gnt_tree_add_choice */ GntTreeRow * gnt_tree_create_row_from_list(GntTree *tree, GList *list); /** - * - * @param tree - * @param col - * @param width + * Set the width of a column in the tree. + * + * @param tree The tree + * @param col The index of the column + * @param width The width for the column + * + * @see gnt_tree_set_column_width_ratio + * @see gnt_tree_set_column_resizable */ void gnt_tree_set_col_width(GntTree *tree, int col, int width); @@ -307,87 +360,144 @@ * @param tree The tree * @param index The index of the column * @param title The title for the column + * + * @see gnt_tree_set_column_titles + * @see gnt_tree_set_show_title */ void gnt_tree_set_column_title(GntTree *tree, int index, const char *title); /** - * - * @param tree + * Set the titles of the columns + * + * @param tree The tree + * @param ... One title for each column in the tree + * + * @see gnt_tree_set_column_title + * @see gnt_tree_set_show_title */ void gnt_tree_set_column_titles(GntTree *tree, ...); /** - * - * @param tree - * @param set + * Set whether to display the title of the columns. + * + * @param tree The tree + * @param set If @c TRUE, the column titles are displayed + * + * @see gnt_tree_set_column_title + * @see gnt_tree_set_column_titles */ void gnt_tree_set_show_title(GntTree *tree, gboolean set); /** - * - * @param tree - * @param func + * Set the compare function for sorting the data. + * + * @param tree The tree + * @param func The comparison function, which is used to compare + * the keys + * + * @see gnt_tree_sort_row */ void gnt_tree_set_compare_func(GntTree *tree, GCompareFunc func); /** - * - * @param tree - * @param key - * @param expanded + * Set whether a row, which has child rows, should be expanded. + * + * @param tree The tree + * @param key The key of the row + * @param expanded Whether to expand the child rows */ void gnt_tree_set_expanded(GntTree *tree, void *key, gboolean expanded); /** - * - * @param tree - * @param set + * Set whether to show column separators. + * + * @param tree The tree + * @param set If @c TRUE, the column separators are displayed */ void gnt_tree_set_show_separator(GntTree *tree, gboolean set); /** - * - * @param tree - * @param row + * Sort a row in the tree. + * + * @param tree The tree + * @param row The row to sort + * + * @see gnt_tree_set_compare_func */ void gnt_tree_sort_row(GntTree *tree, void *row); -/* This will try to automatically adjust the width of the columns in the tree */ /** - * - * @param tree + * Automatically adjust the width of the columns in the tree. + * + * @param tree The tree */ void gnt_tree_adjust_columns(GntTree *tree); /** - * - * @param tree - * @param hash - * @param eq - * @param kd + * Set the hash functions to use to hash, compare and free the keys. + * + * @param tree The tree + * @param hash The hashing function + * @param eq The function to compare keys + * @param kd The function to use to free the keys when a row is removed + * from the tree */ void gnt_tree_set_hash_fns(GntTree *tree, gpointer hash, gpointer eq, gpointer kd); -/* This can be useful when, for example, we want to store some data - * which we don't want/need to display. */ /** + * Set whether a column is visible or not. + * This can be useful when, for example, we want to store some data + * which we don't want/need to display. * - * @param tree - * @param col - * @param vis + * @param tree The tree + * @param col The index of the column + * @param vis If @c FALSE, the column will not be displayed */ void gnt_tree_set_column_visible(GntTree *tree, int col, gboolean vis); +/** + * Set whether a column can be resized to keep the same ratio when the + * tree is resized. + * + * @param tree The tree + * @param col The index of the column + * @param res If @c FALSE, the column will not be resized when the + * tree is resized + * + * @see gnt_tree_set_col_width + * @see gnt_tree_set_column_width_ratio + */ +void gnt_tree_set_column_resizable(GntTree *tree, int col, gboolean res); + +/** + * Set whether data in a column should be considered as binary data, and + * not as strings. A column containing binary data will be display empty text. + * + * @param tree The tree + * @param col The index of the column + * @param bin @c TRUE if the data for the column is binary + */ +void gnt_tree_set_column_is_binary(GntTree *tree, int col, gboolean bin); + +/** + * Set column widths to use when calculating column widths after a tree + * is resized. + * + * @param tree The tree + * @param cols Array of widths. The width must have the same number + * of entries as the number of columns in the tree, or + * end with a negative value for a column-width. + * + * @see gnt_tree_set_col_width + * @see gnt_tree_set_column_resizable + */ +void gnt_tree_set_column_width_ratio(GntTree *tree, int cols[]); + G_END_DECLS /* The following functions should NOT be used by applications. */ /* This should be called by the subclasses of GntTree's in their _new function */ -/** - * - * @param tree - * @param col - */ void _gnt_tree_init_internals(GntTree *tree, int col); #endif /* GNT_TREE_H */
--- a/finch/libgnt/gntwm.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gntwm.c Thu Jul 05 18:21:41 2007 +0000 @@ -63,6 +63,7 @@ SIG_GIVE_FOCUS, SIG_KEY_PRESS, SIG_MOUSE_CLICK, + SIG_TERMINAL_REFRESH, SIGS }; @@ -206,6 +207,9 @@ static gboolean update_screen(GntWM *wm) { + if (wm->mode == GNT_KP_MODE_WAIT_ON_CHILD) + return TRUE; + if (wm->menu) { GntMenu *top = wm->menu; while (top) { @@ -379,13 +383,7 @@ else if (pos >= 0) wid = g_list_nth_data(wm->cws->list, pos); - wm->cws->ordered = g_list_bring_to_front(wm->cws->ordered, wid); - - gnt_wm_raise_window(wm, wm->cws->ordered->data); - - if (w != wid) { - gnt_widget_set_focus(w, FALSE); - } + gnt_wm_raise_window(wm, wid); } static gboolean @@ -408,7 +406,6 @@ switch_window_n(GntBindable *bind, GList *list) { GntWM *wm = GNT_WM(bind); - GntWidget *w = NULL; GList *l; int n; @@ -420,17 +417,11 @@ else n = 0; - w = wm->cws->ordered->data; - if ((l = g_list_nth(wm->cws->list, n)) != NULL) { gnt_wm_raise_window(wm, l->data); } - if (l && w != l->data) - { - gnt_widget_set_focus(w, FALSE); - } return TRUE; } @@ -1005,11 +996,13 @@ endwin(); g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, NULL); + refresh(); + g_signal_emit(wm, signals[SIG_TERMINAL_REFRESH], 0); update_screen(wm); gnt_ws_draw_taskbar(wm->cws, TRUE); curs_set(0); /* endwin resets the cursor to normal */ - return FALSE; + return TRUE; } static gboolean @@ -1277,6 +1270,15 @@ gnt_closure_marshal_BOOLEAN__INT_INT_INT_POINTER, G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_POINTER); + signals[SIG_TERMINAL_REFRESH] = + g_signal_new("terminal-refresh", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, terminal_refresh), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-next", window_next, "\033" "n", NULL); gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-prev", window_prev, @@ -1399,7 +1401,6 @@ gnt_ws_draw_taskbar(wm->cws, TRUE); update_screen(wm); if (wm->cws->ordered) { - gnt_widget_set_focus(wm->cws->ordered->data, TRUE); gnt_wm_raise_window(wm, wm->cws->ordered->data); } @@ -1610,29 +1611,16 @@ if (!transient) { GntWS *ws = wm->cws; if (node->me != wm->_list.window) { - GntWidget *w = NULL; - if (GNT_IS_BOX(widget)) { ws = new_widget_find_workspace(wm, widget); } - - if (ws->ordered) - w = ws->ordered->data; - node->ws = ws; ws->list = g_list_append(ws->list, widget); - - if (wm->event_stack) - ws->ordered = g_list_prepend(ws->ordered, widget); - else - ws->ordered = g_list_append(ws->ordered, widget); - - gnt_widget_set_focus(widget, TRUE); - if (w) - gnt_widget_set_focus(w, FALSE); + ws->ordered = g_list_append(ws->ordered, widget); } - if (wm->event_stack || node->me == wm->_list.window) { + if (wm->event_stack || node->me == wm->_list.window || + node->me == ws->ordered->data) { gnt_wm_raise_window(wm, node->me); } else { bottom_panel(node->panel); /* New windows should not grab focus */ @@ -2021,6 +2009,14 @@ GntWS *ws = gnt_wm_widget_find_workspace(wm, widget); if (wm->cws != ws) gnt_wm_switch_workspace(wm, g_list_index(wm->workspaces, ws)); + if (widget != wm->cws->ordered->data) { + GntWidget *wid = wm->cws->ordered->data; + wm->cws->ordered = g_list_bring_to_front(wm->cws->ordered, widget); + gnt_widget_set_focus(wid, FALSE); + gnt_widget_draw(wid); + } + gnt_widget_set_focus(widget, TRUE); + gnt_widget_draw(widget); g_signal_emit(wm, signals[SIG_GIVE_FOCUS], 0, widget); }
--- a/finch/libgnt/gntwm.h Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gntwm.h Thu Jul 05 18:21:41 2007 +0000 @@ -46,6 +46,7 @@ GNT_KP_MODE_NORMAL, GNT_KP_MODE_RESIZE, GNT_KP_MODE_MOVE, + GNT_KP_MODE_WAIT_ON_CHILD } GntKeyPressMode; typedef struct @@ -169,10 +170,15 @@ */ /*GList *(*window_list)();*/ + /* This is invoked whenever the terminal window is resized, or the + * screen session is attached to a new terminal. (ie, from the + * SIGWINCH callback) + */ + void (*terminal_refresh)(GntWM *wm); + void (*res1)(void); void (*res2)(void); void (*res3)(void); - void (*res4)(void); }; G_BEGIN_DECLS
--- a/finch/libgnt/gntws.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/gntws.c Thu Jul 05 18:21:41 2007 +0000 @@ -35,6 +35,9 @@ int n, width = 0; int i; + if (gnt_is_refugee()) + return; + if (taskbar == NULL) { taskbar = newwin(1, getmaxx(stdscr), getmaxy(stdscr) - 1, 0); } else if (reposition) { @@ -114,11 +117,13 @@ g_list_foreach(ws->ordered, widget_hide, nodes); } -void gnt_ws_widget_hide(GntWidget *widget, GHashTable *nodes) { +void gnt_ws_widget_hide(GntWidget *widget, GHashTable *nodes) +{ widget_hide(widget, nodes); } -void gnt_ws_widget_show(GntWidget *widget, GHashTable *nodes) { +void gnt_ws_widget_show(GntWidget *widget, GHashTable *nodes) +{ widget_show(widget, nodes); }
--- a/finch/libgnt/wms/irssi.c Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/libgnt/wms/irssi.c Thu Jul 05 18:21:41 2007 +0000 @@ -126,7 +126,7 @@ int x, y, w, h; name = gnt_widget_get_name(win); - if (!name || strcmp(name, "conversation-window")) { + if (!name || !strstr(name, "conversation-window")) { if (!GNT_IS_MENU(win) && !GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_TRANSIENT)) { if ((!name || strcmp(name, "buddylist"))) { gnt_widget_get_size(win, &w, &h); @@ -181,8 +181,10 @@ GNT_BOX(node->me)->title); wbkgdset(node->window, '\0' | COLOR_PAIR(gnt_widget_has_focus(node->me) ? GNT_COLOR_TITLE : GNT_COLOR_TITLE_D)); mvwaddstr(node->window, 0, 0, title); - update_panels(); - doupdate(); + if (!gnt_is_refugee()) { + update_panels(); + doupdate(); + } return FALSE; } @@ -191,7 +193,7 @@ { GntWidget *win = node->me; const char *name = gnt_widget_get_name(win); - if (!name || !GNT_IS_BOX(win) || strcmp(name, "conversation-window")) + if (!name || !GNT_IS_BOX(win) || !strstr(name, "conversation-window")) return; g_object_set_data(G_OBJECT(win), "irssi-index", GINT_TO_POINTER(g_list_index(wm->cws->list, win))); g_timeout_add(0, (GSourceFunc)update_conv_window_title, node); @@ -248,6 +250,12 @@ } static void +irssi_terminal_refresh(GntWM *wm) +{ + draw_line_separators((Irssi*)wm); +} + +static void irssi_class_init(IrssiClass *klass) { GntWMClass *pclass = GNT_WM_CLASS(klass); @@ -258,6 +266,7 @@ pclass->window_resized = irssi_window_resized; pclass->close_window = irssi_close_window; pclass->window_update = irssi_update_window; + pclass->terminal_refresh = irssi_terminal_refresh; gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "move-up", move_direction, "\033" "K", GINT_TO_POINTER('k'), NULL);
--- a/finch/plugins/pietray.py Thu Jul 05 18:15:32 2007 +0000 +++ b/finch/plugins/pietray.py Thu Jul 05 18:21:41 2007 +0000 @@ -78,9 +78,27 @@ def toggle_pref(item, pref): purple.PurplePrefsSetBool(pref, item.get_active()) +def quit_finch(item, null): + # XXX: Ask first + purple.PurpleCoreQuit() + gtk.main_quit() + +def close_docklet(item, null): + gtk.main_quit() + def popup_menu(icon, button, tm, none): menu = gtk.Menu() + #item = gtk.ImageMenuItem(gtk.STOCK_QUIT) + #item.connect("activate", quit_finch, None) + #menu.append(item) + + item = gtk.ImageMenuItem(gtk.STOCK_CLOSE) + item.connect("activate", close_docklet, None) + menu.append(item) + + menu.append(gtk.MenuItem()) + item = gtk.CheckMenuItem("Blink for unread IM") item.set_active(purple.PurplePrefsGetBool("/plugins/dbus/docklet/blink/im")) item.connect("activate", toggle_pref, "/plugins/dbus/docklet/blink/im") @@ -176,8 +194,14 @@ t = gtk.StatusIcon() t.connect("popup-menu", popup_menu, None) -init_prefs() -detect_unread_conversations() +try: + init_prefs() + detect_unread_conversations() + gtk.main () +except: + dialog = gtk.Dialog("pietray: Error", None, gtk.DIALOG_NO_SEPARATOR | gtk.DIALOG_MODAL, ("Close", gtk.RESPONSE_CLOSE)) + dialog.set_resizable(False) + dialog.vbox.pack_start(gtk.Label("There was some error. Perhaps a purple client is not running."), False, False, 0) + dialog.show_all() + dialog.run() -gtk.main () -
--- a/libpurple/dnssrv.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/dnssrv.c Thu Jul 05 18:21:41 2007 +0000 @@ -50,11 +50,11 @@ u_char buf[1024]; } queryans; #else -static DNS_STATUS WINAPI (*MyDnsQuery_UTF8) ( +static DNS_STATUS (WINAPI *MyDnsQuery_UTF8) ( PCSTR lpstrName, WORD wType, DWORD fOptions, PIP4_ARRAY aipServers, PDNS_RECORD* ppQueryResultsSet, PVOID* pReserved) = NULL; -static void WINAPI (*MyDnsRecordListFree) (PDNS_RECORD pRecordList, +static void (WINAPI *MyDnsRecordListFree) (PDNS_RECORD pRecordList, DNS_FREE_TYPE FreeType) = NULL; #endif
--- a/libpurple/internal.h Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/internal.h Thu Jul 05 18:21:41 2007 +0000 @@ -93,8 +93,9 @@ #include <langinfo.h> #endif +#include <gmodule.h> + #ifdef PURPLE_PLUGINS -# include <gmodule.h> # ifndef _WIN32 # include <dlfcn.h> # endif @@ -111,11 +112,16 @@ # include <unistd.h> #endif -#ifndef MAXPATHLEN -# ifdef PATH_MAX -# define MAXPATHLEN PATH_MAX -# else -# define MAXPATHLEN 1024 +/* MAXPATHLEN should only be used with readlink() on glib < 2.4.0. For + * anything else, use g_file_read_link() or other dynamic functions. This is + * important because Hurd has no hard limits on path length. */ +#if !GLIB_CHECK_VERSION(2,4,0) +# ifndef MAXPATHLEN +# ifdef PATH_MAX +# define MAXPATHLEN PATH_MAX +# else +# define MAXPATHLEN 1024 +# endif # endif #endif
--- a/libpurple/network.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/network.c Thu Jul 05 18:21:41 2007 +0000 @@ -517,7 +517,7 @@ WSAQUERYSET qs; time_t last_trigger = time(NULL); - int WSAAPI (*MyWSANSPIoctl) ( + int (WSAAPI *MyWSANSPIoctl) ( HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer, DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL;
--- a/libpurple/plugins/autoaccept.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/plugins/autoaccept.c Thu Jul 05 18:21:41 2007 +0000 @@ -178,7 +178,8 @@ { PurpleMenuAction *action; - if (!PURPLE_BLIST_NODE_IS_BUDDY(node) && !PURPLE_BLIST_NODE_IS_CONTACT(node)) + if (!PURPLE_BLIST_NODE_IS_BUDDY(node) && !PURPLE_BLIST_NODE_IS_CONTACT(node) && + !(purple_blist_node_get_flags(node) & PURPLE_BLIST_NODE_FLAG_NO_SAVE)) return; action = purple_menu_action_new(_("Autoaccept File Transfers..."),
--- a/libpurple/plugins/filectl.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/plugins/filectl.c Thu Jul 05 18:21:41 2007 +0000 @@ -56,7 +56,7 @@ purple_debug_misc("filectl", "read: %s\n", buffer); command = getarg(buffer, 0, 0); - if (!strncasecmp(command, "login", 6)) { + if (!g_ascii_strncasecmp(command, "login", 6)) { PurpleAccount *account; arg1 = getarg(buffer, 1, 0); @@ -69,7 +69,7 @@ free(arg1); free(arg2); - } else if (!strncasecmp(command, "logout", 7)) { + } else if (!g_ascii_strncasecmp(command, "logout", 7)) { PurpleAccount *account; arg1 = getarg(buffer, 1, 1); @@ -88,7 +88,7 @@ /* purple_find_conversation() is gone in 2.0.0. */ #if 0 - } else if (!strncasecmp(command, "send", 4)) { + } else if (!g_ascii_strncasecmp(command, "send", 4)) { PurpleConversation *conv; arg1 = getarg(buffer, 1, 0); @@ -107,21 +107,21 @@ free(arg2); #endif - } else if (!strncasecmp(command, "away", 4)) { + } else if (!g_ascii_strncasecmp(command, "away", 4)) { arg1 = getarg(buffer, 1, 1); /* serv_set_away_all(arg1); */ free(arg1); - } else if (!strncasecmp(command, "hide", 4)) { + } else if (!g_ascii_strncasecmp(command, "hide", 4)) { purple_blist_set_visible(FALSE); - } else if (!strncasecmp(command, "unhide", 6)) { + } else if (!g_ascii_strncasecmp(command, "unhide", 6)) { purple_blist_set_visible(TRUE); - } else if (!strncasecmp(command, "back", 4)) { + } else if (!g_ascii_strncasecmp(command, "back", 4)) { /* do_im_back(); */ - } else if (!strncasecmp(command, "quit", 4)) { + } else if (!g_ascii_strncasecmp(command, "quit", 4)) { purple_core_quit(); }
--- a/libpurple/plugins/log_reader.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/plugins/log_reader.c Thu Jul 05 18:21:41 2007 +0000 @@ -1,13 +1,5 @@ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - #include <stdio.h> -#ifndef PURPLE_PLUGINS -# define PURPLE_PLUGINS -#endif - #include "internal.h" #include "debug.h" @@ -106,8 +98,8 @@ if (sscanf(date, "%u|%u|%u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday) != 3) { - purple_debug(PURPLE_DEBUG_ERROR, "Adium log parse", - "Filename timestamp parsing error\n"); + purple_debug_error("Adium log parse", + "Filename timestamp parsing error\n"); } else { char *filename = g_build_filename(path, file, NULL); FILE *handle = g_fopen(filename, "rb"); @@ -141,8 +133,8 @@ if (sscanf(contents2, "%u.%u.%u", &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) { - purple_debug(PURPLE_DEBUG_ERROR, "Adium log parse", - "Contents timestamp parsing error\n"); + purple_debug_error("Adium log parse", + "Contents timestamp parsing error\n"); g_free(contents); g_free(filename); continue; @@ -161,7 +153,7 @@ log->logger = adium_logger; log->logger_data = data; - list = g_list_append(list, log); + list = g_list_prepend(list, log); } } else if (purple_str_has_suffix(file, ".adiumLog")) { struct tm tm; @@ -171,8 +163,8 @@ if (sscanf(date, "%u|%u|%u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday) != 3) { - purple_debug(PURPLE_DEBUG_ERROR, "Adium log parse", - "Filename timestamp parsing error\n"); + purple_debug_error("Adium log parse", + "Filename timestamp parsing error\n"); } else { char *filename = g_build_filename(path, file, NULL); FILE *handle = g_fopen(filename, "rb"); @@ -201,8 +193,8 @@ if (sscanf(contents2, "%u.%u.%u", &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) { - purple_debug(PURPLE_DEBUG_ERROR, "Adium log parse", - "Contents timestamp parsing error\n"); + purple_debug_error("Adium log parse", + "Contents timestamp parsing error\n"); g_free(contents); g_free(filename); continue; @@ -222,7 +214,7 @@ log->logger = adium_logger; log->logger_data = data; - list = g_list_append(list, log); + list = g_list_prepend(list, log); } } } @@ -242,17 +234,19 @@ gchar *read = NULL; gsize length; + /* XXX: TODO: We probably want to set PURPLE_LOG_READ_NO_NEWLINE + * XXX: TODO: for HTML logs. */ + *flags = 0; + g_return_val_if_fail(log != NULL, g_strdup("")); data = log->logger_data; g_return_val_if_fail(data->path != NULL, g_strdup("")); - purple_debug(PURPLE_DEBUG_INFO, "Adium log read", - "Reading %s\n", data->path); + purple_debug_info("Adium log read", "Reading %s\n", data->path); if (!g_file_get_contents(data->path, &read, &length, &error)) { - purple_debug(PURPLE_DEBUG_ERROR, "Adium log read", - "Error reading log\n"); + purple_debug_error("Adium log read", "Error reading log\n"); if (error) g_error_free(error); return g_strdup(""); @@ -320,6 +314,7 @@ data = log->logger_data; g_free(data->path); + g_free(data); } @@ -481,7 +476,7 @@ if (!(datetime && *datetime)) { purple_debug_error("MSN log timestamp parse", - "Attribute missing: %s\n", "DateTime"); + "Attribute missing: %s\n", "DateTime"); return (time_t)0; } @@ -502,7 +497,7 @@ if (!(date && *date)) { purple_debug_error("MSN log timestamp parse", - "Attribute missing: %s\n", "Date"); + "Attribute missing: %s\n", "Date"); *tm_out = &tm2; return stamp; } @@ -511,7 +506,7 @@ if (!(time && *time)) { purple_debug_error("MSN log timestamp parse", - "Attribute missing: %s\n", "Time"); + "Attribute missing: %s\n", "Time"); *tm_out = &tm2; return stamp; } @@ -519,7 +514,7 @@ if (sscanf(date, "%u/%u/%u", &month, &day, &year) != 3) { purple_debug_error("MSN log timestamp parse", - "%s parsing error\n", "Date"); + "%s parsing error\n", "Date"); *tm_out = &tm2; return stamp; } @@ -536,7 +531,7 @@ if (sscanf(time, "%u:%u:%u %c", &hour, &min, &sec, &am_pm) != 4) { purple_debug_error("MSN log timestamp parse", - "%s parsing error\n", "Time"); + "%s parsing error\n", "Time"); *tm_out = &tm2; return stamp; } @@ -803,12 +798,10 @@ logfile = NULL; /* No sense saving the obvious buddy@domain.com. */ } - purple_debug(PURPLE_DEBUG_INFO, "MSN log read", - "Reading %s\n", path); + purple_debug_info("MSN log read", "Reading %s\n", path); if (!g_file_get_contents(path, &contents, &length, &error)) { g_free(path); - purple_debug(PURPLE_DEBUG_ERROR, "MSN log read", - "Error reading log\n"); + purple_debug_error("MSN log read", "Error reading log\n"); if (error) g_error_free(error); return list; @@ -837,8 +830,8 @@ session_id = xmlnode_get_attrib(message, "SessionID"); if (!session_id) { - purple_debug(PURPLE_DEBUG_ERROR, "MSN log parse", - "Error parsing message: %s\n", "SessionID missing"); + purple_debug_error("MSN log parse", + "Error parsing message: %s\n", "SessionID missing"); continue; } @@ -864,7 +857,7 @@ log->logger = msn_logger; log->logger_data = data; - list = g_list_append(list, log); + list = g_list_prepend(list, log); } old_session_id = session_id; } @@ -872,7 +865,7 @@ if (data) data->last_log = TRUE; - return list; + return g_list_reverse(list); } static char * msn_logger_read (PurpleLog *log, PurpleLogReadFlags *flags) @@ -881,6 +874,7 @@ GString *text = NULL; xmlnode *message; + *flags = PURPLE_LOG_READ_NO_NEWLINE; g_return_val_if_fail(log != NULL, g_strdup("")); data = log->logger_data; @@ -898,8 +892,8 @@ if (!data->root || !data->message || !data->session_id) { /* Something isn't allocated correctly. */ - purple_debug(PURPLE_DEBUG_ERROR, "MSN log parse", - "Error parsing message: %s\n", "Internal variables inconsistent"); + purple_debug_error("MSN log parse", + "Error parsing message: %s\n", "Internal variables inconsistent"); data->text = text; return text->str; @@ -926,8 +920,8 @@ /* If this triggers, something is wrong with the XML. */ if (!new_session_id) { - purple_debug(PURPLE_DEBUG_ERROR, "MSN log parse", - "Error parsing message: %s\n", "New SessionID missing"); + purple_debug_error("MSN log parse", + "Error parsing message: %s\n", "New SessionID missing"); break; } @@ -1133,10 +1127,10 @@ text = g_string_append(text, style); text = g_string_append(text, "\">"); text = g_string_append(text, tmp); - text = g_string_append(text, "</span>\n"); + text = g_string_append(text, "</span><br>"); } else { text = g_string_append(text, tmp); - text = g_string_append(text, "\n"); + text = g_string_append(text, "<br>"); } g_free(tmp); } @@ -1176,6 +1170,8 @@ if (data->text) g_string_free(data->text, FALSE); + + g_free(data); } @@ -1238,8 +1234,7 @@ path = g_build_filename( logdir, prpl_name, filename, NULL); - purple_debug(PURPLE_DEBUG_INFO, "Trillian log list", - "Reading %s\n", path); + purple_debug_info("Trillian log list", "Reading %s\n", path); /* FIXME: There's really no need to read the entire file at once. * See src/log.c:old_logger_list for a better approach. */ @@ -1252,8 +1247,7 @@ path = g_build_filename( logdir, prpl_name, "Query", filename, NULL); - purple_debug(PURPLE_DEBUG_INFO, "Trillian log list", - "Reading %s\n", path); + purple_debug_info("Trillian log list", "Reading %s\n", path); if (!g_file_get_contents(path, &contents, &length, &error)) { if (error) g_error_free(error); @@ -1283,8 +1277,8 @@ /* This log had no data, so we remove it. */ GList *last = g_list_last(list); - purple_debug(PURPLE_DEBUG_INFO, "Trillian log list", - "Empty log. Offset %i\n", data->offset); + purple_debug_info("Trillian log list", + "Empty log. Offset %i\n", data->offset); trillian_logger_finalize((PurpleLog *)last->data); list = g_list_delete_link(list, last); @@ -1295,7 +1289,7 @@ /* The conditional is to make sure we're not reading off * the end of the string. We don't want strlen(), as that'd * have to count the whole string needlessly. - * + * * The odd check here is because a Session Start at the * beginning of the file can be overwritten with a UTF-8 * byte order mark. Yes, it's weird. @@ -1348,9 +1342,8 @@ &tm.tm_min, &tm.tm_sec, &tm.tm_year) != 5) { - purple_debug(PURPLE_DEBUG_ERROR, - "Trillian log timestamp parse", - "Session Start parsing error\n"); + purple_debug_error("Trillian log timestamp parse", + "Session Start parsing error\n"); } else { PurpleLog *log; @@ -1405,7 +1398,7 @@ log->logger = trillian_logger; log->logger_data = data; - list = g_list_append(list, log); + list = g_list_prepend(list, log); } } } @@ -1420,7 +1413,7 @@ g_free(prpl_name); - return list; + return g_list_reverse(list); } static char * trillian_logger_read (PurpleLog *log, PurpleLogReadFlags *flags) @@ -1434,6 +1427,7 @@ char *c; const char *line; + *flags = PURPLE_LOG_READ_NO_NEWLINE; g_return_val_if_fail(log != NULL, g_strdup("")); data = log->logger_data; @@ -1442,8 +1436,7 @@ g_return_val_if_fail(data->length > 0, g_strdup("")); g_return_val_if_fail(data->their_nickname != NULL, g_strdup("")); - purple_debug(PURPLE_DEBUG_INFO, "Trillian log read", - "Reading %s\n", data->path); + purple_debug_info("Trillian log read", "Reading %s\n", data->path); read = g_malloc(data->length + 2); @@ -1491,7 +1484,7 @@ * "> * Then, replace the next " " (or add this if the end-of-line is reached) with: * </a> - * + * * As implemented, this isn't perfect, but it should cover common cases. */ while (line && (link = strstr(line, "(Link: "))) @@ -1689,10 +1682,14 @@ if (footer) g_string_append(formatted, footer); - g_string_append_c(formatted, '\n'); + g_string_append(formatted, "<br>"); } g_free(read); + + /* XXX: TODO: What can we do about removing \r characters? + * XXX: TODO: and will that allow us to avoid this + * XXX: TODO: g_strchomp(), or is that unrelated? */ /* XXX: TODO: Avoid this g_strchomp() */ return g_strchomp(g_string_free(formatted, FALSE)); } @@ -1728,9 +1725,369 @@ g_free(data->path); g_free(data->their_nickname); - + g_free(data); } +/***************************************************************************** + * QIP Logger * + *****************************************************************************/ + +/* The QIP logger doesn't write logs, only reads them. This is to include + * QIP logs in the log viewer transparently. + */ +#define QIP_LOG_DELIMITER "--------------------------------------" +#define QIP_LOG_IN_MESSAGE (QIP_LOG_DELIMITER "<-") +#define QIP_LOG_OUT_MESSAGE (QIP_LOG_DELIMITER ">-") +#define QIP_LOG_IN_MESSAGE_ESC (QIP_LOG_DELIMITER "<-") +#define QIP_LOG_OUT_MESSAGE_ESC (QIP_LOG_DELIMITER ">-") +#define QIP_LOG_TIMEOUT (60*60) + +static PurpleLogLogger *qip_logger; + +struct qip_logger_data { + + char *path; /* FIXME: Change this to use PurpleStringref like log.c:old_logger_list */ + int offset; + int length; +}; + +static GList *qip_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account) +{ + GList *list = NULL; + const char *logdir; + PurplePlugin *plugin; + PurplePluginProtocolInfo *prpl_info; + char *username; + char *filename; + char *path; + char *contents; + struct qip_logger_data *data = NULL; + struct tm prev_tm; + struct tm tm; + gboolean prev_tm_init = FALSE; + gboolean main_cycle = TRUE; + char *c; + char *start_log; + char *new_line; + int offset = 0; + GError *error; + + g_return_val_if_fail(sn != NULL, list); + g_return_val_if_fail(account != NULL, list); + + /* QIP only supports ICQ. */ + if (strcmp(account->protocol_id, "prpl-icq")) + return list; + + logdir = purple_prefs_get_string("/plugins/core/log_reader/qip/log_directory"); + + /* By clearing the log directory path, this logger can be (effectively) disabled. */ + if (!*logdir) + return list; + + plugin = purple_find_prpl(purple_account_get_protocol_id(account)); + if (!plugin) + return NULL; + + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); + if (!prpl_info->list_icon) + return NULL; + + username = g_strdup(purple_normalize(account, account->username)); + filename = g_strdup_printf("%s.txt", purple_normalize(account, sn)); + path = g_build_filename(logdir, username, "History", filename, NULL); + g_free(username); + g_free(filename); + + purple_debug_info("QIP logger", "Reading %s\n", path); + + error = NULL; + if (!g_file_get_contents(path, &contents, NULL, &error)) { + purple_debug_error("QIP logger", + "Couldn't read file %s: %s \n", path, error->message); + g_error_free(error); + g_free(path); + return list; + } + + c = contents; + start_log = contents; + while (main_cycle) { + + gboolean add_new_log = FALSE; + + if (*c) { + if (purple_str_has_prefix(c, QIP_LOG_IN_MESSAGE) || + purple_str_has_prefix(c, QIP_LOG_OUT_MESSAGE)) { + + char *tmp; + + new_line = c; + + /* find EOL */ + c = strstr(c, "\n"); + c++; + + /* Find the last '(' character. */ + if ((tmp = strstr(c, "\n")) != NULL) { + while (*tmp && *tmp != '(') --tmp; + c = tmp; + } else { + while (*c) + c++; + c--; + c = g_strrstr(c, "("); + } + + if (c != NULL) { + const char *timestamp = ++c; + + /* Parse the time, day, month and year */ + if (sscanf(timestamp, "%u:%u:%u %u/%u/%u", + &tm.tm_hour, &tm.tm_min, &tm.tm_sec, + &tm.tm_mday, &tm.tm_mon, &tm.tm_year) != 6) { + + purple_debug_error("QIP logger list", + "Parsing timestamp error\n"); + } else { + tm.tm_mon -= 1; + tm.tm_year -= 1900; + + /* Let the C library deal with + * daylight savings time. */ + tm.tm_isdst = -1; + + if (!prev_tm_init) { + prev_tm = tm; + prev_tm_init = TRUE; + } else { + add_new_log = difftime(mktime(&tm), mktime(&prev_tm)) > QIP_LOG_TIMEOUT; + } + } + } + } + } else { + add_new_log = TRUE; + main_cycle = FALSE; + new_line = c; + } + + /* adding log */ + if (add_new_log && prev_tm_init) { + PurpleLog *log; + + /* filling data */ + data = g_new0(struct qip_logger_data, 1); + data->path = g_strdup(path); + data->length = new_line - start_log; + data->offset = offset; + offset += data->length; + purple_debug_info("QIP logger list", + "Creating log: path = (%s); length = (%d); offset = (%d)\n", + data->path, data->length, data->offset); + + /* XXX: Look into this later... Should we pass in a struct tm? */ + log = purple_log_new(PURPLE_LOG_IM, sn, account, + NULL, mktime(&prev_tm), NULL); + + log->logger = qip_logger; + log->logger_data = data; + + list = g_list_prepend(list, log); + + prev_tm = tm; + start_log = new_line; + } + + if (*c) { + /* find EOF */ + c = strstr(c, "\n"); + c++; + } + } + + g_free(contents); + g_free(path); + return g_list_reverse(list); +} + +static char *qip_logger_read(PurpleLog *log, PurpleLogReadFlags *flags) +{ + struct qip_logger_data *data; + PurpleBuddy *buddy; + GString *formatted; + char *c; + const char *line; + gchar *contents; + char *selected; + GError *error; + char *utf8_string; + FILE *file; + + *flags = PURPLE_LOG_READ_NO_NEWLINE; + g_return_val_if_fail(log != NULL, g_strdup("")); + + data = log->logger_data; + + g_return_val_if_fail(data->path != NULL, g_strdup("")); + g_return_val_if_fail(data->length > 0, g_strdup("")); + + error = NULL; + + contents = g_malloc(data->length + 2); + + file = g_fopen(data->path, "rb"); + g_return_val_if_fail(file != NULL, g_strdup("")); + + fseek(file, data->offset, SEEK_SET); + fread(contents, data->length, 1, file); + fclose(file); + + contents[data->length] = '\n'; + contents[data->length + 1] = '\0'; + + /* Convert file contents from Cp1251 to UTF-8 codeset */ + error = NULL; + if (!(utf8_string = g_convert(contents, -1, "UTF-8", "Cp1251", NULL, NULL, &error))) { + purple_debug_error("QIP logger", + "Couldn't convert file %s to UTF-8: %s\n", data->path, error->message); + g_error_free(error); + g_free(contents); + return g_strdup(""); + } + + g_free(contents); + contents = g_markup_escape_text(utf8_string, -1); + g_free(utf8_string); + + buddy = purple_find_buddy(log->account, log->name); + + /* Apply formatting... */ + formatted = g_string_sized_new(data->length + 2); + c = contents; + line = contents; + + while (*c) { + gboolean is_in_message = FALSE; + + if (purple_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC) || + purple_str_has_prefix(line, QIP_LOG_OUT_MESSAGE_ESC)) { + + char *tmp; + const char *buddy_name; + + is_in_message = purple_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC); + + /* find EOL */ + c = strstr(c, "\n"); + + /* XXX: Do we need buddy_name when we have buddy->alias? */ + buddy_name = ++c; + + /* Find the last '(' character. */ + if ((tmp = strstr(c, "\n")) != NULL) { + while (*tmp && *tmp != '(') --tmp; + c = tmp; + } else { + while (*c) + c++; + c--; + c = g_strrstr(c, "("); + } + + if (c != NULL) { + const char *timestamp = c; + int hour; + int min; + int sec; + + timestamp++; + + /* Parse the time, day, month and year */ + if (sscanf(timestamp, "%u:%u:%u", + &hour, &min, &sec) != 3) { + purple_debug_error("QIP logger read", + "Parsing timestamp error\n"); + } else { + g_string_append(formatted, "<font size=\"2\">"); + /* TODO: Figure out if we can do anything more locale-independent. */ + g_string_append_printf(formatted, + "(%u:%02u:%02u) %cM ", hour % 12, + min, sec, (hour >= 12) ? 'P': 'A'); + g_string_append(formatted, "</font> "); + + if (is_in_message) { + if (buddy_name != NULL && buddy->alias) { + g_string_append_printf(formatted, + "<span style=\"color: #A82F2F;\">" + "<b>%s</b></span>: ", buddy->alias); + } + } else { + const char *acct_name; + acct_name = purple_account_get_alias(log->account); + if (!acct_name) + acct_name = purple_account_get_username(log->account); + + g_string_append_printf(formatted, + "<span style=\"color: #16569E;\">" + "<b>%s</b></span>: ", acct_name); + } + + /* find EOF */ + c = strstr(c, "\n"); + line = ++c; + } + } + } else { + if ((c = strstr(c, "\n"))) + *c = '\0'; + + if (line[0] != '\n' && line[0] != '\r') { + + g_string_append(formatted, line); + g_string_append(formatted, "<br>"); + } + line = ++c; + } + } + g_free(contents); + + /* XXX: TODO: Avoid this g_strchomp() */ + return g_strchomp(g_string_free(formatted, FALSE)); +} + +static int qip_logger_size (PurpleLog *log) +{ + struct qip_logger_data *data; + char *text; + size_t size; + + g_return_val_if_fail(log != NULL, 0); + + data = log->logger_data; + + if (purple_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) { + return data ? data->length : 0; + } + + text = qip_logger_read(log, NULL); + size = strlen(text); + g_free(text); + + return size; +} + +static void qip_logger_finalize(PurpleLog *log) +{ + struct qip_logger_data *data; + + g_return_if_fail(log != NULL); + + data = log->logger_data; + + g_free(data->path); + g_free(data); +} /***************************************************************************** * Plugin Code * @@ -1761,15 +2118,11 @@ /* Calculate default Adium log directory. */ #ifdef _WIN32 - path = ""; + purple_prefs_add_string("/plugins/core/log_reader/adium/log_directory", ""); #else - path = g_build_filename(purple_home_dir(), "Library", "Application Support", - "Adium 2.0", "Users", "Default", "Logs", NULL); -#endif - + path = g_build_filename(purple_home_dir(), "Library", "Application Support", + "Adium 2.0", "Users", "Default", "Logs", NULL); purple_prefs_add_string("/plugins/core/log_reader/adium/log_directory", path); - -#ifndef _WIN32 g_free(path); #endif @@ -1779,15 +2132,11 @@ /* Calculate default Fire log directory. */ #ifdef _WIN32 - path = ""; + purple_prefs_add_string("/plugins/core/log_reader/fire/log_directory", ""); #else - path = g_build_filename(purple_home_dir(), "Library", "Application Support", - "Fire", "Sessions", NULL); -#endif - + path = g_build_filename(purple_home_dir(), "Library", "Application Support", + "Fire", "Sessions", NULL); purple_prefs_add_string("/plugins/core/log_reader/fire/log_directory", path); - -#ifndef _WIN32 g_free(path); #endif @@ -1799,21 +2148,15 @@ #ifdef _WIN32 folder = wpurple_get_special_folder(CSIDL_PERSONAL); if (folder) { -#endif - path = g_build_filename( -#ifdef _WIN32 - folder, + path = g_build_filename(folder, "My Chat Logs", NULL); + g_free(folder); + } else + path = g_strdup(""); #else - PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, "Documents and Settings", - g_get_user_name(), "My Documents", + path = g_build_filename(PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, + "Documents and Settings", g_get_user_name(), + "My Documents", "My Chat Logs", NULL); #endif - "My Chat Logs", NULL); -#ifdef _WIN32 - g_free(folder); - } else /* !folder */ - path = g_strdup(""); -#endif - purple_prefs_add_string("/plugins/core/log_reader/messenger_plus/log_directory", path); g_free(path); @@ -1825,21 +2168,15 @@ #ifdef _WIN32 folder = wpurple_get_special_folder(CSIDL_PERSONAL); if (folder) { -#endif - path = g_build_filename( -#ifdef _WIN32 - folder, + path = g_build_filename(folder, "My Received Files", NULL); + g_free(folder); + } else + path = g_strdup(""); #else - PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, "Documents and Settings", - g_get_user_name(), "My Documents", + path = g_build_filename(PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, + "Documents and Settings", g_get_user_name(), + "My Documents", "My Received Files", NULL); #endif - "My Received Files", NULL); -#ifdef _WIN32 - g_free(folder); - } else /* !folder */ - path = g_strdup(""); -#endif - purple_prefs_add_string("/plugins/core/log_reader/msn/log_directory", path); g_free(path); @@ -1882,7 +2219,7 @@ char *folder = wpurple_get_special_folder(CSIDL_PROGRAM_FILES); if (folder) { path = g_build_filename(folder, "Trillian", - "users", "default", "talk.ini", NULL); + "users", "default", "talk.ini", NULL); g_free(folder); } } @@ -1894,25 +2231,25 @@ #if 0 && GLIB_CHECK_VERSION(2,6,0) /* FIXME: Not tested yet. */ GKeyFile *key_file; - purple_debug(PURPLE_DEBUG_INFO, "Trillian talk.ini read", - "Reading %s\n", path); + purple_debug_info("Trillian talk.ini read", "Reading %s\n", path); + + error = NULL; if (!g_key_file_load_from_file(key_file, path, G_KEY_FILE_NONE, GError &error)) { - purple_debug(PURPLE_DEBUG_ERROR, "Trillian talk.ini read", - "Error reading talk.ini\n"); + purple_debug_error("Trillian talk.ini read", + "Error reading talk.ini\n"); if (error) g_error_free(error); } else { char *logdir = g_key_file_get_string(key_file, "Logging", "Directory", &error); if (error) { - purple_debug(PURPLE_DEBUG_ERROR, "Trillian talk.ini read", - "Error reading Directory value from Logging section\n"); + purple_debug_error("Trillian talk.ini read", + "Error reading Directory value from Logging section\n"); g_error_free(error); } if (logdir) { g_strchomp(logdir); - purple_prefs_add_string( - "/plugins/core/log_reader/trillian/log_directory", logdir); + purple_prefs_add_string("/plugins/core/log_reader/trillian/log_directory", logdir); found = TRUE; } @@ -1922,11 +2259,11 @@ gsize length; gchar *contents = NULL; - purple_debug(PURPLE_DEBUG_INFO, "Trillian talk.ini read", + purple_debug_info("Trillian talk.ini read", "Reading %s\n", path); if (!g_file_get_contents(path, &contents, &length, &error)) { - purple_debug(PURPLE_DEBUG_ERROR, "Trillian talk.ini read", - "Error reading talk.ini\n"); + purple_debug_error("Trillian talk.ini read", + "Error reading talk.ini\n"); if (error) g_error_free(error); } else { @@ -1957,32 +2294,43 @@ } /* path */ if (!found) { -#endif /* defined(_WIN32) */ + folder = wpurple_get_special_folder(CSIDL_PROGRAM_FILES); + if (folder) { + path = g_build_filename(folder, "Trillian", "users", + "default", "logs", NULL); + g_free(folder); + } else + path = g_strdup(""); + } +#else /* !defined(_WIN32) */ + /* TODO: At some point, this could attempt to parse talk.ini + * TODO: from the default Trillian install directory on the + * TODO: Windows mount point. */ /* Calculate default Trillian log directory. */ + path = g_build_filename(PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, + "Program Files", "Trillian", "users", + "default", "logs", NULL); +#endif + + + /* Add QIP log directory preference. */ + purple_prefs_add_none("/plugins/core/log_reader/qip"); + + /* Calculate default QIP log directory. */ #ifdef _WIN32 folder = wpurple_get_special_folder(CSIDL_PROGRAM_FILES); if (folder) { -#endif - path = g_build_filename( -#ifdef _WIN32 - folder, + path = g_build_filename(folder, "QIP", "Users", NULL); + g_free(folder); + } else + path = g_strdup(""); #else - PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, "Program Files", + path = g_build_filename(PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, + "Program Files", "QIP", "Users", NULL); #endif - "Trillian", "users", "default", "logs", NULL); -#ifdef _WIN32 - g_free(folder); - } else /* !folder */ - path = g_strdup(""); -#endif - - purple_prefs_add_string("/plugins/core/log_reader/trillian/log_directory", path); + purple_prefs_add_string("/plugins/core/log_reader/qip/log_directory", path); g_free(path); - -#ifdef _WIN32 - } /* !found */ -#endif } static gboolean @@ -2026,11 +2374,24 @@ messenger_plus_logger_read, messenger_plus_logger_size); purple_log_logger_add(messenger_plus_logger); + #endif /* The names of IM clients are marked for translation at the request of translators who wanted to transliterate them. Many translators choose to leave them alone. Choose what's best for your language. */ + qip_logger = purple_log_logger_new("qip", _("QIP"), 6, + NULL, + NULL, + qip_logger_finalize, + qip_logger_list, + qip_logger_read, + qip_logger_size); + purple_log_logger_add(qip_logger); + + /* The names of IM clients are marked for translation at the request of + translators who wanted to transliterate them. Many translators + choose to leave them alone. Choose what's best for your language. */ msn_logger = purple_log_logger_new("msn", _("MSN Messenger"), 6, NULL, NULL, @@ -2067,6 +2428,7 @@ #endif purple_log_logger_remove(msn_logger); purple_log_logger_remove(trillian_logger); + purple_log_logger_remove(qip_logger); return TRUE; } @@ -2116,6 +2478,10 @@ #endif ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/qip/log_directory", _("QIP")); + purple_plugin_pref_frame_add(frame, ppref); + + ppref = purple_plugin_pref_new_with_name_and_label( "/plugins/core/log_reader/msn/log_directory", _("MSN Messenger")); purple_plugin_pref_frame_add(frame, ppref);
--- a/libpurple/protocols/msn/directconn.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/protocols/msn/directconn.c Thu Jul 05 18:21:41 2007 +0000 @@ -370,7 +370,7 @@ else { struct sockaddr_in client_addr; - unsigned int client; + socklen_t client; fd = accept (source, (struct sockaddr *)&client_addr, &client); }
--- a/libpurple/protocols/msn/msn-utils.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/protocols/msn/msn-utils.c Thu Jul 05 18:21:41 2007 +0000 @@ -296,7 +296,7 @@ char *attr_dir; attributes = g_strndup(c, attr_len); attr_dir = purple_markup_get_css_property(attributes, "direction"); - if (attr_dir && (!strncasecmp(attr_dir, "RTL", 3))) + if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3))) direction = '1'; g_free(attr_dir); g_free(attributes);
--- a/libpurple/protocols/oscar/odc.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/protocols/oscar/odc.c Thu Jul 05 18:21:41 2007 +0000 @@ -314,7 +314,7 @@ tmp += size; /* Skip past the closing </data> tag */ - if (strncasecmp(tmp, "</data>", 7)) + if (g_ascii_strncasecmp(tmp, "</data>", 7)) { g_free(embedded_data); break;
--- a/libpurple/protocols/qq/im.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/protocols/qq/im.c Thu Jul 05 18:21:41 2007 +0000 @@ -573,7 +573,7 @@ read_packet_b(data, &cursor, len, &reply); if (reply != QQ_SEND_IM_REPLY_OK) { purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Send IM fail\n"); - purple_notify_error(gc, _("Server ACK"), _("Send IM fail\n"), NULL); + purple_notify_error(gc, _("Server ACK"), _("Failed to send IM."), NULL); } else purple_debug(PURPLE_DEBUG_INFO, "QQ", "IM ACK OK\n");
--- a/libpurple/protocols/silc/buddy.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/protocols/silc/buddy.c Thu Jul 05 18:21:41 2007 +0000 @@ -1252,7 +1252,7 @@ SilcClientEntry entry; silc_dlist_start(clients); while ((entry = silc_dlist_get(clients))) { - if (!strncasecmp(b->name, entry->nickname, + if (!g_ascii_strncasecmp(b->name, entry->nickname, strlen(b->name))) { client_entry = entry; break;
--- a/libpurple/protocols/silc/util.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/protocols/silc/util.c Thu Jul 05 18:21:41 2007 +0000 @@ -623,15 +623,15 @@ ct = strrchr(filename, '.'); if (!ct) return NULL; - else if (!strcasecmp(".png", ct)) + else if (!g_ascii_strcasecmp(".png", ct)) return strdup("image/png"); - else if (!strcasecmp(".jpg", ct)) + else if (!g_ascii_strcasecmp(".jpg", ct)) return strdup("image/jpeg"); - else if (!strcasecmp(".jpeg", ct)) + else if (!g_ascii_strcasecmp(".jpeg", ct)) return strdup("image/jpeg"); - else if (!strcasecmp(".gif", ct)) + else if (!g_ascii_strcasecmp(".gif", ct)) return strdup("image/gif"); - else if (!strcasecmp(".tiff", ct)) + else if (!g_ascii_strcasecmp(".tiff", ct)) return strdup("image/tiff"); return NULL;
--- a/libpurple/protocols/silc10/buddy.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/protocols/silc10/buddy.c Thu Jul 05 18:21:41 2007 +0000 @@ -1246,7 +1246,7 @@ buddy nickname. */ int i; for (i = 0; i < clients_count; i++) { - if (!strncasecmp(b->name, clients[i]->nickname, + if (!g_ascii_strncasecmp(b->name, clients[i]->nickname, strlen(b->name))) { clients[0] = clients[i]; break;
--- a/libpurple/protocols/simple/simple.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/protocols/simple/simple.c Thu Jul 05 18:21:41 2007 +0000 @@ -337,7 +337,7 @@ return; } - if(!g_strncasecmp(hdr, "NTLM", 4)) { + if(!g_ascii_strncasecmp(hdr, "NTLM", 4)) { purple_debug_info("simple", "found NTLM\n"); auth->type = 2; parts = g_strsplit(hdr+5, "\", ", 0);
--- a/libpurple/protocols/yahoo/yahoo_picture.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo_picture.c Thu Jul 05 18:21:41 2007 +0000 @@ -110,7 +110,7 @@ } /* Yahoo IM 6 spits out 0.png as the URL if the buddy icon is not set */ - if (who && got_icon_info && url && !strncasecmp(url, "http://", 7)) { + if (who && got_icon_info && url && !g_ascii_strncasecmp(url, "http://", 7)) { /* TODO: make this work p2p, try p2p before the url */ PurpleUtilFetchUrlData *url_data; struct yahoo_fetch_picture_data *data;
--- a/libpurple/protocols/zephyr/ZVariables.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/protocols/zephyr/ZVariables.c Thu Jul 05 18:21:41 2007 +0000 @@ -186,7 +186,7 @@ #define max(a,b) ((a > b) ? (a) : (b)) #endif - if (strncasecmp(bfr, var, max(strlen(var),cp - bfr))) + if (g_strncasecmp(bfr, var, max(strlen(var), cp - bfr))) return(0); /* var is not the var in bfr ==> no match */
--- a/libpurple/tests/check_libpurple.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/tests/check_libpurple.c Thu Jul 05 18:21:41 2007 +0000 @@ -20,9 +20,17 @@ static PurpleEventLoopUiOps eventloop_ui_ops = { g_timeout_add, - (guint (*)(guint))g_source_remove, + g_source_remove, purple_check_input_add, - (guint (*)(guint))g_source_remove, + g_source_remove, + NULL, /* input_get_error */ +#if GLIB_CHECK_VERSION(2,14,0) + g_timeout_add_seconds, +#else + NULL, +#endif + NULL, + NULL, NULL };
--- a/libpurple/util.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/util.c Thu Jul 05 18:21:41 2007 +0000 @@ -1730,7 +1730,7 @@ if (cdata_close_tag) { /* Note: Don't even assume any other tag is a tag in CDATA */ - if (strncasecmp(str2 + i, cdata_close_tag, + if (g_ascii_strncasecmp(str2 + i, cdata_close_tag, strlen(cdata_close_tag)) == 0) { i += strlen(cdata_close_tag) - 1; @@ -1738,12 +1738,12 @@ } continue; } - else if (strncasecmp(str2 + i, "<td", 3) == 0 && closing_td_p) + else if (g_ascii_strncasecmp(str2 + i, "<td", 3) == 0 && closing_td_p) { str2[j++] = '\t'; visible = TRUE; } - else if (strncasecmp(str2 + i, "</td>", 5) == 0) + else if (g_ascii_strncasecmp(str2 + i, "</td>", 5) == 0) { closing_td_p = TRUE; visible = FALSE; @@ -1771,7 +1771,7 @@ /* If we've got an <a> tag with an href, save the address * to print later. */ - if (strncasecmp(str2 + i, "<a", 2) == 0 && + if (g_ascii_strncasecmp(str2 + i, "<a", 2) == 0 && g_ascii_isspace(str2[i+2])) { int st; /* start of href, inclusive [ */ @@ -1780,7 +1780,7 @@ /* Find start of href */ for (st = i + 3; st < k; st++) { - if (strncasecmp(str2+st, "href=", 5) == 0) + if (g_ascii_strncasecmp(str2+st, "href=", 5) == 0) { st += 5; if (str2[st] == '"' || str2[st] == '\'') @@ -1812,7 +1812,7 @@ /* Replace </a> with an ascii representation of the * address the link was pointing to. */ - else if (href != NULL && strncasecmp(str2 + i, "</a>", 4) == 0) + else if (href != NULL && g_ascii_strncasecmp(str2 + i, "</a>", 4) == 0) { size_t hrlen = strlen(href); @@ -1834,29 +1834,29 @@ } /* Check for tags which should be mapped to newline */ - else if (strncasecmp(str2 + i, "<p>", 3) == 0 - || strncasecmp(str2 + i, "<tr", 3) == 0 - || strncasecmp(str2 + i, "<br", 3) == 0 - || strncasecmp(str2 + i, "<hr", 3) == 0 - || strncasecmp(str2 + i, "<li", 3) == 0 - || strncasecmp(str2 + i, "<div", 4) == 0 - || strncasecmp(str2 + i, "</table>", 8) == 0) + else if (g_ascii_strncasecmp(str2 + i, "<p>", 3) == 0 + || g_ascii_strncasecmp(str2 + i, "<tr", 3) == 0 + || g_ascii_strncasecmp(str2 + i, "<br", 3) == 0 + || g_ascii_strncasecmp(str2 + i, "<hr", 3) == 0 + || g_ascii_strncasecmp(str2 + i, "<li", 3) == 0 + || g_ascii_strncasecmp(str2 + i, "<div", 4) == 0 + || g_ascii_strncasecmp(str2 + i, "</table>", 8) == 0) { str2[j++] = '\n'; } /* Check for tags which begin CDATA and need to be closed */ #if 0 /* FIXME.. option is end tag optional, we can't handle this right now */ - else if (strncasecmp(str2 + i, "<option", 7) == 0) + else if (g_ascii_strncasecmp(str2 + i, "<option", 7) == 0) { /* FIXME: We should not do this if the OPTION is SELECT'd */ cdata_close_tag = "</option>"; } #endif - else if (strncasecmp(str2 + i, "<script", 7) == 0) + else if (g_ascii_strncasecmp(str2 + i, "<script", 7) == 0) { cdata_close_tag = "</script>"; } - else if (strncasecmp(str2 + i, "<style", 6) == 0) + else if (g_ascii_strncasecmp(str2 + i, "<style", 6) == 0) { cdata_close_tag = "</style>"; } @@ -3079,7 +3079,7 @@ i = 0; /* position in the source string */ j = 0; /* number of occurrences of "delimiter" */ while (string[i] != '\0') { - if (!strncasecmp(&string[i], delimiter, length_del)) { + if (!g_ascii_strncasecmp(&string[i], delimiter, length_del)) { i += length_del; j += length_rep; } else { @@ -3093,7 +3093,7 @@ i = 0; /* position in the source string */ j = 0; /* position in the destination string */ while (string[i] != '\0') { - if (!strncasecmp(&string[i], delimiter, length_del)) { + if (!g_ascii_strncasecmp(&string[i], delimiter, length_del)) { strncpy(&ret[j], replacement, length_rep); i += length_del; j += length_rep;
--- a/libpurple/win32/libc_interface.c Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/win32/libc_interface.c Thu Jul 05 18:21:41 2007 +0000 @@ -413,8 +413,8 @@ /* newname exists */ if(g_stat(newname, &newstat) == 0) { /* oldname is a dir */ - if(_S_ISDIR(oldstat.st_mode)) { - if(!_S_ISDIR(newstat.st_mode)) { + if(S_ISDIR(oldstat.st_mode)) { + if(!S_ISDIR(newstat.st_mode)) { return g_rename(oldname, newname); } /* newname is a dir */ @@ -430,7 +430,7 @@ /* oldname is not a dir */ else { /* newname is a dir */ - if(_S_ISDIR(newstat.st_mode)) { + if(S_ISDIR(newstat.st_mode)) { errno = EISDIR; return -1; }
--- a/libpurple/win32/libc_interface.h Thu Jul 05 18:15:32 2007 +0000 +++ b/libpurple/win32/libc_interface.h Thu Jul 05 18:21:41 2007 +0000 @@ -29,6 +29,16 @@ #include "libc_internal.h" #include <glib.h> +#ifdef _MSC_VER +#define S_IRUSR S_IREAD +#define S_IWUSR S_IWRITE +#define S_IXUSR S_IEXEC + +#define S_ISDIR(m) (((m)&S_IFDIR)==S_IFDIR) + +#define F_OK 0 +#endif + /* sys/socket.h */ #define socket( namespace, style, protocol ) \ wpurple_socket( namespace, style, protocol ) @@ -36,11 +46,11 @@ #define connect( socket, addr, length ) \ wpurple_connect( socket, addr, length ) -#define getsockopt( args... ) \ -wpurple_getsockopt( args ) +#define getsockopt( socket, level, optname, optval, optlenptr ) \ +wpurple_getsockopt( socket, level, optname, optval, optlenptr ) -#define setsockopt( args... ) \ -wpurple_setsockopt( args ) +#define setsockopt( socket, level, optname, optval, optlen ) \ +wpurple_setsockopt( socket, level, optname, optval, optlen ) #define getsockname( socket, addr, lenptr ) \ wpurple_getsockname( socket, addr, lenptr )
--- a/pidgin/gtkblist.c Thu Jul 05 18:15:32 2007 +0000 +++ b/pidgin/gtkblist.c Thu Jul 05 18:21:41 2007 +0000 @@ -2873,7 +2873,11 @@ { N_("/_Help"), NULL, NULL, 0, "<Branch>", NULL }, { N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP }, { N_("/Help/_Debug Window"), NULL, toggle_debug, 0, "<Item>", NULL }, +#if GTK_CHECK_VERSION(2,6,0) + { N_("/Help/_About"), NULL, pidgin_dialogs_about, 0, "<StockItem>", GTK_STOCK_ABOUT }, +#else { N_("/Help/_About"), NULL, pidgin_dialogs_about, 0, "<Item>", NULL }, +#endif }; /*********************************************************
--- a/pidgin/gtkconv.c Thu Jul 05 18:15:32 2007 +0000 +++ b/pidgin/gtkconv.c Thu Jul 05 18:21:41 2007 +0000 @@ -2406,9 +2406,6 @@ return FALSE; } - gtkconv->auto_resize = TRUE; - g_idle_add(reset_auto_resize_cb, gtkconv); - gdk_pixbuf_animation_iter_advance(gtkconv->u.im->iter, NULL); buf = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter); @@ -4256,7 +4253,7 @@ if (sr.height < height + PIDGIN_HIG_BOX_SPACE) { gtkconv->auto_resize = TRUE; gtkconv->entry_growing = TRUE; - gtk_widget_set_size_request(gtkconv->lower_hbox, -1, height + PIDGIN_HIG_BOX_SPACE); + gtk_widget_set_size_request(gtkconv->entry, -1, height); g_idle_add(reset_auto_resize_cb, gtkconv); } }
--- a/pidgin/gtkdocklet.c Thu Jul 05 18:15:32 2007 +0000 +++ b/pidgin/gtkdocklet.c Thu Jul 05 18:21:41 2007 +0000 @@ -636,7 +636,7 @@ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet"); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/blink", FALSE); - purple_prefs_add_string(PIDGIN_PREFS_ROOT "/docklet/show", "always"); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/docklet/show", "pending"); purple_prefs_connect_callback(docklet_handle, PIDGIN_PREFS_ROOT "/docklet/show", docklet_show_pref_changed_cb, NULL);
--- a/pidgin/gtkimhtml.c Thu Jul 05 18:15:32 2007 +0000 +++ b/pidgin/gtkimhtml.c Thu Jul 05 18:21:41 2007 +0000 @@ -494,6 +494,8 @@ GSList *tags = NULL, *templist = NULL; GdkColor *norm, *pre; GtkTextTag *tag = NULL, *oldprelit_tag; + GtkTextChildAnchor* anchor; + gboolean hand = TRUE; oldprelit_tag = GTK_IMHTML(imhtml)->prelit_tag; @@ -551,8 +553,15 @@ GTK_IMHTML(imhtml)->tip_timer = 0; } + /* If we don't have a tip from a URL, let's see if we have a tip from a smiley */ + anchor = gtk_text_iter_get_child_anchor(&iter); + if (anchor) { + tip = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_plaintext"); + hand = FALSE; + } + if (tip){ - if (!GTK_IMHTML(imhtml)->editable) + if (!GTK_IMHTML(imhtml)->editable && hand) gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->hand_cursor); GTK_IMHTML(imhtml)->tip_timer = g_timeout_add (TOOLTIP_TIMEOUT, gtk_imhtml_tip, imhtml); @@ -2153,7 +2162,7 @@ gint i; for(i=0; i<accepted_protocols_size; i++){ - if( strncasecmp(text, accepted_protocols[i], strlen(accepted_protocols[i])) == 0 ){ + if( g_ascii_strncasecmp(text, accepted_protocols[i], strlen(accepted_protocols[i])) == 0 ){ return strlen(accepted_protocols[i]); } } @@ -2726,7 +2735,7 @@ /* NEW_BIT (NEW_TEXT_BIT); */ /* Bi-Directional text support */ - if (direction && (!strncasecmp(direction, "RTL", 3))) { + if (direction && (!g_ascii_strncasecmp(direction, "RTL", 3))) { rtl_direction = TRUE; /* insert RLE character to set direction */ ws[wpos++] = 0xE2; @@ -2738,7 +2747,7 @@ } g_free(direction); - if (alignment && (!strncasecmp(alignment, "RIGHT", 5))) { + if (alignment && (!g_ascii_strncasecmp(alignment, "RIGHT", 5))) { align_right = TRUE; align_line = gtk_text_iter_get_line(iter); } @@ -4621,6 +4630,7 @@ GtkWidget *img = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_MENU); gtk_container_add(GTK_CONTAINER(ebox), img); gtk_widget_show(img); + g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", g_strdup(unescaped), g_free); gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), ebox, anchor); } } else {
--- a/pidgin/gtkimhtmltoolbar.c Thu Jul 05 18:15:32 2007 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Thu Jul 05 18:21:41 2007 +0000 @@ -37,6 +37,8 @@ #include "gtkthemes.h" #include "gtkutils.h" +#include <gdk/gdkkeysyms.h> + static GtkHBoxClass *parent_class = NULL; static void toggle_button_set_active_block(GtkToggleButton *button, @@ -629,7 +631,6 @@ return ls; } - static gboolean smiley_is_unique(GSList *list, GtkIMHtmlSmiley *smiley) { @@ -642,6 +643,18 @@ return TRUE; } +static gboolean +smiley_dialog_input_cb(GtkWidget *dialog, GdkEvent *event, GtkIMHtmlToolbar *toolbar) +{ + if ((event->type == GDK_KEY_PRESS && event->key.keyval == GDK_Escape) || + (event->type == GDK_BUTTON_PRESS && event->button.button == 1)) + { + close_smiley_dialog(NULL, NULL, toolbar); + return TRUE; + } + + return FALSE; +} static void insert_smiley_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar) @@ -720,11 +733,16 @@ free(it_tmp); } gtk_box_pack_start(GTK_BOX(smiley_table), line, FALSE, TRUE, 0); + + gtk_widget_add_events(dialog, GDK_KEY_PRESS_MASK); } else { smiley_table = gtk_label_new(_("This theme has no available smileys.")); + gtk_widget_add_events(dialog, GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK); + g_signal_connect(G_OBJECT(dialog), "button-press-event", (GCallback)smiley_dialog_input_cb, toolbar); } + g_signal_connect(G_OBJECT(dialog), "key-press-event", (GCallback)smiley_dialog_input_cb, toolbar); gtk_container_add(GTK_CONTAINER(dialog), smiley_table); gtk_widget_show(smiley_table);
--- a/pidgin/gtkprefs.c Thu Jul 05 18:15:32 2007 +0000 +++ b/pidgin/gtkprefs.c Thu Jul 05 18:21:41 2007 +0000 @@ -60,6 +60,7 @@ static GtkWidget *sound_entry = NULL; static GtkListStore *smiley_theme_store = NULL; +static GtkTreeSelection *smiley_theme_sel = NULL; static GtkWidget *prefs_proxy_frame = NULL; static GtkWidget *prefs = NULL; @@ -366,9 +367,12 @@ GValue val; GtkTreePath *path, *oldpath; struct smiley_theme *new_theme, *old_theme; + GtkWidget *remove_button = g_object_get_data(G_OBJECT(sel), "remove_button"); - if (!gtk_tree_selection_get_selected(sel, &model, &iter)) + if (!gtk_tree_selection_get_selected(sel, &model, &iter)) { + gtk_widget_set_sensitive(remove_button, FALSE); return; + } old_theme = current_smiley_theme; val.g_type = 0; @@ -376,6 +380,9 @@ path = gtk_tree_model_get_path(model, &iter); themename = g_value_get_string(&val); purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", themename); + + gtk_widget_set_sensitive(remove_button, (strcmp(themename, "none") && + strcmp(themename, _("Default")))); g_value_unset (&val); /* current_smiley_theme is set in callback for the above pref change */ @@ -514,8 +521,13 @@ g_free(destdir); theme_rowref = theme_refresh_theme_list(); - if (theme_rowref != NULL) + if (theme_rowref != NULL) { + GtkTreePath *tp = gtk_tree_row_reference_get_path(theme_rowref); + + if (tp) + gtk_tree_selection_select_path(smiley_theme_sel, tp); gtk_tree_row_reference_free(theme_rowref); + } } static void @@ -619,9 +631,55 @@ return ret; } +static void +request_theme_file_name_cb(gpointer data, char *theme_file_name) +{ + theme_install_theme(theme_file_name, NULL) ; +} + +static void +add_theme_button_clicked_cb(GtkWidget *widget, gpointer null) +{ + purple_request_file(NULL, _("Install Theme"), NULL, FALSE, (GCallback)request_theme_file_name_cb, NULL, NULL, NULL, NULL, NULL) ; +} + +static void +remove_theme_button_clicked_cb(GtkWidget *button, GtkTreeView *tv) +{ + char *theme_name = NULL, *theme_file = NULL; + GtkTreeModel *tm; + GtkTreeIter itr; + GtkTreeRowReference *trr = NULL; + + if ((tm = gtk_tree_view_get_model(tv)) == NULL) + return; + if (!gtk_tree_selection_get_selected(smiley_theme_sel, NULL, &itr)) + return; + gtk_tree_model_get(tm, &itr, 2, &theme_file, 3, &theme_name, -1); + + if (theme_file && theme_name && strcmp(theme_name, "none")) + pidgin_themes_remove_smiley_theme(theme_file); + + if ((trr = theme_refresh_theme_list()) != NULL) { + GtkTreePath *tp = gtk_tree_row_reference_get_path(trr); + + if (tp) { + gtk_tree_selection_select_path(smiley_theme_sel, tp); + gtk_tree_path_free(tp); + } + gtk_tree_row_reference_free(trr); + } + + g_free(theme_file); + g_free(theme_name); +} + static GtkWidget * theme_page() { + GtkWidget *add_button, *remove_button; + GtkWidget *hbox_buttons; + GtkWidget *alignment; GtkWidget *ret; GtkWidget *sw; GtkWidget *view; @@ -661,7 +719,7 @@ g_signal_connect(G_OBJECT(view), "drag_data_received", G_CALLBACK(theme_dnd_recv), smiley_theme_store); rend = gtk_cell_renderer_pixbuf_new(); - sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + smiley_theme_sel = sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); /* Custom sort so "none" theme is at top of list */ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(smiley_theme_store), @@ -687,6 +745,25 @@ g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(smiley_sel), NULL); + alignment = gtk_alignment_new(1.0, 0.5, 0.0, 1.0); + gtk_widget_show(alignment); + gtk_box_pack_start(GTK_BOX(ret), alignment, FALSE, TRUE, 0); + + hbox_buttons = gtk_hbox_new(TRUE, PIDGIN_HIG_CAT_SPACE); + gtk_widget_show(hbox_buttons); + gtk_container_add(GTK_CONTAINER(alignment), hbox_buttons); + + add_button = gtk_button_new_from_stock(GTK_STOCK_ADD); + gtk_widget_show(add_button); + gtk_box_pack_start(GTK_BOX(hbox_buttons), add_button, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(add_button), "clicked", (GCallback)add_theme_button_clicked_cb, view); + + remove_button = gtk_button_new_from_stock(GTK_STOCK_REMOVE); + gtk_widget_show(remove_button); + gtk_box_pack_start(GTK_BOX(hbox_buttons), remove_button, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(remove_button), "clicked", (GCallback)remove_theme_button_clicked_cb, view); + g_object_set_data(G_OBJECT(sel), "remove_button", remove_button); + if (rowref) { GtkTreePath *path = gtk_tree_row_reference_get_path(rowref); gtk_tree_row_reference_free(rowref); @@ -975,7 +1052,7 @@ GTK_IMHTML_BACKCOLOR | GTK_IMHTML_BACKGROUND); - gtk_imhtml_append_text(GTK_IMHTML(imhtml), _("This is how your outgoing message text will appear when you use protocols that support formatting. :)"), 0); + gtk_imhtml_append_text(GTK_IMHTML(imhtml), _("This is how your outgoing message text will appear when you use protocols that support formatting."), 0); gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
--- a/pidgin/gtkthemes.c Thu Jul 05 18:15:32 2007 +0000 +++ b/pidgin/gtkthemes.c Thu Jul 05 18:21:41 2007 +0000 @@ -36,6 +36,8 @@ GSList *smiley_themes = NULL; struct smiley_theme *current_smiley_theme; +static void pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme *theme); + gboolean pidgin_themes_smileys_disabled() { if (!current_smiley_theme) @@ -44,6 +46,79 @@ return strcmp(current_smiley_theme->name, "none") == 0; } +static void +pidgin_themes_destroy_smiley_theme(struct smiley_theme *theme) +{ + pidgin_themes_destroy_smiley_theme_smileys(theme); + + g_free(theme->name); + g_free(theme->desc); + g_free(theme->author); + g_free(theme->icon); + g_free(theme->path); + g_free(theme); +} + +static void pidgin_themes_remove_theme_dir(const char *theme_dir_name) +{ + GString *str = NULL; + const char *file_name = NULL; + GDir *theme_dir = NULL; + + if ((theme_dir = g_dir_open(theme_dir_name, 0, NULL)) != NULL) { + if ((str = g_string_new(theme_dir_name)) != NULL) { + while ((file_name = g_dir_read_name(theme_dir)) != NULL) { + g_string_printf(str, "%s%s%s", theme_dir_name, G_DIR_SEPARATOR_S, file_name); + g_unlink(str->str); + } + g_string_free(str, TRUE); + } + g_dir_close(theme_dir); + g_rmdir(theme_dir_name); + } +} + +void pidgin_themes_remove_smiley_theme(const char *file) +{ + char *theme_dir = NULL, *last_slash = NULL; + g_return_if_fail(NULL != file); + + if (!g_file_test(file, G_FILE_TEST_EXISTS)) return; + if ((theme_dir = g_strdup(file)) == NULL) return ; + + if ((last_slash = g_strrstr(theme_dir, G_DIR_SEPARATOR_S)) != NULL) { + GSList *iter = NULL; + struct smiley_theme *theme = NULL, *new_theme = NULL; + + *last_slash = 0; + + /* Delete files on disk */ + pidgin_themes_remove_theme_dir(theme_dir); + + /* Find theme in themes list and remove it */ + for (iter = smiley_themes ; iter ; iter = iter->next) { + theme = ((struct smiley_theme *)(iter->data)); + if (!strcmp(theme->path, file)) + break ; + } + if (iter) { + if (theme == current_smiley_theme) { + new_theme = ((struct smiley_theme *)(NULL == iter->next ? (smiley_themes == iter ? NULL : smiley_themes->data) : iter->next->data)); + if (new_theme) + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", new_theme->name); + else + current_smiley_theme = NULL; + } + smiley_themes = g_slist_delete_link(smiley_themes, iter); + + /* Destroy theme structure */ + pidgin_themes_destroy_smiley_theme(theme); + } + } + + g_free(theme_dir); +} + void pidgin_themes_smiley_themeize(GtkWidget *imhtml) { struct smiley_list *list; @@ -64,7 +139,7 @@ } static void -pidgin_themes_destroy_smiley_theme(struct smiley_theme *theme) +pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme *theme) { GHashTable *already_freed; struct smiley_list *wer; @@ -92,6 +167,32 @@ g_hash_table_destroy(already_freed); } +static void +pidgin_smiley_themes_remove_non_existing() +{ + static struct smiley_theme *theme = NULL; + GSList *iter = NULL; + + if (!smiley_themes) return ; + + for (iter = smiley_themes ; iter ; iter = iter->next) { + theme = ((struct smiley_theme *)(iter->data)); + if (!g_file_test(theme->path, G_FILE_TEST_EXISTS)) { + if (theme == current_smiley_theme) + current_smiley_theme = ((struct smiley_theme *)(NULL == iter->next ? NULL : iter->next->data)); + pidgin_themes_destroy_smiley_theme(theme); + iter->data = NULL; + } + } + /* Remove all elements whose data is NULL */ + smiley_themes = g_slist_remove_all(smiley_themes, NULL); + + if (!current_smiley_theme && smiley_themes) { + struct smiley_theme *smile = g_slist_last(smiley_themes)->data; + pidgin_themes_load_smiley_theme(smile->path, TRUE); + } +} + void pidgin_themes_load_smiley_theme(const char *file, gboolean load) { FILE *f = g_fopen(file, "r"); @@ -221,14 +322,6 @@ purple_debug_error("gtkthemes", "Invalid file format, not loading smiley theme from '%s'\n", file); pidgin_themes_destroy_smiley_theme(theme); - - g_free(theme->name); - g_free(theme->desc); - g_free(theme->author); - g_free(theme->icon); - g_free(theme->path); - g_free(theme); - return; } @@ -240,7 +333,7 @@ GList *cnv; if (current_smiley_theme) - pidgin_themes_destroy_smiley_theme(current_smiley_theme); + pidgin_themes_destroy_smiley_theme_smileys(current_smiley_theme); current_smiley_theme = theme; for (cnv = purple_get_conversations(); cnv != NULL; cnv = cnv->next) { @@ -258,10 +351,12 @@ { GDir *dir; const gchar *file; - gchar *path; + gchar *path, *test_path; int l; + char* probedirs[3]; - char* probedirs[3]; + pidgin_smiley_themes_remove_non_existing(); + probedirs[0] = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes", NULL); probedirs[1] = g_build_filename(purple_user_dir(), "smileys", NULL); probedirs[2] = 0; @@ -269,14 +364,18 @@ dir = g_dir_open(probedirs[l], 0, NULL); if (dir) { while ((file = g_dir_read_name(dir))) { - path = g_build_filename(probedirs[l], file, "theme", NULL); + test_path = g_build_filename(probedirs[l], file, NULL); + if (g_file_test(test_path, G_FILE_TEST_IS_DIR)) { + path = g_build_filename(probedirs[l], file, "theme", NULL); - /* Here we check to see that the theme has proper syntax. - * We set the second argument to FALSE so that it doesn't load - * the theme yet. - */ - pidgin_themes_load_smiley_theme(path, FALSE); - g_free(path); + /* Here we check to see that the theme has proper syntax. + * We set the second argument to FALSE so that it doesn't load + * the theme yet. + */ + pidgin_themes_load_smiley_theme(path, FALSE); + g_free(path); + } + g_free(test_path); } g_dir_close(dir); } else if (l == 1) { @@ -284,6 +383,11 @@ } g_free(probedirs[l]); } + + if (!current_smiley_theme && smiley_themes) { + struct smiley_theme *smile = smiley_themes->data; + pidgin_themes_load_smiley_theme(smile->path, TRUE); + } } GSList *pidgin_themes_get_proto_smileys(const char *id) { @@ -333,5 +437,4 @@ struct smiley_theme *smile = smiley_themes->data; pidgin_themes_load_smiley_theme(smile->path, TRUE); } - }
--- a/pidgin/gtkthemes.h Thu Jul 05 18:15:32 2007 +0000 +++ b/pidgin/gtkthemes.h Thu Jul 05 18:21:41 2007 +0000 @@ -49,5 +49,6 @@ void pidgin_themes_smiley_themeize(GtkWidget *); void pidgin_themes_smiley_theme_probe(void); void pidgin_themes_load_smiley_theme(const char *file, gboolean load); +void pidgin_themes_remove_smiley_theme(const char *file); GSList *pidgin_themes_get_proto_smileys(const char *id); #endif /* _PIDGINDIALOGS_H_ */
--- a/pidgin/gtkutils.c Thu Jul 05 18:15:32 2007 +0000 +++ b/pidgin/gtkutils.c Thu Jul 05 18:21:41 2007 +0000 @@ -176,7 +176,7 @@ sw = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); gtk_widget_show(sw);
--- a/pidgin/plugins/spellchk.c Thu Jul 05 18:15:32 2007 +0000 +++ b/pidgin/plugins/spellchk.c Thu Jul 05 18:21:41 2007 +0000 @@ -45,9 +45,6 @@ #include <stdio.h> #include <string.h> -#ifndef _WIN32 -#include <strings.h> -#endif #include <sys/types.h> #include <sys/stat.h> @@ -1787,19 +1784,19 @@ while (buf_get_line(ibuf, &buf, &pnt, size)) { if (*buf != '#') { - if (!strncasecmp(buf, "BAD ", 4)) + if (!g_ascii_strncasecmp(buf, "BAD ", 4)) { strncpy(bad, buf + 4, 81); } - else if(!strncasecmp(buf, "CASE ", 5)) + else if(!g_ascii_strncasecmp(buf, "CASE ", 5)) { case_sensitive = *(buf+5) == '0' ? FALSE : TRUE; } - else if(!strncasecmp(buf, "COMPLETE ", 9)) + else if(!g_ascii_strncasecmp(buf, "COMPLETE ", 9)) { complete = *(buf+9) == '0' ? FALSE : TRUE; } - else if (!strncasecmp(buf, "GOOD ", 5)) + else if (!g_ascii_strncasecmp(buf, "GOOD ", 5)) { strncpy(good, buf + 5, 255);