Improvements in handling of the X selection, and some explanatory comments

This commit is contained in:
akkana%netscape.com 1999-03-02 18:08:49 +00:00
parent 6829c9dcd5
commit 63f1f1e77a
2 changed files with 123 additions and 128 deletions

View File

@ -16,6 +16,39 @@
* Reserved. * Reserved.
*/ */
//
// gtk/nsSelectionMgr: the class which handles X selection for DoCopy.
//
// Xheads: One thing we might want to change later:
// Currently it's necessary to do edit->copy (or keyboard equivalent)
// to get selection info to this class.
// The PresShell does a DoCopy, which calls the parser and creates a
// content stream with the contents of the then-current selection.
// X users may be more comfortable with having this happen every time
// the selection is changed. This could be done in two ways:
//
// 1. Have the mozilla selection class (nsRangeList) call nsSelectionMgr
// explicitly every time the selection changes. We have to make sure
// that this isn't expensive, since selection changes happen frequently,
// and that it doesn't happen by default on the other platforms, since
// nsRangeList is XP code and the other two platforms wouldn't like
// this behavior.
//
// 2. Have nsSelectionMgr::SelectionRequestor query for the current
// selection each time it's called, and throw away the stream passed in
// (or, better, change the interface not to pass a stream in in the
// first place).
// This requires that nsSelectionMgr have access to the current selection,
// which probably means that when the PresShell is creating nsSelectionMgr,
// it pass a pointer to itself along, and the SelectionMgr just queries
// the PresShell whenever it wants the selection.
// This seems like a much cleaner solution, but unfortunately does
// require an interface change on all three platforms.
//
// #2 will probably happen soon. Talk to me (akkana@netscape.com) if
// you're reading this and have an opinion about this.
//
#include "nsSelectionMgr.h" #include "nsSelectionMgr.h"
#include <strstream.h> #include <strstream.h>
@ -50,7 +83,7 @@ nsresult nsSelectionMgr::QueryInterface(const nsIID& aIID,
NS_ADDREF_THIS(); NS_ADDREF_THIS();
return NS_OK; return NS_OK;
} }
return !NS_OK; return NS_ERROR_FAILURE;
} }
nsSelectionMgr::nsSelectionMgr() nsSelectionMgr::nsSelectionMgr()
@ -58,10 +91,27 @@ nsSelectionMgr::nsSelectionMgr()
NS_INIT_REFCNT(); NS_INIT_REFCNT();
mCopyStream = 0; mCopyStream = 0;
sWidget = 0;
} }
nsSelectionMgr::~nsSelectionMgr() nsSelectionMgr::~nsSelectionMgr()
{ {
// Remove all our event handlers:
if (sWidget &&
gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == sWidget->window)
gtk_selection_remove_all(sWidget);
if (mCopyStream)
delete mCopyStream;
}
void nsSelectionMgr::SetTopLevelWidget(GtkWidget* w)
{
// Don't set up any more event handlers if we're being called twice
// for the same toplevel widget
if (sWidget == w)
return;
sWidget = w;
} }
nsresult nsSelectionMgr::GetCopyOStream(ostream** aStream) nsresult nsSelectionMgr::GetCopyOStream(ostream** aStream)
@ -73,22 +123,31 @@ nsresult nsSelectionMgr::GetCopyOStream(ostream** aStream)
return NS_OK; return NS_OK;
} }
#if 0 // Called when another app requests the selection:
// May or may not need this routine: void nsSelectionMgr::SelectionClearCB( GtkWidget *widget,
static void ClearSelection() GdkEventSelection *event,
gpointer data)
{ {
// Before clearing the selection by setting the owner to NULL, if (data)
// we check if we are the actual owner ((nsSelectionMgr*)data)->SelectionClear(widget, event);
if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == sWidget->window) }
gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
GDK_CURRENT_TIME); void nsSelectionMgr::SelectionClear( GtkWidget *w,
GdkEventSelection *event )
{
// Delete the copy stream, since we don't need it any more:
if (mCopyStream)
delete mCopyStream;
mCopyStream = 0;
} }
#endif
// //
// Here folows a bunch of code which came from GTK's gtktestselection.c: // Here follows a bunch of code which came from GTK's gtktestselection.c:
// //
// XXX Scary -- this list doesn't seem to be in an include file anywhere!
// Apparently every app which uses gtk selection is expected to copy
// this list verbatim, and hope that it doesn't get out of sync! Oy.
typedef enum { typedef enum {
SEL_TYPE_NONE, SEL_TYPE_NONE,
APPLE_PICT, APPLE_PICT,
@ -109,103 +168,20 @@ typedef enum {
LAST_SEL_TYPE LAST_SEL_TYPE
} SelType; } SelType;
GdkAtom seltypes[LAST_SEL_TYPE]; //
// The types we support
typedef struct _Target { //
gchar *target_name;
SelType type;
GdkAtom target;
gint format;
} Target;
/* The following is a list of all the selection targets defined
in the ICCCM */
static Target targets[] = {
{ "ADOBE_PORTABLE_DOCUMENT_FORMAT", STRING, 0, 8 },
{ "APPLE_PICT", APPLE_PICT, 0, 8 },
{ "BACKGROUND", PIXEL, 0, 32 },
{ "BITMAP", BITMAP, 0, 32 },
{ "CHARACTER_POSITION", SPAN, 0, 32 },
{ "CLASS", TEXT, 0, 8 },
{ "CLIENT_WINDOW", WINDOW, 0, 32 },
{ "COLORMAP", COLORMAP, 0, 32 },
{ "COLUMN_NUMBER", SPAN, 0, 32 },
{ "COMPOUND_TEXT", COMPOUND_TEXT, 0, 8 },
/* { "DELETE", "NULL", 0, ? }, */
{ "DRAWABLE", DRAWABLE, 0, 32 },
{ "ENCAPSULATED_POSTSCRIPT", STRING, 0, 8 },
{ "ENCAPSULATED_POSTSCRIPT_INTERCHANGE", STRING, 0, 8 },
{ "FILE_NAME", TEXT, 0, 8 },
{ "FOREGROUND", PIXEL, 0, 32 },
{ "HOST_NAME", TEXT, 0, 8 },
/* { "INSERT_PROPERTY", "NULL", 0, ? NULL }, */
/* { "INSERT_SELECTION", "NULL", 0, ? NULL }, */
{ "LENGTH", INTEGER, 0, 32 },
{ "LINE_NUMBER", SPAN, 0, 32 },
{ "LIST_LENGTH", INTEGER, 0, 32 },
{ "MODULE", TEXT, 0, 8 },
/* { "MULTIPLE", "ATOM_PAIR", 0, 32 }, */
{ "NAME", TEXT, 0, 8 },
{ "ODIF", TEXT, 0, 8 },
{ "OWNER_OS", TEXT, 0, 8 },
{ "PIXMAP", PIXMAP, 0, 32 },
{ "POSTSCRIPT", STRING, 0, 8 },
{ "PROCEDURE", TEXT, 0, 8 },
{ "PROCESS", INTEGER, 0, 32 },
{ "STRING", STRING, 0, 8 },
{ "TARGETS", ATOM, 0, 32 },
{ "TASK", INTEGER, 0, 32 },
{ "TEXT", TEXT, 0, 8 },
{ "TIMESTAMP", INTEGER, 0, 32 },
{ "USER", TEXT, 0, 8 },
};
static int num_targets = sizeof(targets)/sizeof(Target);
static GtkTargetEntry targetlist[] = { static GtkTargetEntry targetlist[] = {
{ "STRING", 0, STRING }, { "STRING", 0, STRING },
{ "TEXT", 0, TEXT }, { "TEXT", 0, TEXT },
{ "COMPOUND_TEXT", 0, COMPOUND_TEXT } { "COMPOUND_TEXT", 0, COMPOUND_TEXT }
// Probably need to add entries for XIF and HTML
}; };
static gint ntargets = sizeof(targetlist) / sizeof(targetlist[0]); static gint ntargets = sizeof(targetlist) / sizeof(targetlist[0]);
GtkWidget *selection_text; //
GtkWidget *selection_button; // The event handler to handle selection requests:
GString *selection_string = NULL; //
static void
init_atoms (void)
{
int i;
seltypes[SEL_TYPE_NONE] = GDK_NONE;
seltypes[APPLE_PICT] = gdk_atom_intern ("APPLE_PICT",FALSE);
seltypes[ATOM] = gdk_atom_intern ("ATOM",FALSE);
seltypes[ATOM_PAIR] = gdk_atom_intern ("ATOM_PAIR",FALSE);
seltypes[BITMAP] = gdk_atom_intern ("BITMAP",FALSE);
seltypes[C_STRING] = gdk_atom_intern ("C_STRING",FALSE);
seltypes[COLORMAP] = gdk_atom_intern ("COLORMAP",FALSE);
seltypes[COMPOUND_TEXT] = gdk_atom_intern ("COMPOUND_TEXT",FALSE);
seltypes[DRAWABLE] = gdk_atom_intern ("DRAWABLE",FALSE);
seltypes[INTEGER] = gdk_atom_intern ("INTEGER",FALSE);
seltypes[PIXEL] = gdk_atom_intern ("PIXEL",FALSE);
seltypes[PIXMAP] = gdk_atom_intern ("PIXMAP",FALSE);
seltypes[SPAN] = gdk_atom_intern ("SPAN",FALSE);
seltypes[STRING] = gdk_atom_intern ("STRING",FALSE);
seltypes[TEXT] = gdk_atom_intern ("TEXT",FALSE);
seltypes[WINDOW] = gdk_atom_intern ("WINDOW",FALSE);
for (i=0; i<num_targets; i++)
targets[i].target = gdk_atom_intern (targets[i].target_name, FALSE);
}
void nsSelectionMgr::SetTopLevelWidget(GtkWidget* w)
{
sWidget = w;
}
// The event handler to handle paste requests:
void nsSelectionMgr::SelectionRequestCB( GtkWidget *widget, void nsSelectionMgr::SelectionRequestCB( GtkWidget *widget,
GtkSelectionData *selection_data, GtkSelectionData *selection_data,
guint /*info*/, guint /*info*/,
@ -216,6 +192,11 @@ void nsSelectionMgr::SelectionRequestCB( GtkWidget *widget,
((nsSelectionMgr*)data)->SelectionRequestor(widget, selection_data); ((nsSelectionMgr*)data)->SelectionRequestor(widget, selection_data);
} }
//
// SelectionRequestor:
// This is the routine which gets called when another app
// requests the selection
//
void nsSelectionMgr::SelectionRequestor( GtkWidget *widget, void nsSelectionMgr::SelectionRequestor( GtkWidget *widget,
GtkSelectionData *selection_data ) GtkSelectionData *selection_data )
{ {
@ -229,17 +210,22 @@ void nsSelectionMgr::SelectionRequestor( GtkWidget *widget,
// the format arg, "8", indicates string data with no endianness // the format arg, "8", indicates string data with no endianness
} }
//
// CopyToClipboard:
// This is the routine which gets called when the user selects edit->copy.
//
nsresult nsSelectionMgr::CopyToClipboard() nsresult nsSelectionMgr::CopyToClipboard()
{ {
// we'd better already have a stream and a widget ... // we'd better already have a stream and a widget ...
if (!mCopyStream || !sWidget) if (!mCopyStream || !sWidget)
return NS_ERROR_NOT_INITIALIZED; return NS_ERROR_NOT_INITIALIZED;
char* str = (char*)mCopyStream->str(); // If we're already the selection owner, don't need to do anything,
//printf("owning selection:\n--'%s'--\n", str); // we're already handling the events:
if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == sWidget->window)
return NS_OK;
// XXX How do we get a widget to pass to gtk_selection_owner_set? // register as the selection owner:
// That's the sticky part of the whole mess
gint have_selection = gtk_selection_owner_set(sWidget, gint have_selection = gtk_selection_owner_set(sWidget,
GDK_SELECTION_PRIMARY, GDK_SELECTION_PRIMARY,
GDK_CURRENT_TIME); GDK_CURRENT_TIME);
@ -251,24 +237,23 @@ nsresult nsSelectionMgr::CopyToClipboard()
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
// Now set up the event handler to handle paste requests: // Set up all the event handlers
// This doesn't seem to be working right, or at least, gtk_selection_add_targets (sWidget, GDK_SELECTION_PRIMARY,
// when we paste in an xterm we never get notified targetlist, ntargets);
// of selection_request_events or the others listed below. // Respond to requests for the selection:
static beenhere = 0; gtk_signal_connect(GTK_OBJECT(sWidget),
if (!beenhere) "selection_get",
{ GTK_SIGNAL_FUNC(nsSelectionMgr::SelectionRequestCB),
init_atoms(); this);
gtk_selection_add_targets (sWidget, GDK_SELECTION_PRIMARY, gtk_signal_connect(GTK_OBJECT(sWidget), "selection_clear_event",
targetlist, ntargets); GTK_SIGNAL_FUNC (nsSelectionMgr::SelectionClearCB),
gtk_signal_connect(GTK_OBJECT(sWidget), this);
//"selection_request_event",
//"selection_notify_event", // Other signals we should handle in some fashion:
"selection_get", //"selection_received" (when we've requested a paste and it's come in --
//"selection_received", // this may need to be handled by a different class, e.g. nsIEditor.
GTK_SIGNAL_FUNC(nsSelectionMgr::SelectionRequestCB), //"selection_request_event" (dunno what this one is, maybe we don't need it)
this); //"selection_notify_event" (don't know what this is either)
}
return NS_OK; return NS_OK;
} }

View File

@ -38,10 +38,14 @@ public:
nsSelectionMgr(); nsSelectionMgr();
virtual ~nsSelectionMgr(); virtual ~nsSelectionMgr();
//
// nsISelectionMgr methods:
//
NS_IMETHOD GetCopyOStream(ostream** aStream); NS_IMETHOD GetCopyOStream(ostream** aStream);
NS_IMETHOD CopyToClipboard(); NS_IMETHOD CopyToClipboard();
// Other methods specific to X:
static void SetTopLevelWidget(GtkWidget* w); static void SetTopLevelWidget(GtkWidget* w);
private: private:
@ -49,13 +53,19 @@ private:
static GtkWidget* sWidget; // the app's top level widget, set by nsWindow static GtkWidget* sWidget; // the app's top level widget, set by nsWindow
void SelectionClear( GtkWidget *w,
GdkEventSelection *event );
void SelectionRequestor( GtkWidget *w, void SelectionRequestor( GtkWidget *w,
GtkSelectionData *selection_data); GtkSelectionData *selection_data );
static void SelectionRequestCB( GtkWidget *widget,
static void SelectionRequestCB( GtkWidget *widget,
GtkSelectionData *selection_data, GtkSelectionData *selection_data,
guint info, guint info,
guint time, guint time,
gpointer data); gpointer data );
static void SelectionClearCB( GtkWidget *widget,
GdkEventSelection *event,
gpointer data );
}; };
nsresult NS_NewSelectionMgr(nsISelectionMgr** aInstancePtrResult); nsresult NS_NewSelectionMgr(nsISelectionMgr** aInstancePtrResult);