#include "CtrlCore.h" #ifdef GUI_X11 namespace Upp { #define LLOG(x) // LOG(x) Index Ctrl::sel_formats; Ptr Ctrl::sel_ctrl; void Ctrl::SetSelectionSource(const char *fmts) { GuiLock __; LLOG("SetSelectionSource " << UPP::Name(this) << ": " << fmts); sel_formats = Split(fmts, ';'); sel_ctrl = this; XSetSelectionOwner(Xdisplay, XAtom("PRIMARY"), xclipboard().win, CurrentTime); } Ctrl::Xclipboard::Xclipboard() { GuiLock __; XSetWindowAttributes swa; win = XCreateWindow(Xdisplay, RootWindow(Xdisplay, Xscreenno), 0, 0, 10, 10, 0, CopyFromParent, InputOnly, CopyFromParent, 0, &swa); XSelectInput(Xdisplay, win, PropertyChangeMask); } Ctrl::Xclipboard::~Xclipboard() { GuiLock __; XDestroyWindow(Xdisplay, win); } void Ctrl::Xclipboard::Write(int fmt, const ClipData& _data) { GuiLock __; LLOG("SetSelectionOwner " << XAtomName(fmt)); data.GetAdd(fmt) = _data; XSetSelectionOwner(Xdisplay, XAtom("CLIPBOARD"), win, CurrentTime); } void Ctrl::Xclipboard::Request(XSelectionRequestEvent *se) { GuiLock __; LLOG("Request " << XAtomName(se->target)); XEvent e; e.xselection.type = SelectionNotify; e.xselection.display = Xdisplay; e.xselection.requestor = se->requestor; e.xselection.selection = se->selection; e.xselection.target = se->target; e.xselection.time = se->time; e.xselection.property = se->property; if(se->target == XAtom("TARGETS")) { LLOG("Request targets:"); if(se->selection == XAtom("PRIMARY")) { Buffer x(sel_formats.GetCount()); for(int i = 0; i < sel_formats.GetCount(); i++) { x[i] = XAtom(sel_formats[i]); LLOG('\t' << sel_formats[i]); } XChangeProperty(Xdisplay, se->requestor, se->property, XAtom("ATOM"), 32, 0, (unsigned char*)~x, sel_formats.GetCount()); } else { Buffer x(data.GetCount()); for(int i = 0; i < data.GetCount(); i++) { x[i] = data.GetKey(i); LLOG('\t' << XAtomName(x[i])); } XChangeProperty(Xdisplay, se->requestor, se->property, XAtom("ATOM"), 32, 0, (unsigned char*)~x, data.GetCount()); } } else { if(se->selection == XAtom("PRIMARY")) { LLOG("Request PRIMARY data " << XAtomName(se->target)); String fmt = XAtomName(se->target); int i = sel_formats.Find(fmt); if(i >= 0 && sel_ctrl) { String d = sel_ctrl->GetSelectionData(fmt); XChangeProperty(Xdisplay, se->requestor, se->property, se->target, 8, PropModeReplace, d, d.GetLength()); } else e.xselection.property = None; } else { LLOG("Request CLIPBOARD data " << XAtomName(se->target)); int i = data.Find(se->target); if(i >= 0) { String d = data[i].Render(); XChangeProperty(Xdisplay, se->requestor, se->property, se->target, 8, PropModeReplace, d, d.GetLength()); } else e.xselection.property = None; } } XSendEvent(Xdisplay, se->requestor, XFalse, 0, &e); } String Ctrl::Xclipboard::Read(int fmt, int selection, int property) { GuiLock __; if(data.GetCount() && (dword)selection == XAtom("CLIPBOARD")) { int q = data.Find(fmt); return q >= 0 ? data[q].Render() : String(); } if(sel_ctrl && (dword)selection == XAtom("PRIMARY")) return sel_ctrl->GetSelectionData(XAtomName(fmt)); XConvertSelection(Xdisplay, selection, fmt, property, win, CurrentTime); XFlush(Xdisplay); XEvent event; for(int i = 0; i < 20; i++) { if(XCheckTypedWindowEvent(Xdisplay, win, SelectionNotify, &event)) { if(event.xselection.property != None) { XSync(Xdisplay, false); return ReadPropertyData(win, event.xselection.property); } return Null; } if(XCheckTypedWindowEvent(Xdisplay, win, SelectionRequest, &event) && event.xselectionrequest.owner == win) Request(&event.xselectionrequest); if(XCheckTypedWindowEvent(Xdisplay, win, SelectionClear, &event) && event.xselectionclear.window == win) { if(event.xselectionclear.selection == XAtom("CLIPBOARD")) Clear(); if(event.xselectionclear.selection == XAtom("PRIMARY")) { sel_ctrl = NULL; sel_formats.Clear(); } } Sleep(10); } return Null; } Ctrl::Xclipboard& Ctrl::xclipboard() { static Xclipboard xc; return xc; } void ClearClipboard() { GuiLock __; Ctrl::xclipboard().Clear(); } void AppendClipboard(const char *format, const Value& data, String (*render)(const Value& data)) { GuiLock __; Vector s = Split(format, ';'); for(int i = 0; i < s.GetCount(); i++) Ctrl::xclipboard().Write(XAtom(s[i]), ClipData(data, render)); } String sRawClipData(const Value& data); void AppendClipboard(const char *fmt, const String& data) { GuiLock __; AppendClipboard(fmt, data, sRawClipData); } String ReadClipboard(const char *fmt) { GuiLock __; return Ctrl::xclipboard().Read(XAtom(fmt), XAtom("CLIPBOARD"), XAtom("CLIPDATA")); } void AppendClipboardText(const String& s) { GuiLock __; AppendClipboard("STRING", s); } String ReadClipboardText() { GuiLock __; return ReadClipboard("STRING"); } void AppendClipboardUnicodeText(const WString& s) { GuiLock __; AppendClipboard("UTF8_STRING", ToUtf8(s)); } WString ReadClipboardUnicodeText() { GuiLock __; return FromUtf8(ReadClipboard("UTF8_STRING")); } bool Ctrl::Xclipboard::IsAvailable(int fmt, const char *type) { GuiLock __; if(data.GetCount()) return data.Find(fmt) >= 0; String formats = Read(XAtom("TARGETS"), XAtom(type), XAtom("CLIPDATA")); int c = formats.GetCount() / sizeof(Atom); const Atom *m = (Atom *) ~formats; for(int i = 0; i < c; i++) { if(m[i] == (dword)fmt) return true; } return false; } bool Ctrl::ClipHas(int type, const char *fmt) { GuiLock __; LLOG("ClipHas " << type << ": " << fmt); if(strcmp(fmt, "files") == 0) fmt = "text/uri-list"; if(type == 0) return Ctrl::xclipboard().IsAvailable(XAtom(fmt), "CLIPBOARD"); if(type == 2) { if(sel_ctrl) return sel_formats.Find(fmt) >= 0; return Ctrl::xclipboard().IsAvailable(XAtom(fmt), "PRIMARY"); } return drop_formats.Find(fmt) >= 0; } String DnDGetData(const String& f); String Ctrl::ClipGet(int type, const char *fmt) { GuiLock __; LLOG("ClipGet " << type << ": " << fmt); if(strcmp(fmt, "files") == 0) fmt = "text/uri-list"; if(type && GetDragAndDropSource()) return DnDGetData(fmt); return Ctrl::xclipboard().Read( XAtom(fmt), XAtom(type == 2 ? "PRIMARY" : type == 1 ? "XdndSelection" : "CLIPBOARD"), type == 1 ? XA_SECONDARY : XAtom("CLIPDATA") ); } const char *ClipFmtsText() { return "STRING;UTF8_STRING;text/plain;text/unicode"; } String GetString(PasteClip& clip) { GuiLock __; if(clip.Accept("STRING") || clip.Accept("text/plain")) return ~clip; if(clip.Accept("UTF8_STRING")) return FromUtf8(~clip).ToString(); if(clip.Accept("text/unicode")) return Unicode__(~clip).ToString(); return Null; } WString GetWString(PasteClip& clip) { GuiLock __; if(clip.Accept("UTF8_STRING")) return FromUtf8(~clip); if(clip.Accept("text/unicode")) return Unicode__(~clip); if(clip.Accept("STRING") || clip.Accept("text/plain")) return ToUnicode(~clip, CHARSET_ISO8859_1); return Null; } String GetTextClip(const WString& text, const String& fmt) { GuiLock __; if(fmt == "STRING" || fmt == "text/plain") return text.ToString(); if(fmt == "UTF8_STRING") return ToUtf8(text); if(fmt == "text/unicode") return Unicode__(text); return Null; } String GetTextClip(const String& text, const String& fmt) { GuiLock __; if(fmt == "STRING" || fmt == "text/plain") return text; if(fmt == "UTF8_STRING") return ToUtf8(text.ToWString()); if(fmt == "text/unicode") return Unicode__(text.ToWString()); return Null; } bool AcceptText(PasteClip& clip) { GuiLock __; return clip.Accept(ClipFmtsText()); } void Append(VectorMap& data, const String& text) // optimize { GuiLock __; data.GetAdd("STRING", text); data.GetAdd("text/plain", text); data.GetAdd("UTF8_STRING", ToUtf8(text.ToWString())); data.GetAdd("text/unicode", Unicode__(text.ToWString())); } void Append(VectorMap& data, const WString& text) // optimize { GuiLock __; data.GetAdd("STRING", text.ToString()); data.GetAdd("text/plain", text.ToString()); data.GetAdd("UTF8_STRING", ToUtf8(text)); data.GetAdd("text/unicode", Unicode__(text)); } bool IsClipboardAvailable(const char *fmt) { GuiLock __; return Ctrl::xclipboard().IsAvailable(XAtom(fmt), "CLIPBOARD"); } bool IsClipboardAvailableText() { GuiLock __; return IsClipboardAvailable("STRING") || IsClipboardAvailable("UTF8_STRING") || IsClipboardAvailable("text/plain") || IsClipboardAvailable("text/unicode"); } bool AcceptFiles(PasteClip& clip) { GuiLock __; return clip.Accept("text/uri-list"); } bool IsAvailableFiles(PasteClip& clip) { GuiLock __; return clip.IsAvailable("text/uri-list"); } int JustLf(int c) { return (byte)c >= 32 || c == '\n' ? c : 0; } Vector GetClipFiles(const String& data) { Vector r; Vector f = Split(Filter(data, JustLf), '\n'); for(int i = 0; i < f.GetCount(); i++) if(f[i].StartsWith("file://")) r.Add(UrlDecode(f[i].Mid(7))); return r; } Vector GetFiles(PasteClip& clip) { GuiLock __; Vector r; if(clip.Accept("text/uri-list")) { String txt = clip; r = GetClipFiles(txt); } return r; } void AppendFiles(VectorMap& data, const Vector& files) { GuiLock __; if(files.GetCount() == 0) return; String h; for(int i = 0; i < files.GetCount(); i++) h << "file://" << UrlEncode(files[i]) << '\n'; data.GetAdd("text/uri-list") = h; } bool PasteClip::IsAvailable(const char *fmt) const { return Ctrl::ClipHas(type, fmt); } String PasteClip::Get(const char *fmt) const { return Ctrl::ClipGet(type, fmt); } void PasteClip::GuiPlatformConstruct() { type = 0; } } #endif