mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-07 11:56:51 +00:00
Improvements in handling of the X selection, and some explanatory comments
This commit is contained in:
parent
6829c9dcd5
commit
63f1f1e77a
@ -16,6 +16,39 @@
|
||||
* 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 <strstream.h>
|
||||
@ -50,7 +83,7 @@ nsresult nsSelectionMgr::QueryInterface(const nsIID& aIID,
|
||||
NS_ADDREF_THIS();
|
||||
return NS_OK;
|
||||
}
|
||||
return !NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsSelectionMgr::nsSelectionMgr()
|
||||
@ -58,10 +91,27 @@ nsSelectionMgr::nsSelectionMgr()
|
||||
NS_INIT_REFCNT();
|
||||
|
||||
mCopyStream = 0;
|
||||
sWidget = 0;
|
||||
}
|
||||
|
||||
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)
|
||||
@ -73,22 +123,31 @@ nsresult nsSelectionMgr::GetCopyOStream(ostream** aStream)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// May or may not need this routine:
|
||||
static void ClearSelection()
|
||||
// Called when another app requests the selection:
|
||||
void nsSelectionMgr::SelectionClearCB( GtkWidget *widget,
|
||||
GdkEventSelection *event,
|
||||
gpointer data)
|
||||
{
|
||||
// Before clearing the selection by setting the owner to NULL,
|
||||
// we check if we are the actual owner
|
||||
if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == sWidget->window)
|
||||
gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
|
||||
GDK_CURRENT_TIME);
|
||||
if (data)
|
||||
((nsSelectionMgr*)data)->SelectionClear(widget, event);
|
||||
}
|
||||
|
||||
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 {
|
||||
SEL_TYPE_NONE,
|
||||
APPLE_PICT,
|
||||
@ -109,103 +168,20 @@ typedef enum {
|
||||
LAST_SEL_TYPE
|
||||
} SelType;
|
||||
|
||||
GdkAtom seltypes[LAST_SEL_TYPE];
|
||||
|
||||
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);
|
||||
|
||||
//
|
||||
// The types we support
|
||||
//
|
||||
static GtkTargetEntry targetlist[] = {
|
||||
{ "STRING", 0, STRING },
|
||||
{ "TEXT", 0, TEXT },
|
||||
{ "COMPOUND_TEXT", 0, COMPOUND_TEXT }
|
||||
// Probably need to add entries for XIF and HTML
|
||||
};
|
||||
static gint ntargets = sizeof(targetlist) / sizeof(targetlist[0]);
|
||||
|
||||
GtkWidget *selection_text;
|
||||
GtkWidget *selection_button;
|
||||
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:
|
||||
//
|
||||
// The event handler to handle selection requests:
|
||||
//
|
||||
void nsSelectionMgr::SelectionRequestCB( GtkWidget *widget,
|
||||
GtkSelectionData *selection_data,
|
||||
guint /*info*/,
|
||||
@ -216,6 +192,11 @@ void nsSelectionMgr::SelectionRequestCB( GtkWidget *widget,
|
||||
((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,
|
||||
GtkSelectionData *selection_data )
|
||||
{
|
||||
@ -229,17 +210,22 @@ void nsSelectionMgr::SelectionRequestor( GtkWidget *widget,
|
||||
// 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()
|
||||
{
|
||||
// we'd better already have a stream and a widget ...
|
||||
if (!mCopyStream || !sWidget)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
char* str = (char*)mCopyStream->str();
|
||||
//printf("owning selection:\n--'%s'--\n", str);
|
||||
// If we're already the selection owner, don't need to do anything,
|
||||
// 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?
|
||||
// That's the sticky part of the whole mess
|
||||
// register as the selection owner:
|
||||
gint have_selection = gtk_selection_owner_set(sWidget,
|
||||
GDK_SELECTION_PRIMARY,
|
||||
GDK_CURRENT_TIME);
|
||||
@ -251,24 +237,23 @@ nsresult nsSelectionMgr::CopyToClipboard()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Now set up the event handler to handle paste requests:
|
||||
// This doesn't seem to be working right, or at least,
|
||||
// when we paste in an xterm we never get notified
|
||||
// of selection_request_events or the others listed below.
|
||||
static beenhere = 0;
|
||||
if (!beenhere)
|
||||
{
|
||||
init_atoms();
|
||||
gtk_selection_add_targets (sWidget, GDK_SELECTION_PRIMARY,
|
||||
targetlist, ntargets);
|
||||
gtk_signal_connect(GTK_OBJECT(sWidget),
|
||||
//"selection_request_event",
|
||||
//"selection_notify_event",
|
||||
"selection_get",
|
||||
//"selection_received",
|
||||
GTK_SIGNAL_FUNC(nsSelectionMgr::SelectionRequestCB),
|
||||
this);
|
||||
}
|
||||
// Set up all the event handlers
|
||||
gtk_selection_add_targets (sWidget, GDK_SELECTION_PRIMARY,
|
||||
targetlist, ntargets);
|
||||
// Respond to requests for the selection:
|
||||
gtk_signal_connect(GTK_OBJECT(sWidget),
|
||||
"selection_get",
|
||||
GTK_SIGNAL_FUNC(nsSelectionMgr::SelectionRequestCB),
|
||||
this);
|
||||
gtk_signal_connect(GTK_OBJECT(sWidget), "selection_clear_event",
|
||||
GTK_SIGNAL_FUNC (nsSelectionMgr::SelectionClearCB),
|
||||
this);
|
||||
|
||||
// Other signals we should handle in some fashion:
|
||||
//"selection_received" (when we've requested a paste and it's come in --
|
||||
// this may need to be handled by a different class, e.g. nsIEditor.
|
||||
//"selection_request_event" (dunno what this one is, maybe we don't need it)
|
||||
//"selection_notify_event" (don't know what this is either)
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -38,10 +38,14 @@ public:
|
||||
nsSelectionMgr();
|
||||
virtual ~nsSelectionMgr();
|
||||
|
||||
//
|
||||
// nsISelectionMgr methods:
|
||||
//
|
||||
NS_IMETHOD GetCopyOStream(ostream** aStream);
|
||||
|
||||
NS_IMETHOD CopyToClipboard();
|
||||
|
||||
// Other methods specific to X:
|
||||
static void SetTopLevelWidget(GtkWidget* w);
|
||||
|
||||
private:
|
||||
@ -49,13 +53,19 @@ private:
|
||||
|
||||
static GtkWidget* sWidget; // the app's top level widget, set by nsWindow
|
||||
|
||||
void SelectionClear( GtkWidget *w,
|
||||
GdkEventSelection *event );
|
||||
void SelectionRequestor( GtkWidget *w,
|
||||
GtkSelectionData *selection_data);
|
||||
static void SelectionRequestCB( GtkWidget *widget,
|
||||
GtkSelectionData *selection_data );
|
||||
|
||||
static void SelectionRequestCB( GtkWidget *widget,
|
||||
GtkSelectionData *selection_data,
|
||||
guint info,
|
||||
guint time,
|
||||
gpointer data);
|
||||
guint info,
|
||||
guint time,
|
||||
gpointer data );
|
||||
static void SelectionClearCB( GtkWidget *widget,
|
||||
GdkEventSelection *event,
|
||||
gpointer data );
|
||||
};
|
||||
|
||||
nsresult NS_NewSelectionMgr(nsISelectionMgr** aInstancePtrResult);
|
||||
|
Loading…
Reference in New Issue
Block a user