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-2009, Apple Inc.
8 * Copyright (c) 2006-2009 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$
14 */
15
16#include "tkMacOSXPrivate.h"
17#include "tkSelect.h"
18
19static NSInteger changeCount = -1;
20static Tk_Window clipboardOwner = NULL;
21
22#pragma mark TKApplication(TKClipboard)
23
24@implementation TKApplication(TKClipboard)
25- (void)tkProvidePasteboard:(TkDisplay *)dispPtr
26	pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type {
27    NSMutableString *string = [NSMutableString new];
28
29    if (dispPtr && dispPtr->clipboardActive &&
30	    [type isEqualToString:NSStringPboardType]) {
31	for (TkClipboardTarget *targetPtr = dispPtr->clipTargetPtr; targetPtr;
32		targetPtr = targetPtr->nextPtr) {
33	    if (targetPtr->type == XA_STRING ||
34		    targetPtr->type == dispPtr->utf8Atom) {
35		for (TkClipboardBuffer *cbPtr = targetPtr->firstBufferPtr;
36			cbPtr; cbPtr = cbPtr->nextPtr) {
37		    NSString *s = [[NSString alloc] initWithBytesNoCopy:
38			    cbPtr->buffer length:cbPtr->length
39			    encoding:NSUTF8StringEncoding freeWhenDone:NO];
40		    [string appendString:s];
41		    [s release];
42		}
43		break;
44	    }
45	}
46    }
47    [sender setString:string forType:type];
48    [string release];
49}
50- (void)tkProvidePasteboard:(TkDisplay *)dispPtr {
51    if (dispPtr && dispPtr->clipboardActive) {
52	[self tkProvidePasteboard:dispPtr
53		pasteboard:[NSPasteboard generalPasteboard]
54		provideDataForType:NSStringPboardType];
55    }
56}
57- (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type {
58    [self tkProvidePasteboard:TkGetDisplayList() pasteboard:sender
59	    provideDataForType:type];
60}
61- (void)tkCheckPasteboard {
62    if (clipboardOwner && [[NSPasteboard generalPasteboard] changeCount] !=
63	    changeCount) {
64	TkDisplay *dispPtr = TkGetDisplayList();
65
66	if (dispPtr) {
67	    XEvent event;
68
69	    event.xany.type = SelectionClear;
70	    event.xany.serial = NextRequest(Tk_Display(clipboardOwner));
71	    event.xany.send_event = False;
72	    event.xany.window = Tk_WindowId(clipboardOwner);
73	    event.xany.display = Tk_Display(clipboardOwner);
74	    event.xselectionclear.selection = dispPtr->clipboardAtom;
75	    Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
76	}
77	clipboardOwner = NULL;
78    }
79}
80@end
81
82#pragma mark -
83
84/*
85 *----------------------------------------------------------------------
86 *
87 * TkSelGetSelection --
88 *
89 *	Retrieve the specified selection from another process. For now, only
90 *	fetching XA_STRING from CLIPBOARD is supported. Eventually other types
91 *	should be allowed.
92 *
93 * Results:
94 *	The return value is a standard Tcl return value. If an error occurs
95 *	(such as no selection exists) then an error message is left in the
96 *	interp's result.
97 *
98 * Side effects:
99 *	None.
100 *
101 *----------------------------------------------------------------------
102 */
103
104int
105TkSelGetSelection(
106    Tcl_Interp *interp,		/* Interpreter to use for reporting errors. */
107    Tk_Window tkwin,		/* Window on whose behalf to retrieve the
108				 * selection (determines display from which to
109				 * retrieve). */
110    Atom selection,		/* Selection to retrieve. */
111    Atom target,		/* Desired form in which selection is to be
112				 * returned. */
113    Tk_GetSelProc *proc,	/* Procedure to call to process the selection,
114				 * once it has been retrieved. */
115    ClientData clientData)	/* Arbitrary value to pass to proc. */
116{
117    int result = TCL_ERROR;
118    TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
119
120    if (dispPtr && selection == dispPtr->clipboardAtom && (target == XA_STRING
121	    || target == dispPtr->utf8Atom)) {
122	NSString *string = nil;
123	NSPasteboard *pb = [NSPasteboard generalPasteboard];
124	NSString *type = [pb availableTypeFromArray:[NSArray arrayWithObject:
125		NSStringPboardType]];
126
127	if (type) {
128	    string = [pb stringForType:type];
129	}
130	result = proc(clientData, interp, string ? (char*)[string UTF8String]
131		: "");
132    } else {
133	Tcl_AppendResult(interp, Tk_GetAtomName(tkwin, selection),
134		" selection doesn't exist or form \"",
135		Tk_GetAtomName(tkwin, target), "\" not defined", NULL);
136    }
137    return result;
138}
139
140/*
141 *----------------------------------------------------------------------
142 *
143 * XSetSelectionOwner --
144 *
145 *	This function claims ownership of the specified selection. If the
146 *	selection is CLIPBOARD, then we empty the system clipboard.
147 *
148 * Results:
149 *	None.
150 *
151 * Side effects:
152 *	None.
153 *
154 *----------------------------------------------------------------------
155 */
156
157void
158XSetSelectionOwner(
159    Display *display,		/* X Display. */
160    Atom selection,		/* What selection to own. */
161    Window owner,		/* Window to be the owner. */
162    Time time)			/* The current time? */
163{
164    TkDisplay *dispPtr = TkGetDisplayList();
165
166    if (dispPtr && selection == dispPtr->clipboardAtom) {
167	clipboardOwner = owner ? Tk_IdToWindow(display, owner) : NULL;
168	if (!dispPtr->clipboardActive) {
169	    NSPasteboard *pb = [NSPasteboard generalPasteboard];
170	    changeCount = [pb declareTypes:[NSArray array] owner:NSApp];
171	}
172    }
173}
174
175/*
176 *----------------------------------------------------------------------
177 *
178 * TkMacOSXSelDeadWindow --
179 *
180 *	This function is invoked just before a TkWindow is deleted. It
181 *	performs selection-related cleanup.
182 *
183 * Results:
184 *	None.
185 *
186 * Side effects:
187 *	clipboardOwner is cleared.
188 *
189 *----------------------------------------------------------------------
190 */
191
192void
193TkMacOSXSelDeadWindow(
194    TkWindow *winPtr)
195{
196    if (winPtr && winPtr == (TkWindow *)clipboardOwner) {
197	clipboardOwner = NULL;
198    }
199}
200
201/*
202 *----------------------------------------------------------------------
203 *
204 * TkSelUpdateClipboard --
205 *
206 *	This function is called to force the clipboard to be updated after new
207 *	data is added.
208 *
209 * Results:
210 *	None.
211 *
212 * Side effects:
213 *	None.
214 *
215 *----------------------------------------------------------------------
216 */
217
218void
219TkSelUpdateClipboard(
220    TkWindow *winPtr,		/* Window associated with clipboard. */
221    TkClipboardTarget *targetPtr)
222				/* Info about the content. */
223{
224    NSPasteboard *pb = [NSPasteboard generalPasteboard];
225    changeCount = [pb addTypes:[NSArray arrayWithObject:NSStringPboardType]
226	    owner:NSApp];
227}
228
229/*
230 *--------------------------------------------------------------
231 *
232 * TkSelEventProc --
233 *
234 *	This procedure is invoked whenever a selection-related event occurs.
235 *
236 * Results:
237 *	None.
238 *
239 * Side effects:
240 *	Lots: depends on the type of event.
241 *
242 *--------------------------------------------------------------
243 */
244
245void
246TkSelEventProc(
247    Tk_Window tkwin,		/* Window for which event was targeted. */
248    register XEvent *eventPtr)	/* X event: either SelectionClear,
249				 * SelectionRequest, or SelectionNotify. */
250{
251    if (eventPtr->type == SelectionClear) {
252	clipboardOwner = NULL;
253	TkSelClearSelection(tkwin, eventPtr);
254    }
255}
256
257/*
258 *----------------------------------------------------------------------
259 *
260 * TkSelPropProc --
261 *
262 *	This procedure is invoked when property-change events occur on windows
263 *	not known to the toolkit. This is a stub function under Windows.
264 *
265 * Results:
266 *	None.
267 *
268 * Side effects:
269 *	None.
270 *
271 *----------------------------------------------------------------------
272 */
273
274void
275TkSelPropProc(
276    register XEvent *eventPtr)	/* X PropertyChange event. */
277{
278}
279
280/*
281 *----------------------------------------------------------------------
282 *
283 * TkSuspendClipboard --
284 *
285 *	Handle clipboard conversion as required by the suppend event.
286 *
287 * Results:
288 *	None.
289 *
290 * Side effects:
291 *	The local scrap is moved to the global scrap.
292 *
293 *----------------------------------------------------------------------
294 */
295
296void
297TkSuspendClipboard(void)
298{
299    changeCount = [[NSPasteboard generalPasteboard] changeCount];
300}
301
302/*
303 * Local Variables:
304 * mode: c
305 * c-basic-offset: 4
306 * fill-column: 79
307 * coding: utf-8
308 * End:
309 */
310