Bug 311340 - "Clipboard data is lost on exit (Should implement the freedesktop.org specification for clipboard management)" [r=karlt]

This commit is contained in:
Martin Stránský 2009-11-25 22:08:09 -06:00
parent 81d6db5548
commit 2150aa916b
2 changed files with 118 additions and 101 deletions

View File

@ -46,6 +46,7 @@
#include "nsIServiceManager.h"
#include "nsImageToPixbuf.h"
#include "nsStringStream.h"
#include "nsIObserverService.h"
#include "imgIContainer.h"
@ -62,19 +63,18 @@
#include <poll.h>
#endif
// Callback when someone asks us for the selection
// Callback when someone asks us for the data
void
invisible_selection_get_cb (GtkWidget *aWidget,
GtkSelectionData *aSelectionData,
guint aTime,
guint aInfo,
nsClipboard *aClipboard);
gboolean
selection_clear_event_cb (GtkWidget *aWidget,
GdkEventSelection *aEvent,
nsClipboard *aClipboard);
clipboard_get_cb(GtkClipboard *aGtkClipboard,
GtkSelectionData *aSelectionData,
guint info,
gpointer user_data);
// Callback when someone asks us to clear a clipboard
void
clipboard_clear_cb(GtkClipboard *aGtkClipboard,
gpointer user_data);
static void
ConvertHTMLtoUCS2 (guchar *data,
PRInt32 dataLength,
@ -121,33 +121,46 @@ clipboard_text_received(GtkClipboard *clipboard,
gpointer data);
nsClipboard::nsClipboard()
{
mWidget = nsnull;
{
}
nsClipboard::~nsClipboard()
{
if (mWidget)
gtk_widget_destroy(mWidget);
}
NS_IMPL_ISUPPORTS1(nsClipboard, nsIClipboard)
nsresult
nsClipboard::Init(void)
{
nsresult rv;
nsCOMPtr<nsIObserverService> os
(do_GetService("@mozilla.org/observer-service;1", &rv));
NS_ENSURE_SUCCESS(rv, rv);
os->AddObserver(this, "quit-application", PR_FALSE);
return NS_OK;
}
NS_IMETHODIMP
nsClipboard::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
{
mWidget = gtk_invisible_new();
if (!mWidget)
return NS_ERROR_FAILURE;
g_signal_connect(G_OBJECT(mWidget), "selection_get",
G_CALLBACK(invisible_selection_get_cb), this);
g_signal_connect(G_OBJECT(mWidget), "selection_clear_event",
G_CALLBACK(selection_clear_event_cb), this);
// XXX make sure to set up the selection_clear event
if (strcmp(aTopic, "quit-application") == 0) {
// application is going to quit, save clipboard content
Store();
}
return NS_OK;
}
nsresult
nsClipboard::Store(void)
{
// Ask the clipboard manager to store the current clipboard content
if (mGlobalTransferable) {
GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
gtk_clipboard_store(clipboard);
}
return NS_OK;
}
@ -176,24 +189,8 @@ nsClipboard::SetData(nsITransferable *aTransferable,
// Clear out the clipboard in order to set the new data
EmptyClipboard(aWhichClipboard);
if (aWhichClipboard == kSelectionClipboard) {
mSelectionOwner = aOwner;
mSelectionTransferable = aTransferable;
}
else {
mGlobalOwner = aOwner;
mGlobalTransferable = aTransferable;
}
// Which selection are we about to claim, CLIPBOARD or PRIMARY?
GdkAtom selectionAtom = GetSelectionAtom(aWhichClipboard);
// Make ourselves the owner. If we fail to, return.
if (!gtk_selection_owner_set(mWidget, selectionAtom, GDK_CURRENT_TIME))
return NS_ERROR_FAILURE;
// Clear the old selection target list.
gtk_selection_clear_targets(mWidget, selectionAtom);
// List of suported targets
GtkTargetList *list = gtk_target_list_new(NULL, 0);
// Get the types of supported flavors
nsCOMPtr<nsISupportsArray> flavors;
@ -203,7 +200,7 @@ nsClipboard::SetData(nsITransferable *aTransferable,
return NS_ERROR_FAILURE;
// Add all the flavors to this widget's supported type.
gint nImageTargets = 0;
PRBool imagesAdded = PR_FALSE;
PRUint32 count;
flavors->Count(&count);
for (PRUint32 i=0; i < count; i++) {
@ -218,13 +215,10 @@ nsClipboard::SetData(nsITransferable *aTransferable,
// special case text/unicode since we can handle all of
// the string types
if (!strcmp(flavorStr, kUnicodeMime)) {
AddTarget(gdk_atom_intern("UTF8_STRING", FALSE),
selectionAtom);
AddTarget(gdk_atom_intern("COMPOUND_TEXT", FALSE),
selectionAtom);
AddTarget(gdk_atom_intern("TEXT", FALSE), selectionAtom);
AddTarget(GDK_SELECTION_TYPE_STRING, selectionAtom);
// next loop iteration
gtk_target_list_add(list, gdk_atom_intern("UTF8_STRING", FALSE), 0, 0);
gtk_target_list_add(list, gdk_atom_intern("COMPOUND_TEXT", FALSE), 0, 0);
gtk_target_list_add(list, gdk_atom_intern("TEXT", FALSE), 0, 0);
gtk_target_list_add(list, GDK_SELECTION_TYPE_STRING, 0, 0);
continue;
}
@ -233,26 +227,53 @@ nsClipboard::SetData(nsITransferable *aTransferable,
flavorStr.EqualsLiteral(kJPEGImageMime) ||
flavorStr.EqualsLiteral(kGIFImageMime)) {
// don't bother adding image targets twice
if (!nImageTargets) {
if (!imagesAdded) {
// accept any writable image type
GtkTargetList *list = gtk_target_list_new(NULL, 0);
gtk_target_list_add_image_targets(list, 0, TRUE);
GtkTargetEntry *targets = gtk_target_table_new_from_list(list, &nImageTargets);
gtk_selection_add_targets(mWidget, selectionAtom, targets, nImageTargets);
gtk_target_table_free(targets, nImageTargets);
gtk_target_list_unref(list);
imagesAdded = PR_TRUE;
}
// next loop iteration
continue;
}
// Add this to our list of valid targets
GdkAtom atom = gdk_atom_intern(flavorStr, FALSE);
AddTarget(atom, selectionAtom);
gtk_target_list_add(list, atom, 0, 0);
}
}
// Get GTK clipboard (CLIPBOARD or PRIMARY)
GtkClipboard *gtkClipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
gint numTargets;
GtkTargetEntry *gtkTargets = gtk_target_table_new_from_list(list, &numTargets);
// Set getcallback and request to store data after an application exit
if (gtk_clipboard_set_with_data(gtkClipboard, gtkTargets, numTargets,
clipboard_get_cb, clipboard_clear_cb, this))
{
// We managed to set-up the clipboard so update internal state
// We have to set it now because gtk_clipboard_set_with_data() calls clipboard_clear_cb()
// which reset our internal state
if (aWhichClipboard == kSelectionClipboard) {
mSelectionOwner = aOwner;
mSelectionTransferable = aTransferable;
}
else {
mGlobalOwner = aOwner;
mGlobalTransferable = aTransferable;
gtk_clipboard_set_can_store(gtkClipboard, gtkTargets, numTargets);
}
return NS_OK;
rv = NS_OK;
}
else {
rv = NS_ERROR_FAILURE;
}
gtk_target_table_free(gtkTargets, numTargets);
gtk_target_list_unref(list);
return rv;
}
NS_IMETHODIMP
@ -489,15 +510,8 @@ nsClipboard::GetTransferable(PRInt32 aWhichClipboard)
}
void
nsClipboard::AddTarget(GdkAtom aName, GdkAtom aClipboard)
{
gtk_selection_add_target(mWidget, aClipboard, aName, 0);
}
void
nsClipboard::SelectionGetEvent (GtkWidget *aWidget,
GtkSelectionData *aSelectionData,
guint aTime)
nsClipboard::SelectionGetEvent(GtkClipboard *aClipboard,
GtkSelectionData *aSelectionData)
{
// Someone has asked us to hand them something. The first thing
// that we want to do is see if that something includes text. If
@ -515,7 +529,15 @@ nsClipboard::SelectionGetEvent (GtkWidget *aWidget,
return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
nsCOMPtr<nsITransferable> trans = GetTransferable(whichClipboard);
if (!trans) {
// We have nothing to serve
#ifdef DEBUG_CLIPBOARD
printf("nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n",
whichClipboard == kSelectionClipboard ? "Selection" : "Global");
#endif
return;
}
nsresult rv;
nsCOMPtr<nsISupports> item;
PRUint32 len;
@ -632,15 +654,14 @@ nsClipboard::SelectionGetEvent (GtkWidget *aWidget,
}
void
nsClipboard::SelectionClearEvent (GtkWidget *aWidget,
GdkEventSelection *aEvent)
nsClipboard::SelectionClearEvent(GtkClipboard *aGtkClipboard)
{
PRInt32 whichClipboard;
// which clipboard?
if (aEvent->selection == GDK_SELECTION_PRIMARY)
if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_PRIMARY))
whichClipboard = kSelectionClipboard;
else if (aEvent->selection == GDK_SELECTION_CLIPBOARD)
else if (aGtkClipboard == gtk_clipboard_get(GDK_SELECTION_CLIPBOARD))
whichClipboard = kGlobalClipboard;
else
return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
@ -649,22 +670,21 @@ nsClipboard::SelectionClearEvent (GtkWidget *aWidget,
}
void
invisible_selection_get_cb (GtkWidget *aWidget,
GtkSelectionData *aSelectionData,
guint aTime,
guint aInfo,
nsClipboard *aClipboard)
clipboard_get_cb(GtkClipboard *aGtkClipboard,
GtkSelectionData *aSelectionData,
guint info,
gpointer user_data)
{
aClipboard->SelectionGetEvent(aWidget, aSelectionData, aTime);
nsClipboard *aClipboard = static_cast<nsClipboard *>(user_data);
aClipboard->SelectionGetEvent(aGtkClipboard, aSelectionData);
}
gboolean
selection_clear_event_cb (GtkWidget *aWidget,
GdkEventSelection *aEvent,
nsClipboard *aClipboard)
void
clipboard_clear_cb(GtkClipboard *aGtkClipboard,
gpointer user_data)
{
aClipboard->SelectionClearEvent(aWidget, aEvent);
return TRUE;
nsClipboard *aClipboard = static_cast<nsClipboard *>(user_data);
aClipboard->SelectionClearEvent(aGtkClipboard);
}
/*

View File

@ -44,7 +44,8 @@
#include "nsAutoPtr.h"
#include <gtk/gtk.h>
class nsClipboard : public nsIClipboard
class nsClipboard : public nsIClipboard,
public nsIObserver
{
public:
nsClipboard();
@ -53,33 +54,29 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSICLIPBOARD
NS_DECL_NSIOBSERVER
// Make sure we are initialized, called from the factory
// constructor
nsresult Init (void);
// Someone requested the selection from the hidden widget
void SelectionGetEvent (GtkWidget *aWidget,
GtkSelectionData *aSelectionData,
guint aTime);
void SelectionClearEvent (GtkWidget *aWidget,
GdkEventSelection *aEvent);
nsresult Init (void);
// Someone requested the selection
void SelectionGetEvent (GtkClipboard *aGtkClipboard,
GtkSelectionData *aSelectionData);
void SelectionClearEvent (GtkClipboard *aGtkClipboard);
private:
// Utility methods
static GdkAtom GetSelectionAtom (PRInt32 aWhichClipboard);
static GtkSelectionData *GetTargets (GdkAtom aWhichClipboard);
// Save global clipboard content to gtk
nsresult Store (void);
// Get our hands on the correct transferable, given a specific
// clipboard
nsITransferable *GetTransferable (PRInt32 aWhichClipboard);
// Add a target type to the hidden widget
void AddTarget (GdkAtom aName,
GdkAtom aClipboard);
// The hidden widget where we do all of our operations
GtkWidget *mWidget;
// Hang on to our owners and transferables so we can transfer data
// when asked.
nsCOMPtr<nsIClipboardOwner> mSelectionOwner;