4 * This file contains functions for managing the clipboard.
6 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
7 * Copyright (c) 1998-2000 by Scriptics Corporation.
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 #include <shlobj.h> /* for DROPFILES */
17 static void UpdateClipboard(HWND hwnd);
20 *----------------------------------------------------------------------
22 * TkSelGetSelection --
24 * Retrieve the specified selection from another process. For now, only
25 * fetching XA_STRING from CLIPBOARD is supported. Eventually other types
29 * The return value is a standard Tcl return value. If an error occurs
30 * (such as no selection exists) then an error message is left in the
36 *----------------------------------------------------------------------
41 Tcl_Interp *interp, /* Interpreter to use for reporting errors. */
42 Tk_Window tkwin, /* Window on whose behalf to retrieve the
43 * selection (determines display from which to
45 Atom selection, /* Selection to retrieve. */
46 Atom target, /* Desired form in which selection is to be
48 Tk_GetSelProc *proc, /* Procedure to call to process the selection,
49 * once it has been retrieved. */
50 ClientData clientData) /* Arbitrary value to pass to proc. */
55 Tcl_Encoding encoding;
56 int result, locale, noBackslash = 0;
58 if (!OpenClipboard(NULL)) {
59 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
60 "clipboard cannot be opened, another application grabbed it"));
61 Tcl_SetErrorCode(interp, "TK", "CLIPBOARD", "BUSY", NULL);
64 if ((selection != Tk_InternAtom(tkwin, "CLIPBOARD"))
65 || (target != XA_STRING)) {
70 * Attempt to get the data in Unicode form if available as this is less
75 if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
76 handle = GetClipboardData(CF_UNICODETEXT);
81 data = (char *)GlobalLock(handle);
83 Tcl_WCharToUtfDString((WCHAR *)data, wcslen((WCHAR *)data), &ds);
85 } else if (IsClipboardFormatAvailable(CF_TEXT)) {
87 * Determine the encoding to use to convert this text.
90 if (IsClipboardFormatAvailable(CF_LOCALE)) {
91 handle = GetClipboardData(CF_LOCALE);
98 * Get the locale identifier, determine the proper code page to
99 * use, and find the corresponding encoding.
102 Tcl_DStringInit(&ds);
103 Tcl_DStringAppend(&ds, "cp######", -1);
104 data = (char *)GlobalLock(handle);
107 * Even though the documentation claims that GetLocaleInfo expects
108 * an LCID, on Windows 9x it really seems to expect a LanguageID.
111 locale = LANGIDFROMLCID(*((int*)data));
112 GetLocaleInfoA(locale, LOCALE_IDEFAULTANSICODEPAGE,
113 Tcl_DStringValue(&ds)+2, Tcl_DStringLength(&ds)-2);
114 GlobalUnlock(handle);
116 encoding = Tcl_GetEncoding(NULL, Tcl_DStringValue(&ds));
117 Tcl_DStringFree(&ds);
123 * Fetch the text and convert it to UTF.
126 handle = GetClipboardData(CF_TEXT);
129 Tcl_FreeEncoding(encoding);
134 data = (char *)GlobalLock(handle);
135 Tcl_ExternalToUtfDString(encoding, data, -1, &ds);
136 GlobalUnlock(handle);
138 Tcl_FreeEncoding(encoding);
140 } else if (IsClipboardFormatAvailable(CF_HDROP)) {
143 handle = GetClipboardData(CF_HDROP);
148 Tcl_DStringInit(&ds);
149 drop = (DROPFILES *) GlobalLock(handle);
151 WCHAR *fname = (WCHAR *) ((char *) drop + drop->pFiles);
156 while (*fname != 0) {
158 Tcl_DStringAppend(&ds, "\n", 1);
161 Tcl_DStringInit(&dsTmp);
162 Tcl_WCharToUtfDString(fname, len, &dsTmp);
163 Tcl_DStringAppend(&ds, Tcl_DStringValue(&dsTmp),
164 Tcl_DStringLength(&dsTmp));
165 Tcl_DStringFree(&dsTmp);
169 noBackslash = (count > 0);
171 GlobalUnlock(handle);
178 * Translate CR/LF to LF.
181 data = destPtr = Tcl_DStringValue(&ds);
183 if (data[0] == '\r' && data[1] == '\n') {
185 } else if (noBackslash && data[0] == '\\') {
189 *destPtr++ = *data++;
195 * Pass the data off to the selection procedure.
198 result = proc(clientData, interp, Tcl_DStringValue(&ds));
199 Tcl_DStringFree(&ds);
204 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
205 "%s selection doesn't exist or form \"%s\" not defined",
206 Tk_GetAtomName(tkwin, selection), Tk_GetAtomName(tkwin, target)));
207 Tcl_SetErrorCode(interp, "TK", "SELECTION", "EXISTS", NULL);
212 *----------------------------------------------------------------------
214 * TkSetSelectionOwner --
216 * This function claims ownership of the specified selection. If the
217 * selection is CLIPBOARD, then we empty the system clipboard.
223 * Empties the system clipboard, and claims ownership.
225 *----------------------------------------------------------------------
235 HWND hwnd = owner ? TkWinGetHWND(owner) : NULL;
241 * This is a gross hack because the Tk_InternAtom interface is broken. It
242 * expects a Tk_Window, even though it only needs a Tk_Display.
245 tkwin = (Tk_Window) TkGetMainInfoList()->winPtr;
247 if (selection == Tk_InternAtom(tkwin, "CLIPBOARD")) {
249 * Only claim and empty the clipboard if we aren't already the owner
253 if (GetClipboardOwner() != hwnd) {
254 UpdateClipboard(hwnd);
261 *----------------------------------------------------------------------
263 * TkWinClipboardRender --
265 * This function supplies the contents of the clipboard in response to a
266 * WM_RENDERFORMAT message.
272 * Sets the contents of the clipboard.
274 *----------------------------------------------------------------------
278 TkWinClipboardRender(
282 TkClipboardTarget *targetPtr;
283 TkClipboardBuffer *cbPtr;
285 char *buffer, *p, *rawText, *endPtr;
290 for (targetPtr = dispPtr->clipTargetPtr; targetPtr != NULL;
291 targetPtr = targetPtr->nextPtr) {
292 if (targetPtr->type == XA_STRING) {
298 * Count the number of newlines so we can add space for them in the
303 if (targetPtr != NULL) {
304 for (cbPtr = targetPtr->firstBufferPtr; cbPtr != NULL;
305 cbPtr = cbPtr->nextPtr) {
306 length += cbPtr->length;
307 for (p = cbPtr->buffer, endPtr = p + cbPtr->length;
317 * Copy the data and change EOL characters.
320 buffer = rawText = (char *)ckalloc(length + 1);
321 if (targetPtr != NULL) {
322 for (cbPtr = targetPtr->firstBufferPtr; cbPtr != NULL;
323 cbPtr = cbPtr->nextPtr) {
324 for (p = cbPtr->buffer, endPtr = p + cbPtr->length;
335 Tcl_DStringInit(&ds);
336 Tcl_UtfToWCharDString(rawText, -1, &ds);
338 handle = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE,
339 Tcl_DStringLength(&ds) + 2);
341 Tcl_DStringFree(&ds);
344 buffer = (char *)GlobalLock(handle);
345 memcpy(buffer, Tcl_DStringValue(&ds),
346 Tcl_DStringLength(&ds) + 2);
347 GlobalUnlock(handle);
348 Tcl_DStringFree(&ds);
349 SetClipboardData(CF_UNICODETEXT, handle);
353 *----------------------------------------------------------------------
355 * TkSelUpdateClipboard --
357 * This function is called to force the clipboard to be updated after new
364 * Clears the current contents of the clipboard.
366 *----------------------------------------------------------------------
370 TkSelUpdateClipboard(
372 TkClipboardTarget *targetPtr)
374 HWND hwnd = TkWinGetHWND(winPtr->window);
377 UpdateClipboard(hwnd);
381 *----------------------------------------------------------------------
385 * Take ownership of the clipboard, clear it, and indicate to the system
386 * the supported formats.
394 *----------------------------------------------------------------------
401 TkWinUpdatingClipboard(TRUE);
405 SetClipboardData(CF_UNICODETEXT, NULL);
407 TkWinUpdatingClipboard(FALSE);
411 *--------------------------------------------------------------
415 * This procedure is invoked whenever a selection-related event occurs.
421 * Lots: depends on the type of event.
423 *--------------------------------------------------------------
428 Tk_Window tkwin, /* Window for which event was targeted. */
429 XEvent *eventPtr) /* X event: either SelectionClear,
430 * SelectionRequest, or SelectionNotify. */
432 if (eventPtr->type == SelectionClear) {
433 TkSelClearSelection(tkwin, eventPtr);
438 *----------------------------------------------------------------------
442 * This procedure is invoked when property-change events occur on windows
443 * not known to the toolkit. This is a stub function under Windows.
451 *----------------------------------------------------------------------
456 XEvent *eventPtr) /* X PropertyChange event. */