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