Back to index

plt-scheme  4.2.1
Clipboard.cc
Go to the documentation of this file.
00001 /*
00002  * File:        wx_clipb.cc
00003  * Purpose:     Clipboard implementation.
00004  * Author:      Julian Smart and Matthew Flatt
00005  * Created:     1993
00006  * Updated:   August 1994
00007  * Copyright:   (c) 2004-2009 PLT Scheme Inc.
00008  * Copyright:   (c) 1993, AIAI, University of Edinburgh
00009  */
00010 
00011 #ifdef __GNUG__
00012 #pragma implementation
00013 #endif
00014 
00015 #define Uses_wxApp
00016 #define Uses_wxFrame
00017 #define Uses_wxClipboard
00018 #define Uses_XtIntrinsic
00019 #define Uses_XtIntrinsicP
00020 #define Uses_XLib
00021 #include "wx.h"
00022 #include <X11/Xatom.h>
00023 #define  Uses_ShellWidget
00024 #include "widgets.h"
00025 #include "scheme.h"
00026 
00027 wxClipboard *wxTheSelection;
00028 wxClipboard *wxTheClipboard;
00029 
00030 static Widget getClipWindow;
00031 /* These two are used by mredx.cxx: */
00032 Widget wx_clipWindow, wx_selWindow;
00033 
00034 extern void MrEdQueueBeingReplaced(wxClipboardClient *clipOwner);
00035 
00036 #ifdef MZ_PRECISE_GC
00037 Atom ATOM(char *atom) 
00038 {
00039   Widget wgt;
00040   wgt = wxAPP_TOPLEVEL;
00041   return XInternAtom(XtDisplay(wgt), atom, FALSE);
00042 }
00043 #else
00044 # define ATOM(atom) XInternAtom(XtDisplay(wxAPP_TOPLEVEL), atom, FALSE)
00045 #endif
00046 #define VALUE_TYPE void*
00047 
00048 Atom xa_utf8, xa_text, xa_targets, xa_clipboard;
00049 
00050 static wxFrame *clipboard_frame, *selection_frame;
00051 static wxFrame *get_clipboard_frame;
00052 
00053 void wxInitClipboard(void)
00054 {
00055   if (!wx_clipWindow) {
00056     /* Hack: We need a window for clipboard stuff */
00057     wxWindow_Xintern *fh;
00058     wxREGGLOB(clipboard_frame);
00059     wxREGGLOB(selection_frame);
00060     wxREGGLOB(get_clipboard_frame);
00061     clipboard_frame = new wxFrame(NULL, "clipboard", 0, 0, 10, 10);
00062     selection_frame = new wxFrame(NULL, "selection", 0, 0, 10, 10);
00063     get_clipboard_frame = new wxFrame(NULL, "get clipboard", 0, 0, 10, 10);
00064 
00065     fh = clipboard_frame->GetHandle();
00066     wx_clipWindow = fh->frame;
00067     XtRealizeWidget(wx_clipWindow);
00068 
00069     fh = selection_frame->GetHandle();
00070     wx_selWindow = fh->frame;
00071     XtRealizeWidget(wx_selWindow);
00072 
00073     fh = get_clipboard_frame->GetHandle();
00074     getClipWindow = fh->frame;
00075     XtRealizeWidget(getClipWindow);
00076 
00077     /* Initially not in any specific context. */
00078     clipboard_frame->context = NULL;
00079     selection_frame->context = NULL;
00080     /* Not in any specific context. */
00081     get_clipboard_frame->context = NULL;
00082   }
00083 
00084   if (!wxTheClipboard) {
00085     int cts;
00086     
00087     wxREGGLOB(wxTheClipboard);
00088     wxREGGLOB(wxTheSelection);
00089 
00090     wxTheSelection = new wxClipboard;
00091     wxTheSelection->is_sel = 1;
00092     wxTheSelection->frame = selection_frame;
00093 
00094     if (!wxGetBoolPreference("selectionAsClipboard", &cts))
00095       cts = 0;
00096 
00097     if (cts) {
00098       wxTheClipboard = wxTheSelection;
00099     } else {
00100       wxTheClipboard = new wxClipboard;
00101       wxTheClipboard->frame = clipboard_frame;      
00102     }
00103   }
00104 
00105   xa_utf8 = ATOM("UTF8_STRING");
00106   xa_text = ATOM("TEXT");
00107   xa_targets = ATOM("TARGETS");
00108   xa_clipboard = ATOM("CLIPBOARD");
00109 }
00110 
00111 Window wxAddClipboardWindowProperty(Atom prop)
00112 {
00113   unsigned char data[1] = { 'm' };
00114   XChangeProperty(XtDisplay(wx_clipWindow), XtWindow(wx_clipWindow),
00115                 prop, prop, 8, PropModeReplace, data, 1);
00116   return XtWindow(wx_clipWindow);
00117 }
00118 
00119 static void AddClipboardFrame(wxClipboard *cb, int on)
00120 {
00121   if (!on)
00122     cb->frame->context = NULL;
00123 }
00124 
00125 wxClipboardClient::wxClipboardClient()
00126 {
00127   formats = new wxStringList();
00128 }
00129 
00130 wxClipboard::wxClipboard()
00131 {
00132   clipOwner = NULL;
00133   cbString = NULL;
00134 
00135   {
00136     void *v;
00137     v = MALLOC_SAFEREF();
00138     saferef = v;
00139     SET_SAFEREF(saferef, this);
00140   }
00141 }
00142 
00143 wxClipboard::~wxClipboard()
00144 {
00145 }
00146 
00147 static Boolean doConvertClipboard(wxClipboard *cb,
00148                               Widget WXUNUSED(w), Atom *WXUNUSED(selection), Atom *target,
00149                               Atom *type_return, XtPointer *value_return,
00150                               unsigned long *length_return,
00151                               int *format_return)
00152 {
00153   Atom xa;
00154   char **formats = NULL;
00155   int i = 0, count, extra;
00156 
00157   if (*target == xa_targets) {
00158     if (cb->clipOwner) {
00159       count = cb->clipOwner->formats->Number();
00160       extra = (cb->clipOwner->formats->Member("TEXT")) ? 2 : 0;
00161       cb->receivedTargets = new WXGC_ATOMIC Atom[count + extra];
00162       formats = cb->clipOwner->formats->ListToArray(FALSE);
00163       for (i = 0; i < count; i++) {
00164        Atom atm;
00165        atm = ATOM(formats[i]);
00166        ((Atom *)cb->receivedTargets)[i] = atm;
00167       }
00168       if (extra) {
00169        ((Atom *)cb->receivedTargets)[count] = xa_utf8;
00170        ((Atom *)cb->receivedTargets)[count+1] = XA_STRING;
00171       }
00172     } else {
00173       count = 3;
00174       cb->receivedTargets = new WXGC_ATOMIC Atom[3];
00175       ((Atom *)cb->receivedTargets)[0] = xa_utf8;
00176       ((Atom *)cb->receivedTargets)[1] = XA_STRING;
00177       ((Atom *)cb->receivedTargets)[2] = xa_text;
00178       extra = 0;
00179     }
00180 
00181     *value_return = (VALUE_TYPE)cb->receivedTargets;
00182     *type_return = XA_ATOM;
00183     if (sizeof(Atom) > 4) {
00184       *format_return = 32;
00185       *length_return = (count + extra) * (sizeof(Atom) / 4);
00186     } else {
00187       *format_return = 8 * sizeof(Atom);
00188       *length_return = count + extra;
00189     }
00190 
00191     cb->sentString = NULL;
00192 
00193     return TRUE;
00194   } 
00195   
00196   cb->receivedTargets = NULL;
00197 
00198   if (cb->clipOwner) {
00199     formats = cb->clipOwner->formats->ListToArray(FALSE);
00200     for (i = cb->clipOwner->formats->Number(); i--; ) {
00201       xa = ATOM(formats[i]);
00202       if (xa == *target)
00203        break;
00204       if (xa == xa_text && (*target == xa_utf8 || *target == XA_STRING))
00205        break;
00206     }
00207     if (i < 0)
00208       return FALSE;
00209   } else if (*target != xa_text && *target != xa_utf8 && *target != XA_STRING)
00210     return FALSE;
00211 
00212   *type_return = xa_utf8;
00213   *format_return = 8;
00214   if (cb->clipOwner) {
00215     long sz = 0;
00216     char *s;
00217     s = cb->clipOwner->GetData(formats[i], &sz);
00218     cb->sentString = s;
00219     *length_return = sz;
00220     *value_return = (VALUE_TYPE)cb->sentString;
00221   } else {
00222     *value_return = (VALUE_TYPE)cb->cbString;
00223     *length_return = strlen(cb->cbString);
00224   }
00225 
00226   return TRUE;
00227 }
00228 
00229 static Boolean wxConvertClipboard(Widget w, Atom *selection, Atom *target,
00230                               Atom *type_return, XtPointer *value_return,
00231                               unsigned long *length_return,
00232                               int *format_return)
00233 {
00234   return doConvertClipboard(wxTheClipboard, w, selection, target, type_return, value_return,
00235                          length_return, format_return);
00236 }
00237 
00238 static Boolean wxConvertSelection(Widget w, Atom *selection, Atom *target,
00239                              Atom *type_return, XtPointer *value_return,
00240                              unsigned long *length_return,
00241                              int *format_return)
00242 {
00243   return doConvertClipboard(wxTheSelection, w, selection, target, type_return, value_return,
00244                          length_return, format_return);
00245 }
00246 
00247 static void doSelectionDone(wxClipboard *cb, Widget WXUNUSED(w), Atom *WXUNUSED(selection), Atom *WXUNUSED(target))
00248 {
00249   cb->sentString = NULL;
00250   cb->receivedTargets = NULL;
00251 }
00252 
00253 static void wxClipboardDone(Widget w, Atom *selection, Atom *target)
00254 {
00255   doSelectionDone(wxTheClipboard, w, selection, target);
00256 }
00257 
00258 static void wxSelectionDone(Widget w, Atom *selection, Atom *target)
00259 {
00260   doSelectionDone(wxTheSelection, w, selection, target);
00261 }
00262 
00263 static void wxStringClipboardDone(Widget WXUNUSED(w), Atom *WXUNUSED(selection), Atom *WXUNUSED(target))
00264 {
00265   /* do nothing */
00266 }
00267 
00268 static void wxStringSelectionDone(Widget WXUNUSED(w), Atom *WXUNUSED(selection), Atom *WXUNUSED(target))
00269 {
00270   /* do nothing */
00271 }
00272 
00273 static void doLoseClipboard(wxClipboard *cb, Widget WXUNUSED(w), Atom *WXUNUSED(selection))
00274 {
00275   if (cb->clipOwner) {
00276     MrEdQueueBeingReplaced(cb->clipOwner);
00277     cb->clipOwner = NULL;
00278     AddClipboardFrame(cb, 0);
00279   }
00280   cb->cbString = NULL;
00281 }
00282 
00283 static void wxLoseClipboard(Widget w, Atom *selection)
00284 {
00285   doLoseClipboard(wxTheClipboard, w, selection);
00286 }
00287 
00288 static void wxLoseSelection(Widget w, Atom *selection)
00289 {
00290   doLoseClipboard(wxTheSelection, w, selection);
00291 }
00292 
00293 void wxClipboard::SetClipboardClient(wxClipboardClient *client, long time)
00294 {
00295   Bool got_selection;
00296 
00297   if (clipOwner) {
00298     MrEdQueueBeingReplaced(clipOwner);
00299     clipOwner = NULL;
00300     AddClipboardFrame(this, 0);
00301   }
00302   cbString = NULL;
00303   
00304   /* Merely setting the context for a frame would not
00305      normally redirect events to a different eventspace.
00306      But there's a hack in mredx.cxx to help complete
00307      the redirection. */
00308   clipOwner = client;
00309   client->context = wxGetContextForFrame();
00310   frame->context = client->context;
00311   AddClipboardFrame(this, 1);
00312 
00313   if (is_sel) {
00314     got_selection = XtOwnSelection(wx_selWindow, XA_PRIMARY, time,
00315                                wxConvertSelection, wxLoseSelection, 
00316                                wxSelectionDone);
00317   } else {
00318     got_selection = XtOwnSelection(wx_clipWindow, xa_clipboard, time,
00319                                wxConvertClipboard, wxLoseClipboard, 
00320                                wxClipboardDone);
00321   }
00322 
00323   if (!got_selection) {
00324     MrEdQueueBeingReplaced(clipOwner);
00325     clipOwner = NULL;
00326     AddClipboardFrame(this, 0);
00327   }
00328 }
00329 
00330 wxClipboardClient *wxClipboard::GetClipboardClient()
00331 {
00332   return clipOwner;
00333 }
00334 
00335 void wxClipboard::SetClipboardString(char *str, long time)
00336 {
00337   Bool got_selection;
00338 
00339   if (clipOwner) {
00340     MrEdQueueBeingReplaced(clipOwner);
00341     clipOwner = NULL;
00342     AddClipboardFrame(this, 0);
00343   }
00344 
00345   cbString = str;
00346 
00347   if (is_sel) {
00348     got_selection = XtOwnSelection(wx_selWindow, XA_PRIMARY, time,
00349                                wxConvertSelection, wxLoseSelection, 
00350                                wxStringSelectionDone);
00351   } else {
00352     got_selection = XtOwnSelection(wx_clipWindow, xa_clipboard, time,
00353                                wxConvertClipboard, wxLoseClipboard, 
00354                                wxStringClipboardDone);
00355   }
00356   
00357   if (!got_selection) {
00358     cbString = NULL;
00359   }
00360 }
00361 
00362 void wxClipboard::SetClipboardBitmap(wxBitmap *bm, long time)
00363 {
00364   if (clipOwner) {
00365     MrEdQueueBeingReplaced(clipOwner);
00366     AddClipboardFrame(this, 0);
00367     clipOwner = NULL;
00368   }
00369 
00370   cbString = NULL;
00371 
00372   /* Don't know how to put a bitmap into the clipboard. */
00373 }
00374 
00375 wxBitmap *wxClipboard::GetClipboardBitmap(long time)
00376 {
00377   return NULL;
00378 }
00379 
00380 static void wxGetTargets(Widget WXUNUSED(w), XtPointer _cb, Atom *WXUNUSED(sel), Atom *WXUNUSED(type),
00381                       XtPointer value, unsigned long *len, int *WXUNUSED(format))
00382 {
00383   wxClipboard *cb = (wxClipboard *)GET_SAFEREF(_cb);
00384 
00385   if (cb->in_progress < 0) {
00386     cb->in_progress = 0;
00387   } else {
00388     if (*len <= 0) {
00389       cb->receivedTargets = (void *)1; /* To break the waiting loop */
00390       cb->receivedLength = 0;
00391     } else {
00392       cb->receivedTargets = new WXGC_ATOMIC Atom[*len];
00393       memcpy(cb->receivedTargets, value, *len * sizeof(Atom));
00394       cb->receivedLength = *len;
00395     }
00396   }
00397 }
00398 
00399 static void wxGetSelection(Widget WXUNUSED(w), XtPointer _cb, Atom *WXUNUSED(sel), Atom *WXUNUSED(type),
00400                         XtPointer value, unsigned long *len, int *WXUNUSED(format))
00401 {
00402   wxClipboard *cb = (wxClipboard *)GET_SAFEREF(_cb);
00403 
00404   if (cb->in_progress < 0) {
00405     cb->in_progress = 0;
00406   } else {
00407     cb->receivedString = new WXGC_ATOMIC char[(*len) + 1];
00408     memcpy(cb->receivedString, value, *len);
00409     cb->receivedString[*len] = 0;
00410     cb->receivedLength = *len;
00411   }
00412 }
00413 
00414 char *wxClipboard::GetClipboardString(long time)
00415 {
00416   char *str;
00417   long length;
00418 
00419   str = GetClipboardData("TEXT", &length, time);
00420   if (!str)
00421     str = "";
00422 
00423   return str;
00424 }
00425 
00426 extern void wxBlockUntil(int (*)(void *), void *);
00427 extern void wxBlockUntilTimeout(int (*)(void *), void *, float);
00428 extern double scheme_get_inexact_milliseconds(void);
00429 
00430 static int clip_timeout;
00431 
00432 static int CheckNotInProgress(void *_cb)
00433 {
00434   wxClipboard *cb = (wxClipboard *)GET_SAFEREF(_cb);
00435   return !cb->in_progress;
00436 }
00437 
00438 static int CheckReadyTarget(void *_cb)
00439 {
00440   wxClipboard *cb = (wxClipboard *)GET_SAFEREF(_cb);
00441   double now;
00442   now = scheme_get_inexact_milliseconds();
00443   if (now > cb->start_time + clip_timeout)
00444     return 1;
00445   return !!cb->receivedTargets;
00446 }
00447 
00448 static int CheckReadyString(void *_cb)
00449 {
00450   wxClipboard *cb = (wxClipboard *)GET_SAFEREF(_cb);
00451   double now;
00452   now = scheme_get_inexact_milliseconds();
00453   if (now > cb->start_time + clip_timeout)
00454     return 1;
00455   return !!cb->receivedString;
00456 }
00457 
00458 static void abandoned_clip(void *_cb)
00459 {
00460   wxClipboard *cb = (wxClipboard *)GET_SAFEREF(_cb);
00461 
00462   if (cb->in_progress)
00463     cb->in_progress = -1;
00464 }
00465 
00466 char *wxClipboard::GetClipboardData(char *format, long *length, long time, int alt_sel)
00467 {
00468   if (clipOwner && !alt_sel)  {
00469     if (clipOwner->formats->Member(format))
00470       return wxsGetDataInEventspace(clipOwner, format, length);
00471     else
00472       return NULL;
00473   } else if (cbString && !alt_sel) {
00474     if (!strcmp(format, "TEXT"))
00475       return copystring(cbString);
00476     else
00477       return NULL;
00478   } else {
00479     Atom xa;
00480     long i;
00481 
00482     if (!clip_timeout)
00483       clip_timeout = XtAppGetSelectionTimeout(wxAPP_CONTEXT) + 1;
00484 
00485     /* Need to make sure that only one thread is here at a time. */
00486     wxBlockUntil(CheckNotInProgress, saferef);
00487     
00488     in_progress = 1;
00489 
00490     receivedString = NULL;
00491     receivedTargets = NULL;
00492 
00493     XtGetSelectionValue(getClipWindow, alt_sel ? alt_sel : (is_sel ? XA_PRIMARY : xa_clipboard),
00494                      xa_targets, wxGetTargets, (XtPointer)saferef, time);
00495 
00496     start_time = scheme_get_inexact_milliseconds();
00497     BEGIN_ESCAPEABLE(abandoned_clip, saferef);
00498     wxBlockUntilTimeout(CheckReadyTarget, saferef, clip_timeout);
00499     END_ESCAPEABLE();
00500 
00501     if (!receivedTargets) {
00502       /* Timeout */
00503       in_progress = 0;
00504       return NULL;
00505     }
00506 
00507     xa = ATOM(format);
00508 
00509     for (i = 0; i < receivedLength; i++) {
00510       if (((Atom *)receivedTargets)[i] == xa)
00511        break;
00512       else if ((((Atom *)receivedTargets)[i] == xa_utf8
00513               || ((Atom *)receivedTargets)[i] == XA_STRING)
00514               && xa == xa_text) {
00515        xa = ((Atom *)receivedTargets)[i];
00516        break;
00517       }
00518     }
00519 
00520     if (receivedLength)
00521       receivedTargets = NULL;
00522 
00523     if (i >= receivedLength) {
00524       in_progress = 0;
00525       return NULL;
00526     }
00527 
00528     XtGetSelectionValue(getClipWindow, alt_sel ? alt_sel : (is_sel ? XA_PRIMARY : xa_clipboard),
00529                      xa, wxGetSelection, (XtPointer)saferef, 0);
00530     
00531     start_time = scheme_get_inexact_milliseconds();
00532     BEGIN_ESCAPEABLE(abandoned_clip, saferef);
00533     wxBlockUntilTimeout(CheckReadyString, saferef, clip_timeout);
00534     END_ESCAPEABLE();
00535 
00536     if (!receivedString) {
00537       /* Timeout */
00538       in_progress = 0;
00539       return NULL;
00540     }
00541     
00542     *length = receivedLength;
00543 
00544     in_progress = 0;
00545 
00546     if (xa == XA_STRING) {
00547       /* Really should encode... */
00548     }
00549 
00550     return receivedString;
00551   }
00552 }