This patch is based on glipper 0.95.1 because it was faster for me to get
a working build via the Ubuntu source package than it was to figure out
why I couldn't get the CVS version to run (which I will look into).

This patch fixes the following problems:

 - When binary data is on the primary or default clipboard, that data is
   no longer replaced with the last-known text data.  This makes it
   possible to copy/paste binary data again.  The menu displays an
   italicized "(Binary Data)" menu entry for the appropriate clipboard(s)
   that are affected, and this item vanishes when it is no longer the
   active item on the clipboard.  (TODO: make the binary-data entry
   unselectable.)

 - Switched from strcasecmp() to regular strcmp() because I want the
   glipper menu to update if I select a string with the same letters but
   in a different case.

 - When selecting text via the keyboard, an expanding string only creates
   a single entry in the menu, not several 1/2-second snapshots of the
   selection as it expands.  (Unfortunately, it still creates a new entry
   if a keyboard-created selection gets smaller.)

 - Fixed a bug where hasChanged was not getting set when the top-most
   item in the history list is unchanged, but the highlight changes (this
   would manifest as the bold entry being in the wrong spot).

 - Fixed a problem where a change to the default clipboard could put an
   entry above the primary clipboard's entry in the history list, making
   it appear that the wrong item would be pasted via middle-click.

 - When starting up, we set the last-known values for each clipboard to
   the first history item we read.  This ensures that if no clipboard
   contents are active, that we will set the contents to the first entry
   in the history.  (Fixes an inconsistency in the history menu where it
   would look like an entry was available for pasting, when it wasn't.)

 - When trimming the list, do not delete the menu entry for the default-
   clipboard item when it is at the bottom (delete the item above it).

 - The last-known-text pointers are now direct pointers to the history
   data items instead of string copies, making them easier to compare.

 - Fixed a compiler warning about an unused variable.

 - Fixed a bug in g_warning() message that should have output "path" as a
   string instead of "file" (the latter was a file handle).

 - Fixed a compiler warning about an ambiguous "else" after a nested
   "if".

..wayne..

--- glipper-0.95.1/debian/changelog	2007-04-21 13:28:31.577671087 -0700
+++ glipper-0.95.1/debian/changelog	2007-04-21 14:21:48.455850792 -0700
@@ -1,3 +1,22 @@
+glipper (0.95.1-1.1) unstable; urgency=low
+
+  * Unofficial NMU version.
+  - Fixed pasting of non-text clipboard items.
+  - Use case-sensitive string comparisons.
+  - Expanding keyboard-initiated selections don't flood the menu with
+    .5-second snapshots of the same selection.
+  - Fixed a bug where the menu might not be updated even though the
+    default clipboard entry had changed (the bold needed to move).
+  - Fixed an out-of-order problem where the primary selection's menu
+    entry could appear second in the list.
+  - On startup, load the first history value into the clipboard if
+    it does not have any data.
+  - When trimming the list, do not delete the menu entry for the
+    default-clipboard when it is at the bottom.
+  - Fixed some compiler warnings and a variable in an error message.
+
+ -- Wayne Davison <wayned@sourceforge.net>  Wed, 18 Apr 2007 22:14:18 -0800
+
 glipper (0.95.1-1) unstable; urgency=low
 
   * New upstream release.
--- glipper-0.95.1/src/main.c	2006-11-20 16:05:53.000000000 -0800
+++ glipper-0.95.1/src/main.c	2007-04-22 17:04:50.814935295 -0700
@@ -42,10 +42,15 @@ gboolean markDefault = TRUE; //whether d
 gboolean weSaveHistory = TRUE; //whether history should be saved
 char* keyComb;
 
-GtkClipboard* PrimaryCl;
-GtkClipboard* DefaultCl;
-gchar* lastPr = NULL;
-gchar* lastDf = NULL;
+typedef struct {
+    GtkClipboard* board; //The clipboard object itself
+    gchar* data;	 //A pointer to the data on the history list
+    gboolean afterClear; //TRUE when "data" is allocated after a clear
+    gboolean isBin;	 //TRUE when clipboard set to binary data w/o text
+} ClipStruct;
+
+ClipStruct PrimaryClip;
+ClipStruct DefaultClip;
 GSList* history = NULL;
 GtkWidget* historyMenu = NULL;
 GtkWidget* popupMenu;
@@ -64,13 +69,25 @@ void getClipboards()
 	  PrimaryClipboard: Marked with mouse
 	  DefaultClipboard: copied with Strg+C
 	*/
-	DefaultCl = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-	PrimaryCl = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+	DefaultClip.board = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+	PrimaryClip.board = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
 }
 
 void deleteHistory(GtkMenuItem* menuItem, gpointer user_data)
 {
+	if (!history)
+		return;
 	hasChanged = 1;
+	if (PrimaryClip.data && !PrimaryClip.afterClear)
+	{
+		PrimaryClip.data = g_strdup(PrimaryClip.data);
+		PrimaryClip.afterClear = TRUE;
+	}
+	if (DefaultClip.data && !DefaultClip.afterClear)
+	{
+		DefaultClip.data = g_strdup(DefaultClip.data);
+		DefaultClip.afterClear = TRUE;
+	}
 	g_slist_free(history);
 	history = NULL;
 	if (weSaveHistory)
@@ -87,7 +104,7 @@ GtkWidget* addHistMenuItem(gchar* item)
 	if (toolTip == NULL) toolTip = gtk_tooltips_new();
 
 	//we have to cut the string to "maxItemLength" characters:
-	GString* ellipseData = g_string_new(item);
+	GString* ellipseData = g_string_new(item ? item : "(Binary Data)");
 	if (ellipseData->len > maxItemLength)
 	{
 		ellipseData = g_string_erase(ellipseData, maxItemLength/2, 
@@ -106,17 +123,26 @@ GtkWidget* addHistMenuItem(gchar* item)
 	//The cutted string is now stored in "ellipseData"
 	GtkWidget* MenuItem = gtk_menu_item_new_with_label(ellipseData->str);
 	//if this string belongs to the default Clipboard, we tag it (presumed markDefault is true):
-	if (markDefault && useDefault && usePrimary)
-		if ((lastDf!=NULL)&&(g_ascii_strcasecmp(lastDf, item)==0))
-		{
-			GtkLabel* label = (GtkLabel*)gtk_bin_get_child((GtkBin*)MenuItem); 
-			gchar* temp = g_strdup_printf("<b>%s</b>", ellipseData->str);
-			gtk_label_set_markup(label, temp);
-			g_free(temp);
-
-			//Add description tooltip to bold entry
-			gtk_tooltips_set_tip(toolTip, MenuItem, _("This entry was copied with ctrl+c.\nIt can be pasted with ctrl+v."), "Glipper");
-		}
+	if (markDefault && useDefault && usePrimary
+	 && ((item==NULL && DefaultClip.isBin)
+	  || (item!=NULL && !DefaultClip.isBin && DefaultClip.data == item)))
+	{
+		GtkLabel* label = (GtkLabel*)gtk_bin_get_child((GtkBin*)MenuItem); 
+		gchar* fmt = item ? "<b>%s</b>" : "<b><i>%s</i></b>";
+		gchar* temp = g_strdup_printf(fmt, ellipseData->str);
+		gtk_label_set_markup(label, temp);
+		g_free(temp);
+
+		//Add description tooltip to bold entry
+		gtk_tooltips_set_tip(toolTip, MenuItem, _("This entry was copied with ctrl+c.\nIt can be pasted with ctrl+v."), "Glipper");
+	}
+	else if (!item)
+	{
+		GtkLabel* label = (GtkLabel*)gtk_bin_get_child((GtkBin*)MenuItem); 
+		gchar* temp = g_strdup_printf("<i>%s</i>", ellipseData->str);
+		gtk_label_set_markup(label, temp);
+		g_free(temp);
+	}
 	g_string_free(ellipseData, TRUE);
 	gtk_menu_append(historyMenu, MenuItem);
 	g_signal_connect(G_OBJECT(MenuItem), "activate", 
@@ -180,17 +206,27 @@ void createHistMenu()
 		GSList* temp = history;
 
 		//We add the first item manually
-		GtkWidget* firstItem = addHistMenuItem(temp->data);
-		//addHistMenuItem(temp->data);
+		GtkWidget* firstItem;
+		if (PrimaryClip.isBin)
+			firstItem = addHistMenuItem(NULL);
+		else
+		{
+			firstItem = addHistMenuItem(temp->data);
+			temp = g_slist_next(temp);
+		}
 
 		//Add description tooltip to first item
 		gtk_tooltips_set_tip(toolTip, firstItem, _("This is the last element to be copied.\nIt can be pasted with the middle mouse button."), "Glipper");
 
-		if (temp->next != NULL)
+		if (temp != NULL || (DefaultClip.isBin && !PrimaryClip.isBin))
 		{
 			gtk_menu_append((GtkMenu*)historyMenu, gtk_separator_menu_item_new());
-			while ((temp = g_slist_next(temp)) != NULL)
+			if (DefaultClip.isBin && !PrimaryClip.isBin)
+				addHistMenuItem(NULL);
+			while (temp != NULL) {
 				addHistMenuItem(temp->data);
+				temp = g_slist_next(temp);
+			}
 		}
 	}
 
@@ -205,71 +241,142 @@ void createHistMenu()
 	gtk_widget_show_all(historyMenu);
 }
 
-void insertInHistory(gchar* content)
+//Delete the bottom element in the menu, or the 2nd-to-bottom if the
+//DefaultClipboard item is at the bottom.
+void deleteOldElement(int limit)
 {
-	if ((history == NULL) ||
-	  (g_ascii_strcasecmp(content, (gchar*)history->data)!=0))
+	if (limit < 2)
+		limit = 2;
+
+	GSList* grandparentElement = g_slist_nth(history, limit-2);
+	if (grandparentElement == NULL)
+		return;
+
+	GSList* parentElement = grandparentElement->next;
+	if (parentElement == NULL || parentElement->next == NULL)
+		return;
+
+	GSList* cur;
+	GSList* prior = parentElement;
+	for (cur = prior->next; cur != NULL; prior = cur, cur = prior->next) {
+		if (cur->data == DefaultClip.data)
+		{
+			//Unlink DefaultClip's node from its current spot
+			prior->next = cur->next;
+			//Move it to the last spot in the retained list
+			grandparentElement->next = cur;
+			cur->next = parentElement;
+			parentElement = cur;
+			break;
+		}
+	}
+
+	g_slist_free(parentElement->next);
+	parentElement->next = NULL;
+}
+
+void insertInHistory(ClipStruct *clip)
+{
+	gchar* content = clip->data;
+
+	if (history == NULL || strcmp(content, history->data)!=0)
 	{
 		//g_print(content);
-		history = g_slist_prepend(history, g_strdup(content));
-		hasChanged = 1;
-		//We look whether the same entry still exists in the history and delete it:
+		history = g_slist_prepend(history, content);
 		GSList* temp = history;
+		//If the primary selection is growing, replace the previous
+		//substring (as long as the string is not needed by the
+		//default clipboard).  If the newly-grown string duplicates
+		//a later entry, we'll also remove that below.
+		if (clip==&PrimaryClip && temp->next!=NULL && temp->next->data!=NULL
+		 && DefaultClip.data != temp->next->data) {
+			int old_len = strlen(temp->next->data);
+			int new_len = strlen(content);
+			if (new_len > old_len
+			 && (strncmp(temp->next->data, content, old_len) == 0
+			  || strcmp(temp->next->data, content+new_len-old_len) == 0))
+			{
+				GSList* dummy = temp->next;
+				if (DefaultClip.data == dummy->data)
+					DefaultClip.data = content;
+				temp->next = temp->next->next;
+				g_slist_free_1(dummy);
+			}
+		}
+		//We look whether the same entry still exists in the history and delete it:
 		while ((temp->next!=NULL)&&(temp->next->data!=NULL)&&
-		  (g_ascii_strcasecmp((gchar*)temp->next->data, content)!=0))
+		  (strcmp(temp->next->data, content)!=0))
 		  	temp = temp->next;
 		//If the same entry was found:
 		if (temp->next!=NULL)
 		{
 			GSList* dummy = temp->next;
+			if (DefaultClip.data == dummy->data)
+				DefaultClip.data = content;
 			temp->next = temp->next->next;
 			g_slist_free_1(dummy);
 		}
 		//We shorten the history if it gets longer than "maxElements":
-		GSList* deleteElement = g_slist_nth(history, maxElements-1);
-		if (deleteElement != NULL)
-		{
-			g_slist_free(deleteElement->next);
-			deleteElement->next = NULL;
-		}
+		deleteOldElement(maxElements);
 		if (weSaveHistory)
 			saveHistory();
 	}
+	else
+	{
+		g_free(content);
+		clip->data = history->data;
+	}
 }
 
-void processContent(gchar* newContent, gchar** lastContent, GtkClipboard* Clipboard)
+void processContent(ClipStruct *clip)
 {
+	gchar* newContent = gtk_clipboard_wait_for_text(clip->board);
 	if (newContent == NULL)
 	{
-		if (*lastContent != NULL)
-			gtk_clipboard_set_text(Clipboard, *lastContent, -1);
-	} else {
-		if ((*lastContent==NULL)||
-		  ((*lastContent!=NULL)&&(g_ascii_strcasecmp(newContent, *lastContent)!=0)))
-		{
-			insertInHistory(newContent);
-			if (*lastContent!=NULL)
-				g_free(*lastContent);
-			*lastContent = newContent;
+		gint count;
+		GdkAtom *targets;
+		int ok = gtk_clipboard_wait_for_targets(clip->board, &targets, &count);
+		g_free(targets);
+		if (ok)
+		{
+			hasChanged |= !clip->isBin;
+			clip->isBin = TRUE;
+		}
+		else if (clip->data != NULL)
+		{
+			gtk_clipboard_set_text(clip->board, clip->data, -1);
+			hasChanged = 1;
+			clip->isBin = FALSE;
 		}
-		else
-			g_free(newContent);
+	}
+	else if (clip->data==NULL || strcmp(newContent, clip->data)!=0
+	 || (clip == &PrimaryClip && history != NULL && strcmp(newContent, history->data)!=0))
+	{
+		if (clip->afterClear)
+		{
+			clip->afterClear = FALSE;
+			g_free(clip->data);
+		}
+		clip->data = newContent;
+		insertInHistory(clip);
+		hasChanged = 1;
+		clip->isBin = FALSE;
+	}
+	else
+	{
+		g_free(newContent);
+		hasChanged |= !clip->isBin;
+		clip->isBin = FALSE;
 	}
 }
 
 gboolean checkClipboard(gpointer data)
 {
 	g_source_remove(mainTimeout);
-	if (usePrimary)
-	{
-		gchar* newContentPr = gtk_clipboard_wait_for_text(PrimaryCl);
-		processContent(newContentPr, &lastPr, PrimaryCl);
-	}
 	if (useDefault)
-	{
-		gchar* newContentCl = gtk_clipboard_wait_for_text(DefaultCl);
-		processContent(newContentCl, &lastDf, DefaultCl);
-	}
+		processContent(&DefaultClip);
+	if (usePrimary)
+		processContent(&PrimaryClip);
 	mainTimeout = g_timeout_add(500, checkClipboard, NULL);
 	return 1;
 }
@@ -297,9 +404,9 @@ void TrayIconClicked(GtkWidget* widget, 
 void historyEntryActivate(GtkMenuItem* menuItem, gpointer user_data)
 {
 	if (usePrimary)
-		gtk_clipboard_set_text(PrimaryCl, (gchar*)user_data, -1);
+		gtk_clipboard_set_text(PrimaryClip.board, (gchar*)user_data, -1);
 	if (useDefault)
-		gtk_clipboard_set_text(DefaultCl, (gchar*)user_data, -1);
+		gtk_clipboard_set_text(DefaultClip.board, (gchar*)user_data, -1);
 	checkClipboard(NULL);
 	hasChanged = 1;
 }
@@ -331,7 +438,7 @@ void showHelp(gpointer data)
 
 void createTrayIcon()
 {
-	GdkPixbuf* pixbuf, *scaled;
+	GdkPixbuf* pixbuf;
 	GtkWidget* tray_icon_image;
 	GError* pix_error = NULL;
 
@@ -446,14 +553,16 @@ FILE* writeGlipperFile(char* filename)
 	gchar* path = g_build_filename(directory, filename, NULL);
 	FILE* file = fopen(path, "w");
 	if (file == NULL)
+	{
 		if (mkdir(directory, S_IRWXU)==0)  //Trys to create the folder if open failes
 		{
 			file = fopen(path, "w");
 			if (file == NULL)
-				g_warning("Can't open or create file %s!", file);
+				g_warning("Can't open or create file %s!", path);
 		}
 		else
 			g_warning ("Can't create directory '.glipper' in user's home directory!");
+	}
 	g_free(directory);
 	g_free(path);
 	return file;
@@ -495,6 +604,10 @@ void readHistory()
 			fread(data, size, 1, histFile);
 			data[size] = '\0';
 			history = g_slist_append(history, data);
+			if (!PrimaryClip.data)
+				PrimaryClip.data = data;
+			if (!DefaultClip.data)
+				DefaultClip.data = data;
 		}
 	}
 }
@@ -532,12 +645,7 @@ void readPreferences()
 void applyPreferences()
 {
 	hasChanged=1; 
-	GSList* deleteElement = g_slist_nth(history, maxElements-1);
-	if (deleteElement != NULL)
-	{
-		g_slist_free(deleteElement->next);
-		deleteElement->next = NULL;
-	}
+	deleteOldElement(maxElements);
 	keybinder_bind(keyComb, keyhandler, NULL);
 	gtk_tooltips_set_tip(toolTip, eventbox, g_strdup_printf(_("Glipper (%s)\nClipboardmanager"),
 		keyComb), "Glipper");
