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