0
|
1 // Written in the D programming language
|
|
2 // www.digitalmars.com/d/
|
|
3
|
|
4 /*
|
|
5 * The contents of this file are subject to the Mozilla Public License Version
|
|
6 * 1.1 (the "License"); you may not use this file except in compliance with
|
|
7 * the License. You may obtain a copy of the License at
|
|
8 * http://www.mozilla.org/MPL/
|
|
9 *
|
|
10 * Software distributed under the License is distributed on an "AS IS" basis,
|
|
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
12 * for the specific language governing rights and limitations under the
|
|
13 * License.
|
|
14 *
|
|
15 * The Original Code is the Dynamin library.
|
|
16 *
|
|
17 * The Initial Developer of the Original Code is Jordan Miner.
|
|
18 * Portions created by the Initial Developer are Copyright (C) 2007-2009
|
|
19 * the Initial Developer. All Rights Reserved.
|
|
20 *
|
|
21 * Contributor(s):
|
|
22 * Jordan Miner <jminer7@gmail.com>
|
|
23 *
|
|
24 */
|
|
25
|
|
26 module dynamin.gui.x_clipboard;
|
|
27
|
|
28 public import dynamin.core.string;
|
|
29 public import dynamin.core.environment;
|
|
30 public import dynamin.gui.x_window;
|
|
31
|
|
32 // TODO: get notified of selection changes by using the XFixes extension
|
|
33 // TODO: use the ClipboardManager to ensure that the clipboard data stays after
|
|
34 // the program is closed
|
|
35
|
|
36 /* Fairly obvious, but:
|
|
37 selecting but with no explicit copy should only set PRIMARY,
|
|
38 never CLIPBOARD
|
|
39
|
|
40 middle mouse button should paste PRIMARY, never CLIPBOARD
|
|
41
|
|
42 explicit cut/copy commands (i.e. menu items, toolbar buttons)
|
|
43 should always set CLIPBOARD to the currently-selected data (i.e. conceptually copy PRIMARY to CLIPBOARD)
|
|
44
|
|
45 explicit paste commands should paste CLIPBOARD, not PRIMARY
|
|
46
|
|
47 possibly contradicting the ICCCM, clients don't need to support
|
|
48 SECONDARY, though if anyone can figure out what it's good for they should feel free to use it for that
|
|
49 */
|
|
50 extern(C) XBool isRequestOrNotify(XDisplay* d, XEvent* e, XPointer arg) {
|
|
51 // either
|
|
52 // - SelectionRequest & the msgWin is the owner
|
|
53 // - SelectionNotify & another program is owner is giving data
|
|
54 return (e.type == SelectionRequest || e.type == SelectionNotify) &&
|
|
55 e.xany.window == msgWin;
|
|
56
|
|
57 }
|
|
58
|
|
59 string backend_getSelText(XAtom sel, ref ClipboardData data) {
|
|
60 XConvertSelection(display, sel, XA.UTF8_STRING, XA.DYNAMIN_SELECTION, msgWin, CurrentTime);
|
|
61 XSync(display, false);
|
|
62 auto start = Environment.runningTime;
|
|
63 XEvent ev;
|
|
64 while(true) {
|
|
65 // don't wait more than a second
|
|
66 if(Environment.runningTime - start > 1000)
|
|
67 return null;
|
|
68 if(!XCheckIfEvent(display, &ev, &isRequestOrNotify, null))
|
|
69 continue;
|
|
70 if(ev.type == SelectionRequest)
|
|
71 return data.data[0..data.length];
|
|
72 // must be SelectionNotify past here
|
|
73
|
|
74 auto selEv = &ev.xselection;
|
|
75 if(selEv.property == None)
|
|
76 return null;
|
|
77
|
|
78 int count;
|
|
79 char* propData = cast(char*)getXWindowProperty(display, msgWin,
|
|
80 selEv.property, &count);
|
|
81 scope(exit) XFree(propData);
|
|
82 XDeleteProperty(display, msgWin, selEv.property);
|
|
83
|
|
84 string str = new char[count];
|
|
85 str[] = propData[0..count];
|
|
86 return str;
|
|
87 }
|
|
88 }
|
|
89 struct ClipboardData {
|
|
90 XAtom target;
|
|
91 char* data;
|
|
92 uint length; // number of bytes in data
|
|
93 }
|
|
94 // always called from the event thread...don't have to avoid static data
|
|
95 void backend_setSelText(XAtom sel, string text, ref ClipboardData data) {
|
|
96 XSetSelectionOwner(display, sel, msgWin, CurrentTime);
|
|
97 data.target = XA.UTF8_STRING;
|
|
98 data.data = text.ptr;
|
|
99 data.length = text.length;
|
|
100
|
|
101 XConvertSelection(display, XA.CLIPBOARD_MANAGER, XA.SAVE_TARGETS, None, msgWin, CurrentTime);
|
|
102 }
|
|
103
|
|
104 template ClipboardBackend() {
|
|
105 ClipboardData data; // make array when supporting multiple types (PNG & BMP)
|
|
106 void backend_setText(string text) {
|
|
107 backend_setSelText(XA.CLIPBOARD, text, data);
|
|
108 }
|
|
109 string backend_getText() {
|
|
110 return backend_getSelText(XA.CLIPBOARD, data);
|
|
111 }
|
|
112 bool backend_containsText() {
|
|
113 return backend_getText() != null;
|
|
114 }
|
|
115 }
|
|
116
|
|
117 template SelectionBackend() {
|
|
118 ClipboardData data; // make array when supporting multiple types (PNG & BMP)
|
|
119 void backend_setText(string text) {
|
|
120 backend_setSelText(XA.PRIMARY, text, data);
|
|
121 }
|
|
122 string backend_getText() {
|
|
123 return backend_getSelText(XA.PRIMARY, data);
|
|
124 }
|
|
125 bool backend_containsText() {
|
|
126 return backend_getText() != null;
|
|
127 }
|
|
128 }
|
|
129
|