1/* 2 * tkMacOSXClipboard.c -- 3 * 4 * This file manages the clipboard for the Tk toolkit. 5 * 6 * Copyright (c) 1995-1997 Sun Microsystems, Inc. 7 * Copyright 2001, Apple Computer, Inc. 8 * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net> 9 * 10 * See the file "license.terms" for information on usage and redistribution 11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id: tkMacOSXClipboard.c,v 1.2.2.7 2007/06/29 03:22:01 das Exp $ 14 */ 15 16#include "tkMacOSXPrivate.h" 17#include "tkSelect.h" 18 19 20/* 21 *---------------------------------------------------------------------- 22 * 23 * TkSelGetSelection -- 24 * 25 * Retrieve the specified selection from another process. For 26 * now, only fetching XA_STRING from CLIPBOARD is supported. 27 * Eventually other types should be allowed. 28 * 29 * Results: 30 * The return value is a standard Tcl return value. 31 * If an error occurs (such as no selection exists) 32 * then an error message is left in the interp's result. 33 * 34 * Side effects: 35 * None. 36 * 37 *---------------------------------------------------------------------- 38 */ 39 40int 41TkSelGetSelection( 42 Tcl_Interp *interp, /* Interpreter to use for reporting errors. */ 43 Tk_Window tkwin, /* Window on whose behalf to retrieve the 44 * selection (determines display from which to 45 * retrieve). */ 46 Atom selection, /* Selection to retrieve. */ 47 Atom target, /* Desired form in which selection is to be 48 * returned. */ 49 Tk_GetSelProc *proc, /* Procedure to call to process the selection, 50 * once it has been retrieved. */ 51 ClientData clientData) /* Arbitrary value to pass to proc. */ 52{ 53 int result; 54 OSStatus err; 55 long length; 56 ScrapRef scrapRef; 57 char *buf; 58 59 if ((selection == Tk_InternAtom(tkwin, "CLIPBOARD")) 60 && (target == XA_STRING)) { 61 /* 62 * Get the scrap from the Macintosh global clipboard. 63 */ 64 65 err = ChkErr(GetCurrentScrap, &scrapRef); 66 if (err != noErr) { 67 Tcl_AppendResult(interp, Tk_GetAtomName(tkwin, selection), 68 " GetCurrentScrap failed.", NULL); 69 return TCL_ERROR; 70 } 71 72 /* 73 * Try UNICODE first 74 */ 75 err = ChkErr(GetScrapFlavorSize, scrapRef, kScrapFlavorTypeUnicode, 76 &length); 77 if (err == noErr && length > 0) { 78 Tcl_DString ds; 79 char *data; 80 81 buf = (char *) ckalloc(length + 2); 82 buf[length] = 0; 83 buf[length+1] = 0; /* 2-byte unicode null */ 84 err = ChkErr(GetScrapFlavorData, scrapRef, kScrapFlavorTypeUnicode, 85 &length, buf); 86 if (err == noErr) { 87 Tcl_DStringInit(&ds); 88 Tcl_UniCharToUtfDString((Tcl_UniChar *)buf, 89 Tcl_UniCharLen((Tcl_UniChar *)buf), &ds); 90 for (data = Tcl_DStringValue(&ds); *data != '\0'; data++) { 91 if (*data == '\r') { 92 *data = '\n'; 93 } 94 } 95 result = (*proc)(clientData, interp, Tcl_DStringValue(&ds)); 96 Tcl_DStringFree(&ds); 97 ckfree(buf); 98 return result; 99 } 100 } 101 102 err = ChkErr(GetScrapFlavorSize, scrapRef, 'TEXT', &length); 103 if (err != noErr) { 104 Tcl_AppendResult(interp, Tk_GetAtomName(tkwin, selection), 105 " GetScrapFlavorSize failed.", NULL); 106 return TCL_ERROR; 107 } 108 if (length > 0) { 109 Tcl_DString encodedText; 110 char *data; 111 112 buf = (char *) ckalloc(length + 1); 113 buf[length] = 0; 114 err = ChkErr(GetScrapFlavorData, scrapRef, 'TEXT', &length, buf); 115 if (err != noErr) { 116 Tcl_AppendResult(interp, Tk_GetAtomName(tkwin, selection), 117 " GetScrapFlavorData failed.", NULL); 118 return TCL_ERROR; 119 } 120 121 /* 122 * Tcl expects '\n' not '\r' as the line break character. 123 */ 124 125 for (data = buf; *data != '\0'; data++) { 126 if (*data == '\r') { 127 *data = '\n'; 128 } 129 } 130 131 Tcl_ExternalToUtfDString(TkMacOSXCarbonEncoding, buf, length, 132 &encodedText); 133 result = (*proc)(clientData, interp, 134 Tcl_DStringValue(&encodedText)); 135 Tcl_DStringFree(&encodedText); 136 137 ckfree(buf); 138 return result; 139 } 140 } 141 142 Tcl_AppendResult(interp, Tk_GetAtomName(tkwin, selection), 143 " selection doesn't exist or form \"", 144 Tk_GetAtomName(tkwin, target), "\" not defined", NULL); 145 return TCL_ERROR; 146} 147 148/* 149 *---------------------------------------------------------------------- 150 * 151 * TkSetSelectionOwner -- 152 * 153 * This function claims ownership of the specified selection. 154 * If the selection is CLIPBOARD, then we empty the system 155 * clipboard. 156 * 157 * Results: 158 * None. 159 * 160 * Side effects: 161 * None. 162 * 163 *---------------------------------------------------------------------- 164 */ 165 166void 167XSetSelectionOwner( 168 Display *display, /* X Display. */ 169 Atom selection, /* What selection to own. */ 170 Window owner, /* Window to be the owner. */ 171 Time time) /* The current time? */ 172{ 173 Tk_Window tkwin; 174 TkDisplay *dispPtr; 175 176 /* 177 * This is a gross hack because the Tk_InternAtom interface is broken. 178 * It expects a Tk_Window, even though it only needs a Tk_Display. 179 */ 180 181 tkwin = (Tk_Window) TkGetMainInfoList()->winPtr; 182 183 if (selection == Tk_InternAtom(tkwin, "CLIPBOARD")) { 184 /* 185 * Only claim and empty the clipboard if we aren't already the 186 * owner of the clipboard. 187 */ 188 189 dispPtr = TkGetMainInfoList()->winPtr->dispPtr; 190 if (dispPtr->clipboardActive) { 191 return; 192 } 193 ClearCurrentScrap(); 194 } 195} 196 197/* 198 *---------------------------------------------------------------------- 199 * 200 * TkSelUpdateClipboard -- 201 * 202 * This function is called to force the clipboard to be updated 203 * after new data is added. On the Mac we don't need to do 204 * anything. 205 * 206 * Results: 207 * None. 208 * 209 * Side effects: 210 * None. 211 * 212 *---------------------------------------------------------------------- 213 */ 214 215void 216TkSelUpdateClipboard( 217 TkWindow *winPtr, /* Window associated with clipboard. */ 218 TkClipboardTarget *targetPtr) 219 /* Info about the content. */ 220{ 221} 222 223/* 224 *-------------------------------------------------------------- 225 * 226 * TkSelEventProc -- 227 * 228 * This procedure is invoked whenever a selection-related 229 * event occurs. 230 * 231 * Results: 232 * None. 233 * 234 * Side effects: 235 * Lots: depends on the type of event. 236 * 237 *-------------------------------------------------------------- 238 */ 239 240void 241TkSelEventProc( 242 Tk_Window tkwin, /* Window for which event was targeted. */ 243 register XEvent *eventPtr) /* X event: either SelectionClear, 244 * SelectionRequest, or SelectionNotify. */ 245{ 246 if (eventPtr->type == SelectionClear) { 247 TkSelClearSelection(tkwin, eventPtr); 248 } 249} 250 251/* 252 *---------------------------------------------------------------------- 253 * 254 * TkSelPropProc -- 255 * 256 * This procedure is invoked when property-change events 257 * occur on windows not known to the toolkit. This is a stub 258 * function under Windows. 259 * 260 * Results: 261 * None. 262 * 263 * Side effects: 264 * None. 265 * 266 *---------------------------------------------------------------------- 267 */ 268 269void 270TkSelPropProc( 271 register XEvent *eventPtr) /* X PropertyChange event. */ 272{ 273} 274 275/* 276 *---------------------------------------------------------------------- 277 * 278 * TkSuspendClipboard -- 279 * 280 * Handle clipboard conversion as required by the suppend event. 281 * This function is also called on exit. 282 * 283 * Results: 284 * None. 285 * 286 * Side effects: 287 * The local scrap is moved to the global scrap. 288 * 289 *---------------------------------------------------------------------- 290 */ 291 292void 293TkSuspendClipboard(void) 294{ 295 TkClipboardTarget *targetPtr; 296 TkClipboardBuffer *cbPtr; 297 TkDisplay *dispPtr; 298 char *buffer, *p, *endPtr, *buffPtr; 299 long length; 300 ScrapRef scrapRef; 301 302 dispPtr = TkGetDisplayList(); 303 if ((dispPtr == NULL) || !dispPtr->clipboardActive) { 304 return; 305 } 306 307 for (targetPtr = dispPtr->clipTargetPtr; targetPtr != NULL; 308 targetPtr = targetPtr->nextPtr) { 309 if (targetPtr->type == XA_STRING) { 310 break; 311 } 312 } 313 if (targetPtr != NULL) { 314 Tcl_DString encodedText, unicodedText; 315 316 length = 0; 317 for (cbPtr = targetPtr->firstBufferPtr; cbPtr != NULL; 318 cbPtr = cbPtr->nextPtr) { 319 length += cbPtr->length; 320 } 321 322 buffer = ckalloc(length); 323 buffPtr = buffer; 324 for (cbPtr = targetPtr->firstBufferPtr; cbPtr != NULL; 325 cbPtr = cbPtr->nextPtr) { 326 for (p = cbPtr->buffer, endPtr = p + cbPtr->length; 327 p < endPtr; p++) { 328 if (*p == '\n') { 329 *buffPtr++ = '\r'; 330 } else { 331 *buffPtr++ = *p; 332 } 333 } 334 } 335 336 ClearCurrentScrap(); 337 GetCurrentScrap(&scrapRef); 338 Tcl_UtfToExternalDString(TkMacOSXCarbonEncoding, buffer, length, 339 &encodedText); 340 PutScrapFlavor(scrapRef, 'TEXT', 0, Tcl_DStringLength(&encodedText), 341 Tcl_DStringValue(&encodedText)); 342 Tcl_DStringFree(&encodedText); 343 344 /* 345 * Also put unicode data on scrap. 346 */ 347 348 Tcl_DStringInit(&unicodedText); 349 Tcl_UtfToUniCharDString(buffer, length, &unicodedText); 350 PutScrapFlavor(scrapRef, kScrapFlavorTypeUnicode, 0, 351 Tcl_DStringLength(&unicodedText), 352 Tcl_DStringValue(&unicodedText)); 353 Tcl_DStringFree(&unicodedText); 354 355 ckfree(buffer); 356 } 357 358 /* 359 * The system now owns the scrap. We tell Tk that it has 360 * lost the selection so that it will look for it the next time 361 * it needs it. (Window list NULL if quiting.) 362 */ 363 364 if (TkGetMainInfoList() != NULL) { 365 Tk_ClearSelection((Tk_Window) TkGetMainInfoList()->winPtr, 366 Tk_InternAtom((Tk_Window) TkGetMainInfoList()->winPtr, 367 "CLIPBOARD")); 368 } 369 370 return; 371} 372