changeset 2385:bdc74764245c

[gaim-migrate @ 2398] Sean Egan's tab-completion patch. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Fri, 28 Sep 2001 12:15:54 +0000
parents 05034cd0402b
children 437ae28a1172
files ChangeLog TODO src/buddy_chat.c src/conversation.c src/convo.h src/gaim.h src/prefs.c
diffstat 7 files changed, 200 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri Sep 28 09:33:38 2001 +0000
+++ b/ChangeLog	Fri Sep 28 12:15:54 2001 +0000
@@ -8,6 +8,9 @@
 	  SIGNALS)
 	* Some GtkIMHtml improvements
 	* Various bugfixes
+	* Nick Highlighting in chat
+	* Tab-completion for nicks in chat (thanks to Sean Egan)
+	* Large internal reworkings
 
 version 0.44 (09/20/2001):
 	* More sane scaling of buddy icons (intelligently scale to
--- a/TODO	Fri Sep 28 09:33:38 2001 +0000
+++ b/TODO	Fri Sep 28 12:15:54 2001 +0000
@@ -2,22 +2,18 @@
 	GPG Encryption of messages
 	Have plugin_event use varargs instead of void*
 	Separate core functions from UI stuff.
-		about.c, applet.[ch], away.c, conversation.c, convo.h, multi.c,
-		gtk*, prefs.c, prpl.c, sound.c, ticker.c
+		about.c, applet.[ch], away.c, buddy.c, conversation.c, convo.h,
+		dialogs.c, multi.c, gtk*, prefs.c, prpl.c, sound.c, ticker.c
 			little to no work (mostly GTK)
 
-		gaimrc.c, html.c, proxy.[ch], prpl.h, server.c, util.c
+		gaimrc.c, html.c, list.c, proxy.[ch], prpl.h, server.c, util.c
 			little to no work (mostly CORE)
 
 		aim.c - need to redo main() completely
 			- current main should be moved mostly to core
 			- GTK main should connect to core
 		browser.c - let the UI deal with this? little to no work.
-		buddy.c - need to split into buddy.c and list.c
-			- buddy.c handles add/remove/rename/alias buddies
-			- list.c handles drawing the buddy list for GTK
 		buddy_chat.c - need to move out non-GTK functions. some work.
-		dialogs.c - need to move import/export out of here and into buddy.c
 		gaim.h - need to separate into core.h and ui.h.
 		idle.c - Need to figure out how to report idle times when there's
 			more than one UI
--- a/src/buddy_chat.c	Fri Sep 28 09:33:38 2001 +0000
+++ b/src/buddy_chat.c	Fri Sep 28 12:15:54 2001 +0000
@@ -440,6 +440,163 @@
 	gtk_widget_show(invite);
 }
 
+void tab_complete(struct conversation *c)
+{
+	int pos = GTK_EDITABLE(c->entry)->current_pos;
+	int start = pos;
+	int most_matched = -1;
+	char *entered, *partial = NULL;
+	char *text;
+	GList *matches = NULL;
+	GList *nicks = c->in_room;
+
+	/* if there's nothing there just return */
+	if (!start)
+		return;
+	
+	text = gtk_editable_get_chars(GTK_EDITABLE(c->entry), 0, pos);
+
+	/* if we're at the end of ": " we need to move back 2 spaces */
+	if (start >= 2 && text[start - 1] == ' ' && text[start - 2] == ':')
+		start -= 2;
+	
+	/* find the start of the word that we're tabbing */
+	while (start > 0 && text[start - 1] != ' ')
+		start--;
+
+	entered = text + start;
+	if (chat_options & OPT_CHAT_OLD_STYLE_TAB) {
+		if (strlen(entered) >= 2 && !strncmp(": ", entered + strlen(entered) - 2, 2))
+			entered[strlen(entered) - 2] = 0;
+	}
+		
+	if (!strlen(entered)) {
+		g_free(text);
+		return;
+	}
+
+	debug_printf("checking tab-completion for %s\n", entered);
+
+	while (nicks) {
+		char *nick = nicks->data;
+		/* this checks to see if the current nick could be a completion */
+		if (g_strncasecmp(nick, entered, strlen(entered))) {
+			if (nick[0] != '+' && nick[0] != '@') {
+				nicks = nicks->next;
+				continue;
+			}
+			if (g_strncasecmp(nick + 1, entered, strlen(entered))) {
+				if (nick[0] != '@' && nick[1] != '+') {
+					nicks = nicks->next;
+					continue;
+				}
+				if (g_strncasecmp(nick + 2, entered, strlen(entered))) {
+					nicks = nicks->next;
+					continue;
+				}
+				else
+					nick += 2;
+			} else
+				nick++;
+		}
+		/* if we're here, it's a possible completion */
+		debug_printf("possible completion: %s\n", nick);
+
+		/* if we're doing old-style, just fill in the completion */
+		if (chat_options & OPT_CHAT_OLD_STYLE_TAB) {
+		        gtk_editable_delete_text(GTK_EDITABLE(c->entry), start, pos);
+			if (strlen(nick) == strlen(entered)) {
+				nicks = nicks->next ? nicks->next : c->in_room;
+				nick = nicks->data;
+				if (*nick == '@')
+					nick++;
+				if (*nick == '+')
+					nick++;
+			}
+
+			if (start == 0) {
+				char *tmp = g_strdup_printf("%s: ", nick);
+				int t = start;
+				gtk_editable_insert_text(GTK_EDITABLE(c->entry), tmp, strlen(tmp), &start);
+				if (t == start) {
+					t = start + strlen(tmp);
+					gtk_editable_set_position(GTK_EDITABLE(c->entry), t);
+				}
+				g_free(tmp);
+			} else {
+				int t = start;
+				gtk_editable_insert_text(GTK_EDITABLE(c->entry), nick, strlen(nick), &start);
+				if (t == start) {
+					t = start + strlen(nick);
+					gtk_editable_set_position(GTK_EDITABLE(c->entry), t);
+				}
+			}
+			g_free(text);
+			return;
+		}
+
+		/* we're only here if we're doing new style */
+		if (most_matched == -1) {
+			/* this will only get called once, since from now on most_matched is >= 0 */
+			most_matched = strlen(nick);
+			partial = g_strdup(nick);
+		} else if (most_matched) {
+			while (g_strncasecmp(nick, partial, most_matched))
+				most_matched--;
+			partial[most_matched] = 0;
+		}
+		matches = g_list_append(matches, nick);
+
+		nicks = nicks->next;
+	}
+	/* we're only here if we're doing new style */
+	
+	/* if there weren't any matches, return */
+	if (!matches) {
+		/* if matches isn't set partials won't be either */
+		g_free(text);
+		return;
+	}
+	
+	gtk_editable_delete_text(GTK_EDITABLE(c->entry), start, pos);
+	if (!matches->next) {
+		/* there was only one match. fill it in. */
+		if (start == 0) {
+			char *tmp = g_strdup_printf("%s: ", (char *)matches->data);
+			int t = start;
+			gtk_editable_insert_text(GTK_EDITABLE(c->entry), tmp, strlen(tmp), &start);
+			if (t == start) {
+				t = start + strlen(tmp);
+				gtk_editable_set_position(GTK_EDITABLE(c->entry), t);
+			}
+			g_free(tmp);
+		} else {
+			gtk_editable_insert_text(GTK_EDITABLE(c->entry), matches->data, strlen(matches->data), &start);
+		}
+		matches = g_list_remove(matches, matches->data);
+	} else {
+		/* there were lots of matches, fill in as much as possible and display all of them */
+		char *addthis = g_malloc0(1);
+		int t = start;
+		while (matches) {
+			char *tmp = addthis;
+			addthis = g_strconcat(tmp, matches->data, " ", NULL);
+			g_free(tmp);
+			matches = g_list_remove(matches, matches->data);
+		}
+		write_to_conv(c, addthis, WFLAG_NOLOG, NULL, time(NULL));
+		gtk_editable_insert_text(GTK_EDITABLE(c->entry), partial, strlen(partial), &start);
+		if (t == start) {
+			t = start + strlen(partial);
+			gtk_editable_set_position(GTK_EDITABLE(c->entry), t);
+		}
+		g_free(addthis);
+	}
+	
+	g_free(text);
+	g_free(partial);
+}
+
 gboolean meify(char *message)
 {
 	/* read /me-ify : if the message (post-HTML) starts with /me, remove
--- a/src/conversation.c	Fri Sep 28 09:33:38 2001 +0000
+++ b/src/conversation.c	Fri Sep 28 12:15:54 2001 +0000
@@ -755,6 +755,9 @@
 				gtk_signal_emit_stop_by_name(GTK_OBJECT(entry), "key_press_event");
 			}
 		}
+	} else if ((event->keyval == GDK_Tab) && c->is_chat && (chat_options & OPT_CHAT_TAB_COMPLETE)) {
+	        tab_complete(c);
+		gtk_signal_emit_stop_by_name(GTK_OBJECT(entry), "key_press_event");
 	} else if (((!c->is_chat && (im_options & OPT_IM_ONE_WINDOW)) ||
 		    (c->is_chat && (chat_options & OPT_CHAT_ONE_WINDOW))) &&
 		   (event->state & GDK_MOD1_MASK) && isdigit(event->keyval) && (event->keyval > '0')) {
@@ -1396,7 +1399,7 @@
 			c->history = g_string_append(c->history, "<BR>\n");
 		}
 
-		if ((logging_options & OPT_LOG_ALL) || find_log_info(c->name)) {
+		if (!(flags & WFLAG_NOLOG) && ((logging_options & OPT_LOG_ALL) || find_log_info(c->name))) {
 			char *t1;
 			char nm[256];
 
@@ -1422,7 +1425,9 @@
 				g_free(t1);
 			}
 		}
-
+	} else if (flags & WFLAG_NOLOG) {
+		g_snprintf(buf, BUF_LONG, "<B><FONT COLOR=\"#777777\">%s</FONT></B><BR>", what);
+		gtk_imhtml_append_text(GTK_IMHTML(c->text), buf, 0);
 	} else {
 		if (flags & WFLAG_WHISPER) {
 			/* if we're whispering, it's not an autoresponse */
@@ -1499,7 +1504,7 @@
 			g_free(t2);
 		}
 
-		if ((logging_options & OPT_LOG_ALL) || find_log_info(c->name)) {
+		if (!(flags & WFLAG_NOLOG) && ((logging_options & OPT_LOG_ALL) || find_log_info(c->name))) {
 			char *t1, *t2;
 			char *nm = g_malloc(256);
 			if (c->is_chat)
--- a/src/convo.h	Fri Sep 28 09:33:38 2001 +0000
+++ b/src/convo.h	Fri Sep 28 12:15:54 2001 +0000
@@ -41,6 +41,7 @@
 extern void ignore_callback(GtkWidget *, struct conversation *);
 extern void whisper_callback(GtkWidget *, struct conversation *);
 extern void invite_callback(GtkWidget *, struct conversation *);
+extern void tab_complete(struct conversation *c);
 
 /* now IM */
 extern void warn_callback(GtkWidget *, struct conversation *);
--- a/src/gaim.h	Fri Sep 28 09:33:38 2001 +0000
+++ b/src/gaim.h	Fri Sep 28 12:15:54 2001 +0000
@@ -70,6 +70,7 @@
 #define WFLAG_FILERECV	0x10
 #define WFLAG_SYSTEM	0x20
 #define WFLAG_NICK	0x40
+#define WFLAG_NOLOG	0x80
 
 #define AUTO_RESPONSE "&lt;AUTO-REPLY&gt; : "
 
@@ -528,6 +529,8 @@
 #define OPT_CHAT_POPUP			0x00000010
 #define OPT_CHAT_SIDE_TAB		0x00000020
 #define OPT_CHAT_BR_TAB			0x00000040
+#define OPT_CHAT_TAB_COMPLETE		0x00000080
+#define OPT_CHAT_OLD_STYLE_TAB		0x00000100
 
 extern guint font_options;
 #define OPT_FONT_BOLD			0x00000001
--- a/src/prefs.c	Fri Sep 28 09:33:38 2001 +0000
+++ b/src/prefs.c	Fri Sep 28 12:15:54 2001 +0000
@@ -984,6 +984,8 @@
 	GtkWidget *button;
 	GtkWidget *hbox2;
 	GtkWidget *vbox3;
+	GtkWidget *tab;
+	GtkWidget *old;
 
 	parent = prefdialog->parent;
 	gtk_widget_destroy(prefdialog);
@@ -1086,6 +1088,29 @@
 	opt = tab_radio(_("Right"), OPT_CHAT_SIDE_TAB | OPT_CHAT_BR_TAB | 1, vbox3, opt);
 	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(toggle_sensitive), opt);
 
+	frame = gtk_frame_new(_("Tab Completion"));
+	gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
+	gtk_widget_show(frame);
+
+	hbox = gtk_hbox_new(FALSE, 5);
+	gtk_container_add(GTK_CONTAINER(frame), hbox);
+	gtk_widget_show(hbox);
+
+	vbox = gtk_vbox_new(FALSE, 5);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 5);
+	gtk_widget_show(vbox);
+
+	tab = gaim_button(_("Tab-Complete Nicks"), &chat_options, OPT_CHAT_TAB_COMPLETE, vbox);
+
+	vbox = gtk_vbox_new(FALSE, 5);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 5);
+	gtk_widget_show(vbox);
+
+	old = gaim_button(_("Old-Style Tab Completion"), &chat_options, OPT_CHAT_OLD_STYLE_TAB, vbox);
+	if (!(chat_options & OPT_CHAT_TAB_COMPLETE))
+		gtk_widget_set_sensitive(GTK_WIDGET(old), FALSE);
+	gtk_signal_connect(GTK_OBJECT(tab), "clicked", GTK_SIGNAL_FUNC(toggle_sensitive), old);
+
 	gtk_widget_show(prefdialog);
 }