summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorManolo Gouy <Manolo>2014-05-23 16:47:21 +0000
committerManolo Gouy <Manolo>2014-05-23 16:47:21 +0000
commit07dd8ba328117a2599cb39dbaa9f17d4f279f923 (patch)
tree6e4976f79f015d70a9e540b6e66262219fd300a2 /src
parent85af2efe09d6ac88bfc18f8a991ea59af9a5b24b (diff)
Added copy/paste from/to FLTK applications of graphical data.
Added Fl_Image_Surface class to draw into an Fl_Image object. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10159 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src')
-rw-r--r--src/Fl.cxx18
-rw-r--r--src/Fl_Copy_Surface.cxx399
-rw-r--r--src/Fl_Image_Surface.cxx158
-rw-r--r--src/Fl_cocoa.mm223
-rw-r--r--src/Fl_win32.cxx148
-rw-r--r--src/Fl_x.cxx339
-rw-r--r--src/Makefile2
7 files changed, 1177 insertions, 110 deletions
diff --git a/src/Fl.cxx b/src/Fl.cxx
index b49425d38..9d64b95fb 100644
--- a/src/Fl.cxx
+++ b/src/Fl.cxx
@@ -104,6 +104,8 @@ int Fl::damage_,
char *Fl::e_text = (char *)"";
int Fl::e_length;
+const char* Fl::e_clipboard_type = "";
+void * Fl::e_clipboard_data = NULL;
Fl_Event_Dispatch Fl::e_dispatch = 0;
@@ -118,6 +120,9 @@ Fl_Window *Fl::modal_; // topmost modal() window
#endif // FL_DOXYGEN
+char const * const Fl::clipboard_plain_text = "text/plain";
+char const * const Fl::clipboard_image = "image";
+
//
// 'Fl::version()' - Return the API version number...
//
@@ -1635,12 +1640,23 @@ void Fl::selection(Fl_Widget &owner, const char* text, int len) {
/** Backward compatibility only.
This calls Fl::paste(receiver, 0);
- \see Fl::paste(Fl_Widget &receiver, int clipboard)
+ \see Fl::paste(Fl_Widget &receiver, int clipboard, const char* type)
*/
void Fl::paste(Fl_Widget &receiver) {
Fl::paste(receiver, 0);
}
+#if FLTK_ABI_VERSION >= 10303
+#elif !defined(FL_DOXYGEN)
+void Fl::paste(Fl_Widget &receiver, int source)
+{
+ Fl::paste(receiver, source, Fl::clipboard_plain_text);
+}
+void Fl::copy(const char* stuff, int len, int destination) {
+ Fl::copy(stuff, len, destination, Fl::clipboard_plain_text);
+}
+
+#endif
////////////////////////////////////////////////////////////////
#include <FL/fl_draw.H>
diff --git a/src/Fl_Copy_Surface.cxx b/src/Fl_Copy_Surface.cxx
new file mode 100644
index 000000000..206c1e684
--- /dev/null
+++ b/src/Fl_Copy_Surface.cxx
@@ -0,0 +1,399 @@
+//
+// "$Id: Fl_Copy_Surface.cxx 9869 2013-04-09 20:11:28Z greg.ercolano $"
+//
+// Copy-to-clipboard code for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2014 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// http://www.fltk.org/COPYING.php
+//
+// Please report all bugs and problems on the following page:
+//
+// http://www.fltk.org/str.php
+//
+
+#include <FL/Fl_Copy_Surface.H>
+#include <FL/Fl.H>
+
+
+#if defined(__APPLE__)
+#include <ApplicationServices/ApplicationServices.h>
+#if defined(__ppc__)
+#include <QuickTime/QuickTimeComponents.h>
+#endif // __ppc__
+
+Fl_Quartz_Surface_::Fl_Quartz_Surface_(int w, int h) : Fl_System_Printer(), width(w), height(h) {
+}
+
+int Fl_Quartz_Surface_::printable_rect(int *w, int *h) {
+ *w = width;
+ *h = height;
+ return 0;
+}
+
+const char *Fl_Quartz_Surface_::class_id = "Fl_Quartz_Surface_";
+
+#elif defined(WIN32)
+
+Fl_GDI_Surface_::Fl_GDI_Surface_() : Fl_Paged_Device() {
+ driver(new Fl_GDI_Graphics_Driver);
+ depth = 0;
+}
+
+Fl_GDI_Surface_::~Fl_GDI_Surface_() {
+ delete driver();
+}
+
+void Fl_GDI_Surface_::translate(int x, int y) {
+ GetWindowOrgEx(fl_gc, origins+depth);
+ SetWindowOrgEx(fl_gc, origins[depth].x - x, origins[depth].y - y, NULL);
+ if (depth < sizeof(origins)/sizeof(POINT)) depth++;
+ else Fl::warning("Fl_GDI_Surface_: translate stack overflow!");
+}
+
+void Fl_GDI_Surface_::untranslate() {
+ if (depth > 0) depth--;
+ SetWindowOrgEx(fl_gc, origins[depth].x, origins[depth].y, NULL);
+}
+
+const char *Fl_GDI_Surface_::class_id = "Fl_GDI_Surface_";
+
+#endif
+
+
+const char *Fl_Copy_Surface::class_id = "Fl_Copy_Surface";
+
+/** Constructor.
+ \param w and \param h are the width and height of the clipboard surface
+ in pixels where drawing will occur.
+ */
+Fl_Copy_Surface::Fl_Copy_Surface(int w, int h) : Fl_Surface_Device(NULL)
+{
+ width = w;
+ height = h;
+#ifdef __APPLE__
+ helper = new Fl_Quartz_Surface_(width, height);
+ driver(helper->driver());
+ prepare_copy_pdf_and_tiff(w, h);
+ oldgc = fl_gc;
+#elif defined(WIN32)
+ helper = new Fl_GDI_Surface_();
+ driver(helper->driver());
+ oldgc = fl_gc;
+ // exact computation of factor from screen units to EnhMetaFile units (0.01 mm)
+ HDC hdc = GetDC(NULL);
+ int hmm = GetDeviceCaps(hdc, HORZSIZE);
+ int hdots = GetDeviceCaps(hdc, HORZRES);
+ int vmm = GetDeviceCaps(hdc, VERTSIZE);
+ int vdots = GetDeviceCaps(hdc, VERTRES);
+ ReleaseDC(NULL, hdc);
+ float factorw = (100. * hmm) / hdots;
+ float factorh = (100. * vmm) / vdots + 0.5;
+
+ RECT rect; rect.left = 0; rect.top = 0; rect.right = w * factorw; rect.bottom = h * factorh;
+ gc = CreateEnhMetaFile (NULL, NULL, &rect, NULL);
+ if (gc != NULL) {
+ SetTextAlign(gc, TA_BASELINE|TA_LEFT);
+ SetBkMode(gc, TRANSPARENT);
+ }
+#else // Xlib
+ helper = new Fl_Xlib_Surface_();
+ driver(helper->driver());
+ Fl::first_window()->make_current();
+ oldwindow = fl_xid(Fl::first_window());
+ xid = fl_create_offscreen(w,h);
+ Fl_Surface_Device *present_surface = Fl_Surface_Device::surface();
+ set_current();
+ fl_color(FL_WHITE);
+ fl_rectf(0, 0, w, h);
+ present_surface->set_current();
+#endif
+}
+
+/** Destructor.
+ */
+Fl_Copy_Surface::~Fl_Copy_Surface()
+{
+#ifdef __APPLE__
+ complete_copy_pdf_and_tiff();
+ fl_gc = oldgc;
+ delete (Fl_Quartz_Surface_*)helper;
+#elif defined(WIN32)
+ if(oldgc == fl_gc) oldgc = NULL;
+ HENHMETAFILE hmf = CloseEnhMetaFile (gc);
+ if ( hmf != NULL ) {
+ if ( OpenClipboard (NULL) ){
+ EmptyClipboard ();
+ SetClipboardData (CF_ENHMETAFILE, hmf);
+ CloseClipboard ();
+ }
+ DeleteEnhMetaFile(hmf);
+ }
+ DeleteDC(gc);
+ fl_gc = oldgc;
+ delete (Fl_GDI_Surface_*)helper;
+#else // Xlib
+ fl_pop_clip();
+ unsigned char *data = fl_read_image(NULL,0,0,width,height,0);
+ fl_window = oldwindow;
+ _ss->set_current();
+ Fl::copy_image(data,width,height,1);
+ delete[] data;
+ fl_delete_offscreen(xid);
+ delete (Fl_Xlib_Surface_*)helper;
+#endif
+}
+
+/** Copies a widget in the clipboard
+
+ \param widget any FLTK widget (e.g., standard, custom, window, GL view) to copy
+ \param delta_x and \param delta_y give
+ the position in the clipboard of the top-left corner of the widget
+ */
+void Fl_Copy_Surface::draw(Fl_Widget* widget, int delta_x, int delta_y)
+{
+ helper->print_widget(widget, delta_x, delta_y);
+}
+
+void Fl_Copy_Surface::set_current()
+{
+#if defined(__APPLE__) || defined(WIN32)
+ fl_gc = gc;
+ fl_window = (Window)1;
+ Fl_Surface_Device::set_current();
+#else
+ fl_window=xid;
+ _ss = Fl_Surface_Device::surface();
+ Fl_Surface_Device::set_current();
+ fl_push_no_clip();
+#endif
+}
+
+
+#if defined(__APPLE__)
+
+size_t Fl_Copy_Surface::MyPutBytes(void* info, const void* buffer, size_t count)
+ {
+ CFDataAppendBytes ((CFMutableDataRef) info, (const UInt8 *)buffer, count);
+ return count;
+}
+
+void Fl_Copy_Surface::init_PDF_context(int w, int h)
+{
+ CGRect bounds = CGRectMake(0, 0, w, h );
+ pdfdata = CFDataCreateMutable(NULL, 0);
+ CGDataConsumerRef myconsumer;
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1040
+ if(CGDataConsumerCreateWithCFData != NULL) {
+ myconsumer = CGDataConsumerCreateWithCFData(pdfdata); // 10.4
+ }
+ else
+#endif
+ {
+ static CGDataConsumerCallbacks callbacks = { Fl_Copy_Surface::MyPutBytes, NULL };
+ myconsumer = CGDataConsumerCreate ((void*) pdfdata, &callbacks);
+ }
+ gc = CGPDFContextCreate (myconsumer, &bounds, NULL);
+ CGDataConsumerRelease (myconsumer);
+}
+
+void Fl_Copy_Surface::prepare_copy_pdf_and_tiff(int w, int h)
+{
+ init_PDF_context(w, h);
+ if (gc == NULL) return;
+ CGRect bounds = CGRectMake(0, 0, w, h );
+ CGContextBeginPage (gc, &bounds);
+ CGContextTranslateCTM(gc, 0, h);
+ CGContextScaleCTM(gc, 1.0f, -1.0f);
+ CGContextSaveGState(gc);
+}
+
+
+void Fl_Copy_Surface::complete_copy_pdf_and_tiff()
+{
+ CGContextRestoreGState(gc);
+ CGContextEndPage(gc);
+ CGContextRelease(gc);
+ PasteboardRef clipboard = NULL;
+ PasteboardCreate(kPasteboardClipboard, &clipboard);
+ PasteboardClear(clipboard); // first, copy PDF to clipboard
+ PasteboardPutItemFlavor (clipboard, (PasteboardItemID)1,
+ CFSTR("com.adobe.pdf"), // kUTTypePDF
+ pdfdata, kPasteboardFlavorNoFlags);
+ //second, transform this PDF to a bitmap image and put it as tiff in clipboard
+ CGDataProviderRef prov;
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1040
+ if(fl_mac_os_version >= 100400)
+ prov = CGDataProviderCreateWithCFData(pdfdata); // 10.4
+ else
+#endif
+ prov = CGDataProviderCreateWithData(NULL, CFDataGetBytePtr(pdfdata), CFDataGetLength(pdfdata), NULL);
+ CGPDFDocumentRef pdfdoc = CGPDFDocumentCreateWithProvider(prov);
+ CGPDFPageRef pdfpage = CGPDFDocumentGetPage(pdfdoc, 1);
+ CGDataProviderRelease(prov);
+ CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
+ void *mem = ( fl_mac_os_version >= 100600 ? NULL : malloc(width * height * 4) );
+ gc = CGBitmapContextCreate(mem, width, height, 8, width * 4, space, kCGImageAlphaNoneSkipLast);
+ CFRelease(space);
+ if (gc == NULL) { if (mem) free(mem); return; }
+ CGRect rect = CGRectMake(0, 0, width, height);
+ CGContextSetRGBFillColor(gc, 1,1,1,1);//need to clear background
+ CGContextFillRect(gc, rect);
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
+ CGContextDrawPDFPage(gc, pdfpage); // requires 10.3
+#endif
+ CGPDFDocumentRelease(pdfdoc);
+ CFRelease(pdfdata);
+ PasteboardPutItemFlavor(clipboard, (PasteboardItemID)1, CFSTR("public.tiff"),
+ Fl_X::CGBitmapContextToTIFF(gc), kPasteboardFlavorNoFlags);
+ CFRelease(clipboard);
+ CGContextRelease(gc);
+ if (mem) free(mem);
+}
+
+#endif // __APPLE__
+
+#if !(defined(__APPLE__) || defined(WIN32) || defined(FL_DOXYGEN))
+/* graphics driver that translates all graphics coordinates before calling Xlib */
+class Fl_translated_Xlib_Graphics_Driver_ : public Fl_Xlib_Graphics_Driver {
+ int offset_x, offset_y; // translation between user and graphical coordinates: graphical = user + offset
+ int depth; // depth of translation stack
+ int stack_x[20], stack_y[20]; // translation stack allowing cumulative translations
+public:
+ static const char *class_id;
+ const char *class_name() {return class_id;};
+ Fl_translated_Xlib_Graphics_Driver_() {
+ offset_x = 0; offset_y = 0;
+ depth = 0;
+ }
+ virtual ~Fl_translated_Xlib_Graphics_Driver_() {};
+ void translate_all(int dx, int dy) { // reversibly adds dx,dy to the offset between user and graphical coordinates
+ stack_x[depth] = offset_x;
+ stack_y[depth] = offset_y;
+ offset_x = stack_x[depth] + dx;
+ offset_y = stack_y[depth] + dy;
+ push_matrix();
+ translate(dx, dy);
+ if (depth < sizeof(stack_x)/sizeof(int)) depth++;
+ else Fl::warning("%s: translate stack overflow!", class_id);
+ }
+ void untranslate_all() { // undoes previous translate_all()
+ if (depth > 0) depth--;
+ offset_x = stack_x[depth];
+ offset_y = stack_y[depth];
+ pop_matrix();
+ }
+ void rect(int x, int y, int w, int h) { Fl_Xlib_Graphics_Driver::rect(x+offset_x, y+offset_y, w, h); }
+ void rectf(int x, int y, int w, int h) { Fl_Xlib_Graphics_Driver::rectf(x+offset_x, y+offset_y, w, h); }
+ void xyline(int x, int y, int x1) { Fl_Xlib_Graphics_Driver::xyline(x+offset_x, y+offset_y, x1+offset_x); }
+ void xyline(int x, int y, int x1, int y2) { Fl_Xlib_Graphics_Driver::xyline(x+offset_x, y+offset_y, x1+offset_x, y2+offset_y); }
+ void xyline(int x, int y, int x1, int y2, int x3) { Fl_Xlib_Graphics_Driver::xyline(x+offset_x, y+offset_y, x1+offset_x, y2+offset_y, x3+offset_x); }
+ void yxline(int x, int y, int y1) { Fl_Xlib_Graphics_Driver::yxline(x+offset_x, y+offset_y, y1+offset_y); }
+ void yxline(int x, int y, int y1, int x2) { Fl_Xlib_Graphics_Driver::yxline(x+offset_x, y+offset_y, y1+offset_y, x2+offset_x); }
+ void yxline(int x, int y, int y1, int x2, int y3) { Fl_Xlib_Graphics_Driver::yxline(x+offset_x, y+offset_y, y1+offset_y, x2+offset_x, y3+offset_y); }
+ void line(int x, int y, int x1, int y1) { Fl_Xlib_Graphics_Driver::line(x+offset_x, y+offset_y, x1+offset_x, y1+offset_y); }
+ void line(int x, int y, int x1, int y1, int x2, int y2) { Fl_Xlib_Graphics_Driver::line(x+offset_x, y+offset_y, x1+offset_x, y1+offset_y, x2+offset_x, y2+offset_y); }
+ void draw(const char* str, int n, int x, int y) {
+ Fl_Xlib_Graphics_Driver::draw(str, n, x+offset_x, y+offset_y);
+ }
+ void draw(int angle, const char *str, int n, int x, int y) {
+ Fl_Xlib_Graphics_Driver::draw(angle, str, n, x+offset_x, y+offset_y);
+ }
+ void rtl_draw(const char* str, int n, int x, int y) {
+ Fl_Xlib_Graphics_Driver::rtl_draw(str, n, x+offset_x, y+offset_y);
+ }
+ void draw(Fl_Pixmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy) {
+ XP += offset_x; YP += offset_y;
+ translate_all(-offset_x, -offset_y);
+ Fl_Xlib_Graphics_Driver::draw(pxm, XP, YP, WP,HP,cx,cy);
+ untranslate_all();
+ }
+ void draw(Fl_Bitmap *bm, int XP, int YP, int WP, int HP, int cx, int cy) {
+ XP += offset_x; YP += offset_y;
+ translate_all(-offset_x, -offset_y);
+ Fl_Xlib_Graphics_Driver::draw(bm, XP, YP, WP,HP,cx,cy);
+ untranslate_all();
+ }
+ void draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
+ XP += offset_x; YP += offset_y;
+ translate_all(-offset_x, -offset_y);
+ Fl_Xlib_Graphics_Driver::draw(img, XP, YP, WP,HP,cx,cy);
+ untranslate_all();
+ }
+ void draw_image(const uchar* buf, int X,int Y,int W,int H, int D=3, int L=0) {
+ X += offset_x; Y += offset_y;
+ translate_all(-offset_x, -offset_y);
+ Fl_Xlib_Graphics_Driver::draw_image(buf, X, Y, W,H,D,L);
+ untranslate_all();
+ }
+ void draw_image(Fl_Draw_Image_Cb cb, void* data, int X,int Y,int W,int H, int D=3) {
+ X += offset_x; Y += offset_y;
+ translate_all(-offset_x, -offset_y);
+ Fl_Xlib_Graphics_Driver::draw_image(cb, data, X, Y, W,H,D);
+ untranslate_all();
+ }
+ void draw_image_mono(const uchar* buf, int X,int Y,int W,int H, int D=1, int L=0) {
+ X += offset_x; Y += offset_y;
+ translate_all(-offset_x, -offset_y);
+ Fl_Xlib_Graphics_Driver::draw_image_mono(buf, X, Y, W,H,D,L);
+ untranslate_all();
+ }
+ void draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int X,int Y,int W,int H, int D=1) {
+ X += offset_x; Y += offset_y;
+ translate_all(-offset_x, -offset_y);
+ Fl_Xlib_Graphics_Driver::draw_image_mono(cb, data, X, Y, W,H,D);
+ untranslate_all();
+ }
+ void copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) {
+ Fl_Xlib_Graphics_Driver::copy_offscreen(x+offset_x, y+offset_y, w, h,pixmap,srcx,srcy);
+ }
+ void push_clip(int x, int y, int w, int h) {
+ Fl_Xlib_Graphics_Driver::push_clip(x+offset_x, y+offset_y, w, h);
+ }
+ int not_clipped(int x, int y, int w, int h) { return Fl_Xlib_Graphics_Driver::not_clipped(x + offset_x, y + offset_y, w, h); };
+ int clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H) {
+ int retval = Fl_Xlib_Graphics_Driver::clip_box(x + offset_x, y + offset_y, w,h,X,Y,W,H);
+ X -= offset_x;
+ Y -= offset_y;
+ return retval;
+ }
+ void pie(int x, int y, int w, int h, double a1, double a2) { Fl_Xlib_Graphics_Driver::pie(x+offset_x,y+offset_y,w,h,a1,a2); }
+ void arc(int x, int y, int w, int h, double a1, double a2) { Fl_Xlib_Graphics_Driver::arc(x+offset_x,y+offset_y,w,h,a1,a2); }
+ void polygon(int x0, int y0, int x1, int y1, int x2, int y2) { Fl_Xlib_Graphics_Driver::polygon(x0+offset_x,y0+offset_y,x1+offset_x,y1+offset_y,x2+offset_x,y2+offset_y);}
+ void polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
+ Fl_Xlib_Graphics_Driver::polygon(x0+offset_x,y0+offset_y,x1+offset_x,y1+offset_y,x2+offset_x,y2+offset_y,x3+offset_x,y3+offset_y);
+ }
+ void loop(int x0, int y0, int x1, int y1, int x2, int y2) {Fl_Xlib_Graphics_Driver::loop(x0+offset_x,y0+offset_y,x1+offset_x,y1+offset_y,x2+offset_x,y2+offset_y);}
+ void loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
+ Fl_Xlib_Graphics_Driver::loop(x0+offset_x,y0+offset_y,x1+offset_x,y1+offset_y,x2+offset_x,y2+offset_y,x3+offset_x,y3+offset_y);
+ }
+ void point(int x, int y) { Fl_Xlib_Graphics_Driver::point(x+offset_x, y+offset_y); }
+};
+
+const char *Fl_translated_Xlib_Graphics_Driver_::class_id = "Fl_translated_Xlib_Graphics_Driver_";
+
+void Fl_Xlib_Surface_::translate(int x, int y) {
+ ((Fl_translated_Xlib_Graphics_Driver_*)driver())->translate_all(x, y);
+}
+void Fl_Xlib_Surface_::untranslate() {
+ ((Fl_translated_Xlib_Graphics_Driver_*)driver())->untranslate_all();
+}
+
+Fl_Xlib_Surface_::Fl_Xlib_Surface_() : Fl_Paged_Device() {
+ driver(new Fl_translated_Xlib_Graphics_Driver_());
+}
+Fl_Xlib_Surface_::~Fl_Xlib_Surface_() {
+ delete driver();
+}
+
+const char *Fl_Xlib_Surface_::class_id = "Fl_Xlib_Surface_";
+
+#endif
+
+//
+// End of "$Id: $".
+//
diff --git a/src/Fl_Image_Surface.cxx b/src/Fl_Image_Surface.cxx
new file mode 100644
index 000000000..047de7b57
--- /dev/null
+++ b/src/Fl_Image_Surface.cxx
@@ -0,0 +1,158 @@
+//
+// "$Id: Fl_Image_Surface.cxx 9869 2013-04-09 20:11:28Z greg.ercolano $"
+//
+// Draw-to-image code for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2014 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// http://www.fltk.org/COPYING.php
+//
+// Please report all bugs and problems on the following page:
+//
+// http://www.fltk.org/str.php
+//
+
+#include <FL/Fl_Image_Surface.H>
+#include <FL/Fl_Printer.H>
+#include <FL/Fl.H>
+
+
+const char *Fl_Image_Surface::class_id = "Fl_Image_Surface";
+
+/** The constructor.
+ \param w and \param h give the size in pixels of the resulting image.
+ */
+Fl_Image_Surface::Fl_Image_Surface(int w, int h) : Fl_Surface_Device(NULL) {
+ width = w;
+ height = h;
+#if !(defined(__APPLE__) || defined(WIN32))
+ gc = 0;
+ if (!fl_display) { // allows use of this class before any window is shown
+ fl_open_display();
+ gc = XCreateGC(fl_display, RootWindow(fl_display, fl_screen), 0, 0);
+ fl_gc = gc;
+ }
+#endif
+ offscreen = fl_create_offscreen(w, h);
+#ifdef __APPLE__
+ helper = new Fl_Quartz_Flipped_Surface_(width, height);
+ driver(helper->driver());
+#elif defined(WIN32)
+ helper = new Fl_GDI_Surface_();
+ driver(helper->driver());
+#else
+ helper = new Fl_Xlib_Surface_();
+ driver(helper->driver());
+#endif
+}
+
+/** The destructor.
+ */
+Fl_Image_Surface::~Fl_Image_Surface() {
+ fl_delete_offscreen(offscreen);
+#ifdef __APPLE__
+ delete (Fl_Quartz_Flipped_Surface_*)helper;
+#elif defined(WIN32)
+ delete (Fl_GDI_Surface_*)helper;
+#else
+ if (gc) { XFreeGC(fl_display, gc); fl_gc = 0; }
+ delete (Fl_Xlib_Surface_*)helper;
+#endif
+}
+
+/** Returns the image made of all drawings sent to the Fl_Image_Surface object.
+ The returned object can be safely cast to Fl_RGB_Image* and contains its own copy
+ of the RGB data.
+ */
+Fl_Image* Fl_Image_Surface::image()
+{
+ unsigned char *data;
+#ifdef __APPLE__
+ CGContextFlush(offscreen);
+ data = fl_read_image(NULL, 0, 0, width, height, 0);
+ fl_end_offscreen();
+#elif defined(WIN32)
+ fl_pop_clip();
+ data = fl_read_image(NULL, 0, 0, width, height, 0);
+ RestoreDC(fl_gc, _savedc);
+ DeleteDC(fl_gc);
+ _ss->set_current();
+ fl_window=_sw;
+ fl_gc = _sgc;
+#else
+ fl_pop_clip();
+ data = fl_read_image(NULL, 0, 0, width, height, 0);
+ fl_window = pre_window;
+ previous->set_current();
+#endif
+ Fl_RGB_Image *image = new Fl_RGB_Image(data, width, height);
+ image->alloc_array = 1;
+ return image;
+}
+
+/** Draws a widget in the image surface
+
+ \param widget any FLTK widget (e.g., standard, custom, window, GL view) to draw in the image
+ \param delta_x and \param delta_y give
+ the position in the image of the top-left corner of the widget
+ */
+void Fl_Image_Surface::draw(Fl_Widget *widget, int delta_x, int delta_y)
+{
+ helper->print_widget(widget, delta_x, delta_y);
+}
+
+
+void Fl_Image_Surface::set_current()
+{
+#if defined(__APPLE__)
+ fl_begin_offscreen(offscreen);
+ fl_pop_clip();
+ Fl_Surface_Device::set_current();
+ fl_push_no_clip();
+#elif defined(WIN32)
+ _sgc=fl_gc;
+ _sw=fl_window;
+ _ss = Fl_Surface_Device::surface();
+ Fl_Surface_Device::set_current();
+ fl_gc = fl_makeDC(offscreen);
+ _savedc = SaveDC(fl_gc);
+ fl_window=(HWND)offscreen;
+ fl_push_no_clip();
+#else
+ pre_window = fl_window;
+ fl_window = offscreen;
+ previous = Fl_Surface_Device::surface();
+ Fl_Surface_Device::set_current();
+ fl_push_no_clip();
+#endif
+}
+
+#if defined(__APPLE__)
+
+Fl_Quartz_Flipped_Surface_::Fl_Quartz_Flipped_Surface_(int w, int h) : Fl_Quartz_Surface_(w, h) {
+}
+
+void Fl_Quartz_Flipped_Surface_::translate(int x, int y) {
+ CGContextRestoreGState(fl_gc);
+ CGContextSaveGState(fl_gc);
+ CGContextTranslateCTM(fl_gc, x, -y);
+ CGContextSaveGState(fl_gc);
+ CGContextTranslateCTM(fl_gc, 0, height);
+ CGContextScaleCTM(fl_gc, 1.0f, -1.0f);
+}
+
+void Fl_Quartz_Flipped_Surface_::untranslate() {
+ CGContextRestoreGState(fl_gc);
+}
+
+const char *Fl_Quartz_Flipped_Surface_::class_id = "Fl_Quartz_Flipped_Surface_";
+
+#endif // __APPLE__
+
+//
+// End of "$Id: $".
+//
diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm
index 456daf0bf..b341acd20 100644
--- a/src/Fl_cocoa.mm
+++ b/src/Fl_cocoa.mm
@@ -2803,8 +2803,9 @@ static void clipboard_check(void)
* create a selection
* stuff: pointer to selected data
* len: size of selected data
+ * type: always "plain/text" for now
*/
-void Fl::copy(const char *stuff, int len, int clipboard) {
+void Fl::copy(const char *stuff, int len, int clipboard, const char *type) {
if (!stuff || len<0) return;
if (len+1 > fl_selection_buffer_length[clipboard]) {
delete[] fl_selection_buffer[clipboard];
@@ -2824,56 +2825,171 @@ void Fl::copy(const char *stuff, int len, int clipboard) {
}
}
+static int get_plain_text_from_clipboard(char **buffer, int previous_length)
+{
+ NSInteger length = 0;
+ NSPasteboard *clip = [NSPasteboard generalPasteboard];
+ NSString *found = [clip availableTypeFromArray:[NSArray arrayWithObjects:utf8_format, @"public.utf16-plain-text", @"com.apple.traditional-mac-plain-text", nil]];
+ if (found) {
+ NSData *data = [clip dataForType:found];
+ if (data) {
+ NSInteger len;
+ char *aux_c = NULL;
+ if (![found isEqualToString:utf8_format]) {
+ NSString *auxstring;
+ auxstring = (NSString *)CFStringCreateWithBytes(NULL,
+ (const UInt8*)[data bytes],
+ [data length],
+ [found isEqualToString:@"public.utf16-plain-text"] ? kCFStringEncodingUnicode : kCFStringEncodingMacRoman,
+ false);
+ aux_c = strdup([auxstring UTF8String]);
+ [auxstring release];
+ len = strlen(aux_c) + 1;
+ }
+ else len = [data length] + 1;
+ if ( len >= previous_length ) {
+ length = len;
+ delete[] *buffer;
+ *buffer = new char[len];
+ }
+ if (![found isEqualToString:utf8_format]) {
+ strcpy(*buffer, aux_c);
+ free(aux_c);
+ }
+ else {
+ [data getBytes:*buffer];
+ }
+ (*buffer)[len - 1] = 0;
+ length = len - 1;
+ convert_crlf(*buffer, len - 1); // turn all \r characters into \n:
+ Fl::e_clipboard_type = Fl::clipboard_plain_text;
+ }
+ }
+ return length;
+}
+
+static Fl_Image* get_image_from_clipboard()
+{
+ Fl_RGB_Image *image = NULL;
+ uchar *imagedata;
+ NSBitmapImageRep *bitmap;
+ NSPasteboard *clip = [NSPasteboard generalPasteboard];
+ NSArray *present = [clip types]; // types in pasteboard in order of decreasing preference
+ NSArray *possible = [NSArray arrayWithObjects:@"com.adobe.pdf", @"public.tiff", @"com.apple.pict", nil];
+ NSString *found = nil;
+ NSUInteger rank;
+ for (rank = 0; rank < [present count]; rank++) { // find first of possible types present in pasteboard
+ for (NSUInteger i = 0; i < [possible count]; i++) {
+ if ([[present objectAtIndex:rank] isEqualToString:[possible objectAtIndex:i]]) {
+ found = [present objectAtIndex:rank];
+ goto after_loop;
+ }
+ }
+ }
+after_loop:
+ if (found) {
+ NSData *data = [clip dataForType:found];
+ if (data) {
+ if ([found isEqualToString:@"public.tiff"]) {
+ bitmap = [NSBitmapImageRep imageRepWithData:data];
+ int bpp = [bitmap bytesPerPlane];
+ int bpr = [bitmap bytesPerRow];
+ int depth = [bitmap samplesPerPixel], w = bpr/depth, h = bpp/bpr;
+ imagedata = new uchar[w * h * depth];
+ memcpy(imagedata, [bitmap bitmapData], w * h * depth);
+ image = new Fl_RGB_Image(imagedata, w, h, depth);
+ image->alloc_array = 1;
+ }
+ else if ([found isEqualToString:@"com.adobe.pdf"] || [found isEqualToString:@"com.apple.pict"]) {
+ NSRect rect;
+ NSImageRep *vectorial;
+ NSAffineTransform *dilate = [NSAffineTransform transform];
+ if ([found isEqualToString:@"com.adobe.pdf"] ) {
+ vectorial = [NSPDFImageRep imageRepWithData:data];
+ rect = [(NSPDFImageRep*)vectorial bounds]; // in points = 1/72 inch
+ Fl_Window *win = Fl::first_window();
+ int screen_num = win ? Fl::screen_num(win->x(), win->y(), win->w(), win->h()) : 0;
+ float hr, vr;
+ Fl::screen_dpi(hr, vr, screen_num); // 1 inch = hr pixels = 72 points -> hr/72 pixel/point
+ CGFloat scale = hr/72;
+ [dilate scaleBy:scale];
+ rect.size.width *= scale;
+ rect.size.height *= scale;
+ rect = NSIntegralRect(rect);
+ }
+ else {
+ vectorial = [NSPICTImageRep imageRepWithData:data];
+ rect = [(NSPICTImageRep*)vectorial boundingBox]; // in pixel, no scaling required
+ }
+ imagedata = new uchar[(int)(rect.size.width * rect.size.height) * 4];
+ memset(imagedata, -1, (int)(rect.size.width * rect.size.height) * 4);
+ bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&imagedata
+ pixelsWide:rect.size.width
+ pixelsHigh:rect.size.height
+ bitsPerSample:8
+ samplesPerPixel:3
+ hasAlpha:NO
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:rect.size.width*4
+ bitsPerPixel:32];
+ NSDictionary *dict = [NSDictionary dictionaryWithObject:bitmap
+ forKey:NSGraphicsContextDestinationAttributeName];
+ NSGraphicsContext *oldgc = [NSGraphicsContext currentContext];
+ [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithAttributes:dict]];
+ [dilate concat];
+ [vectorial draw];
+ [NSGraphicsContext setCurrentContext:oldgc];
+ [bitmap release];
+ image = new Fl_RGB_Image(imagedata, rect.size.width, rect.size.height, 4);
+ image->alloc_array = 1;
+ }
+ Fl::e_clipboard_type = Fl::clipboard_image;
+ }
+ }
+ return image;
+}
// Call this when a "paste" operation happens:
-void Fl::paste(Fl_Widget &receiver, int clipboard) {
+void Fl::paste(Fl_Widget &receiver, int clipboard, const char *type) {
+ if (type[0] == 0) type = Fl::clipboard_plain_text;
if (clipboard) {
- // see if we own the selection, if not go get it:
- fl_selection_length[1] = 0;
-
- NSPasteboard *clip = [NSPasteboard generalPasteboard];
- NSString *found = [clip availableTypeFromArray:[NSArray arrayWithObjects:utf8_format, @"public.utf16-plain-text", @"com.apple.traditional-mac-plain-text", nil]];
- if (found) {
- NSData *data = [clip dataForType:found];
- if (data) {
- NSInteger len;
- char *aux_c = NULL;
- if (![found isEqualToString:utf8_format]) {
- NSString *auxstring;
- auxstring = (NSString *)CFStringCreateWithBytes(NULL,
- (const UInt8*)[data bytes],
- [data length],
- [found isEqualToString:@"public.utf16-plain-text"] ? kCFStringEncodingUnicode : kCFStringEncodingMacRoman,
- false);
- aux_c = strdup([auxstring UTF8String]);
- [auxstring release];
- len = strlen(aux_c) + 1;
- }
- else len = [data length] + 1;
- if ( len >= fl_selection_buffer_length[1] ) {
- fl_selection_buffer_length[1] = len;
- delete[] fl_selection_buffer[1];
- fl_selection_buffer[1] = new char[len];
- }
- if (![found isEqualToString:utf8_format]) {
- strcpy(fl_selection_buffer[1], aux_c);
- free(aux_c);
- }
- else {
- [data getBytes:fl_selection_buffer[1]];
+ Fl::e_clipboard_type = "";
+ if (strcmp(type, Fl::clipboard_plain_text) == 0) {
+ fl_selection_length[1] = get_plain_text_from_clipboard( &fl_selection_buffer[1], fl_selection_length[1]);
+ }
+ else if (strcmp(type, Fl::clipboard_image) == 0) {
+ Fl::e_clipboard_data = get_image_from_clipboard( );
+ if (Fl::e_clipboard_data) {
+ int done = receiver.handle(FL_PASTE);
+ Fl::e_clipboard_type = "";
+ if (done == 0) {
+ delete (Fl_Image*)Fl::e_clipboard_data;
+ Fl::e_clipboard_data = NULL;
}
- fl_selection_buffer[1][len - 1] = 0;
- fl_selection_length[1] = len - 1;
- convert_crlf(fl_selection_buffer[1], len - 1); // turn all \r characters into \n:
}
- }
+ return;
+ }
+ else
+ fl_selection_length[1] = 0;
}
Fl::e_text = fl_selection_buffer[clipboard];
Fl::e_length = fl_selection_length[clipboard];
- if (!Fl::e_text) Fl::e_text = (char *)"";
+ if (!Fl::e_length) Fl::e_text = (char *)"";
receiver.handle(FL_PASTE);
}
+int Fl::clipboard_contains(const char *type) {
+ NSString *found = nil;
+ if (strcmp(type, Fl::clipboard_plain_text) == 0) {
+ found = [[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:utf8_format, @"public.utf16-plain-text", @"com.apple.traditional-mac-plain-text", nil]];
+ }
+ else if (strcmp(type, Fl::clipboard_image) == 0) {
+ found = [[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:@"public.tiff", @"com.adobe.pdf", @"com.apple.pict", nil]];
+ }
+ return found != nil;
+}
+
int Fl_X::unlink(Fl_X *start) {
if (start) {
Fl_X *pc = start;
@@ -3045,6 +3161,25 @@ static NSImage *CGBitmapContextToNSImage(CGContextRef c)
return [image autorelease];
}
+
+CFDataRef Fl_X::CGBitmapContextToTIFF(CGContextRef c)
+{ // the returned value is autoreleased
+ unsigned char *pdata = (unsigned char *)CGBitmapContextGetData(c);
+ NSBitmapImageRep *imagerep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pdata
+ pixelsWide:CGBitmapContextGetWidth(c)
+ pixelsHigh:CGBitmapContextGetHeight(c)
+ bitsPerSample:8
+ samplesPerPixel:3
+ hasAlpha:NO
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:CGBitmapContextGetBytesPerRow(c)
+ bitsPerPixel:CGBitmapContextGetBitsPerPixel(c)];
+ NSData* tiff = [imagerep TIFFRepresentation];
+ [imagerep release];
+ return (CFDataRef)tiff;
+}
+
static NSCursor *PrepareCursor(NSCursor *cursor, CGContextRef (*f)() )
{
if (cursor == nil) {
@@ -3531,10 +3666,10 @@ void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset)
[title_s drawWithRect:r options:(NSStringDrawingOptions)0 attributes:attr]; // 10.4
[[NSGraphicsContext currentContext] setShouldAntialias:NO];
[NSGraphicsContext setCurrentContext:current];
- }
- else
+ }
+ else
#endif
- {
+ {
fl_font(FL_HELVETICA, 14); // the exact font is LucidaGrande 13 pts
fl_color(FL_BLACK);
int x = x_offset + win->w()/2 - fl_width(title)/2;
@@ -3542,7 +3677,7 @@ void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset)
fl_push_clip(x_offset, y_offset, win->w(), bt);
fl_draw(title, x, y_offset+bt/2+4);
fl_pop_clip();
- }
+ }
}
this->print_widget(win, x_offset, y_offset + bt); // print the window inner part
}
diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx
index 092f194a0..3aeba8ab0 100644
--- a/src/Fl_win32.cxx
+++ b/src/Fl_win32.cxx
@@ -567,7 +567,7 @@ void fl_update_clipboard(void) {
}
// call this when you create a selection:
-void Fl::copy(const char *stuff, int len, int clipboard) {
+void Fl::copy(const char *stuff, int len, int clipboard, const char *type) {
if (!stuff || len<0) return;
// Convert \n -> \r\n (for old apps like Notepad, DOS)
@@ -589,13 +589,11 @@ void Fl::copy(const char *stuff, int len, int clipboard) {
}
// Call this when a "paste" operation happens:
-void Fl::paste(Fl_Widget &receiver, int clipboard) {
- if (!clipboard || fl_i_own_selection[clipboard]) {
+void Fl::paste(Fl_Widget &receiver, int clipboard, const char *type) {
+ if (!clipboard || (fl_i_own_selection[clipboard] && strcmp(type, Fl::clipboard_plain_text) == 0)) {
// We already have it, do it quickly without window server.
// Notice that the text is clobbered if set_selection is
// called in response to FL_PASTE!
-
- // Convert \r\n -> \n
char *i = fl_selection_buffer[clipboard];
if (i==0L) {
Fl::e_text = 0;
@@ -603,39 +601,137 @@ void Fl::paste(Fl_Widget &receiver, int clipboard) {
}
Fl::e_text = new char[fl_selection_length[clipboard]+1];
char *o = Fl::e_text;
- while (*i) {
+ while (*i) { // Convert \r\n -> \n
if ( *i == '\r' && *(i+1) == '\n') i++;
else *o++ = *i++;
}
*o = 0;
Fl::e_length = (int) (o - Fl::e_text);
+ Fl::e_clipboard_type = Fl::clipboard_plain_text;
receiver.handle(FL_PASTE);
delete [] Fl::e_text;
Fl::e_text = 0;
- } else {
+ } else if (clipboard) {
+ HANDLE h;
if (!OpenClipboard(NULL)) return;
- HANDLE h = GetClipboardData(CF_UNICODETEXT);
- if (h) {
- wchar_t *memLock = (wchar_t*) GlobalLock(h);
- size_t utf16_len = wcslen(memLock);
- Fl::e_text = (char*) malloc (utf16_len * 4 + 1);
- unsigned utf8_len = fl_utf8fromwc(Fl::e_text, (unsigned) (utf16_len * 4), memLock, (unsigned) utf16_len);
- *(Fl::e_text + utf8_len) = 0;
- LPSTR a,b;
- a = b = Fl::e_text;
- while (*a) { // strip the CRLF pairs ($%$#@^)
- if (*a == '\r' && a[1] == '\n') a++;
- else *b++ = *a++;
+ if (strcmp(type, Fl::clipboard_plain_text) == 0) { // we want plain text from clipboard
+ if ((h = GetClipboardData(CF_UNICODETEXT))) { // there's text in the clipboard
+ wchar_t *memLock = (wchar_t*) GlobalLock(h);
+ size_t utf16_len = wcslen(memLock);
+ Fl::e_text = new char[utf16_len * 4 + 1];
+ unsigned utf8_len = fl_utf8fromwc(Fl::e_text, (unsigned) (utf16_len * 4), memLock, (unsigned) utf16_len);
+ *(Fl::e_text + utf8_len) = 0;
+ GlobalUnlock(h);
+ LPSTR a,b;
+ a = b = Fl::e_text;
+ while (*a) { // strip the CRLF pairs ($%$#@^)
+ if (*a == '\r' && a[1] == '\n') a++;
+ else *b++ = *a++;
+ }
+ *b = 0;
+ Fl::e_length = (int) (b - Fl::e_text);
+ Fl::e_clipboard_type = Fl::clipboard_plain_text; // indicates that the paste event is for plain UTF8 text
+ receiver.handle(FL_PASTE); // send the FL_PASTE event to the widget
+ delete[] Fl::e_text;
+ Fl::e_text = 0;
+ }
}
- *b = 0;
- Fl::e_length = (int) (b - Fl::e_text);
- receiver.handle(FL_PASTE);
- GlobalUnlock(h);
- free(Fl::e_text);
- Fl::e_text = 0;
+ else if (strcmp(type, Fl::clipboard_image) == 0) { // we want an image from clipboard
+ uchar *rgb = NULL;
+ int width, height, depth;
+ if ( (h = GetClipboardData(CF_DIB)) ) { // if there's a DIB in clipboard
+ LPBITMAPINFO lpBI = (LPBITMAPINFO)GlobalLock(h) ;
+ width = lpBI->bmiHeader.biWidth; // bitmap width & height
+ height = lpBI->bmiHeader.biHeight;
+ if ( (lpBI->bmiHeader.biBitCount == 24 || lpBI->bmiHeader.biBitCount == 32) &&
+ lpBI->bmiHeader.biCompression == BI_RGB &&
+ lpBI->bmiHeader.biClrUsed == 0) { // direct use of the DIB data if it's RGB or RGBA
+ int linewidth; // row length
+ depth = lpBI->bmiHeader.biBitCount/8; // 3 or 4
+ if (depth == 3) linewidth = 4 * ((3*width + 3)/4); // row length: series of groups of 3 bytes, rounded to multiple of 4 bytes
+ else linewidth = 4*width;
+ rgb = new uchar[width * height * depth]; // will hold the image data
+ uchar *p = rgb, *r, rr, gg, bb;
+ for (int i=height-1; i>=0; i--) { // for each row, from last to first
+ r = (uchar*)(lpBI->bmiColors) + i*linewidth; // beginning of pixel data for the ith row
+ for (int j=0; j<width; j++) { // for each pixel in a row
+ bb = *r++; // BGR is in DIB
+ gg = *r++;
+ rr = *r++;
+ *p++ = rr; // we want RGB
+ *p++ = gg;
+ *p++ = bb;
+ if (depth == 4) *p++ = *r++; // copy alpha if present
+ }
+ }
+ }
+ else { // the system will decode a complex DIB
+ void *pDIBBits = (void*)(lpBI->bmiColors);
+ if (lpBI->bmiHeader.biCompression == BI_BITFIELDS) pDIBBits = (void*)(lpBI->bmiColors + 3);
+ else if (lpBI->bmiHeader.biClrUsed > 0) pDIBBits = (void*)(lpBI->bmiColors + lpBI->bmiHeader.biClrUsed);
+ Fl_Offscreen off = fl_create_offscreen(width, height);
+ fl_begin_offscreen(off);
+ SetDIBitsToDevice(fl_gc, 0, 0, width, height, 0, 0, 0, height, pDIBBits, lpBI, DIB_RGB_COLORS);
+ rgb = fl_read_image(NULL, 0, 0, width, height);
+ depth = 3;
+ fl_end_offscreen();
+ fl_delete_offscreen(off);
+ }
+ GlobalUnlock(h);
+ }
+ else if ((h = GetClipboardData(CF_ENHMETAFILE))) { // if there's an enhanced metafile in clipboard
+ ENHMETAHEADER header;
+ GetEnhMetaFileHeader((HENHMETAFILE)h, sizeof(header), &header); // get structure containing metafile dimensions
+ width = (header.rclFrame.right - header.rclFrame.left + 1); // in .01 mm units
+ height = (header.rclFrame.bottom - header.rclFrame.top + 1);
+ HDC hdc = GetDC(NULL); // get unit correspondance between .01 mm and screen pixels
+ int hmm = GetDeviceCaps(hdc, HORZSIZE);
+ int hdots = GetDeviceCaps(hdc, HORZRES);
+ int vmm = GetDeviceCaps(hdc, VERTSIZE);
+ int vdots = GetDeviceCaps(hdc, VERTRES);
+ ReleaseDC(NULL, hdc);
+ float factorw = (100. * hmm) / hdots;
+ float factorh = (100. * vmm) / vdots + 0.5;
+ width /= factorw; height /= factorh; // convert to screen pixel unit
+ RECT rect = {0, 0, width, height};
+ Fl_Offscreen off = fl_create_offscreen(width, height);
+ fl_begin_offscreen(off);
+ fl_color(FL_WHITE); fl_rectf(0,0,width, height); // draw white background
+ PlayEnhMetaFile(fl_gc, (HENHMETAFILE)h, &rect); // draw metafile to offscreen buffer
+ rgb = fl_read_image(NULL, 0, 0, width, height); // read pixels from offscreen buffer
+ depth = 3;
+ fl_end_offscreen();
+ fl_delete_offscreen(off);
+ }
+ if (rgb) {
+ Fl_RGB_Image *image = new Fl_RGB_Image(rgb, width, height, depth); // create new image from pixel data
+ image->alloc_array = 1;
+ Fl::e_clipboard_data = image;
+ Fl::e_clipboard_type = Fl::clipboard_image; // indicates that the paste event is for image data
+ int done = receiver.handle(FL_PASTE); // send FL_PASTE event to widget
+ Fl::e_clipboard_type = "";
+ if (done == 0) { // if widget did not handle the event, delete the image
+ Fl::e_clipboard_data = NULL;
+ delete image;
+ }
+ }
+ }
+ CloseClipboard();
}
- CloseClipboard();
+}
+
+int Fl::clipboard_contains(const char *type)
+{
+ int retval = 0;
+ if (!OpenClipboard(NULL)) return 0;
+ if (strcmp(type, Fl::clipboard_plain_text) == 0 || type[0] == 0) {
+ retval = IsClipboardFormatAvailable(CF_UNICODETEXT);
+ }
+ else if (strcmp(type, Fl::clipboard_image) == 0) {
+ retval = IsClipboardFormatAvailable(CF_DIB) || IsClipboardFormatAvailable(CF_ENHMETAFILE);
}
+ CloseClipboard();
+ return retval;
}
static HWND clipboard_wnd = 0;
diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx
index 58f2bf906..a6dd44fa8 100644
--- a/src/Fl_x.cxx
+++ b/src/Fl_x.cxx
@@ -34,6 +34,7 @@
# include <FL/Fl_Tooltip.H>
# include <FL/fl_draw.H>
# include <FL/Fl_Paged_Device.H>
+# include <FL/Fl_Shared_Image.H>
# include <FL/fl_ask.H>
# include <stdio.h>
# include <stdlib.h>
@@ -335,6 +336,9 @@ static Atom fl_XaText;
Atom fl_XaCompoundText;
Atom fl_XaUtf8String;
Atom fl_XaTextUriList;
+Atom fl_XaImageBmp;
+Atom fl_XaImagePNG;
+Atom fl_INCR;
Atom fl_NET_WM_NAME; // utf8 aware window label
Atom fl_NET_WM_ICON_NAME; // utf8 aware window icon name
Atom fl_NET_SUPPORTING_WM_CHECK;
@@ -641,6 +645,9 @@ void fl_open_display(Display* d) {
fl_XaCompoundText = XInternAtom(d, "COMPOUND_TEXT", 0);
fl_XaUtf8String = XInternAtom(d, "UTF8_STRING", 0);
fl_XaTextUriList = XInternAtom(d, "text/uri-list", 0);
+ fl_XaImageBmp = XInternAtom(d, "image/bmp", 0);
+ fl_XaImagePNG = XInternAtom(d, "image/png", 0);
+ fl_INCR = XInternAtom(d, "INCR", 0);
fl_NET_WM_NAME = XInternAtom(d, "_NET_WM_NAME", 0);
fl_NET_WM_ICON_NAME = XInternAtom(d, "_NET_WM_ICON_NAME", 0);
fl_NET_SUPPORTING_WM_CHECK = XInternAtom(d, "_NET_SUPPORTING_WM_CHECK", 0);
@@ -773,15 +780,18 @@ void Fl::get_mouse(int &xx, int &yy) {
Fl_Widget *fl_selection_requestor;
char *fl_selection_buffer[2];
int fl_selection_length[2];
+const char * fl_selection_type[2];
int fl_selection_buffer_length[2];
char fl_i_own_selection[2] = {0,0};
// Call this when a "paste" operation happens:
-void Fl::paste(Fl_Widget &receiver, int clipboard) {
+void Fl::paste(Fl_Widget &receiver, int clipboard, const char *type) {
if (fl_i_own_selection[clipboard]) {
// We already have it, do it quickly without window server.
// Notice that the text is clobbered if set_selection is
// called in response to FL_PASTE!
+ // However, for now, we only paste text in this function
+ if (fl_selection_type[clipboard] != Fl::clipboard_plain_text) return; //TODO: allow copy/paste of image within same app
Fl::e_text = fl_selection_buffer[clipboard];
Fl::e_length = fl_selection_length[clipboard];
if (!Fl::e_text) Fl::e_text = (char *)"";
@@ -791,10 +801,57 @@ void Fl::paste(Fl_Widget &receiver, int clipboard) {
// otherwise get the window server to return it:
fl_selection_requestor = &receiver;
Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
+ Fl::e_clipboard_type = type;
XConvertSelection(fl_display, property, TARGETS, property,
fl_xid(Fl::first_window()), fl_event_time);
}
+int Fl::clipboard_contains(const char *type)
+{
+ XEvent event;
+ Atom actual; int format; unsigned long count, remaining, i = 0;
+ unsigned char* portion = NULL;
+ Fl_Window *win = Fl::first_window();
+ if (!win || !fl_xid(win)) return 0;
+ XConvertSelection(fl_display, CLIPBOARD, TARGETS, CLIPBOARD,
+ fl_xid(win), fl_event_time);
+ XFlush(fl_display);
+ do { XNextEvent(fl_display, &event); i++; }
+ while (i < 10 && (event.type != SelectionNotify || event.xselection.property == None));
+ if (i >= 10) return 0;
+ XGetWindowProperty(fl_display,
+ event.xselection.requestor,
+ event.xselection.property,
+ 0, 4000, 0, 0,
+ &actual, &format, &count, &remaining, &portion);
+ if (actual != XA_ATOM) return 0;
+ Atom t;
+ int retval = 0;
+ if (strcmp(type, Fl::clipboard_plain_text) == 0) {
+ for (i = 0; i<count; i++) { // searching for text data
+ t = ((Atom*)portion)[i];
+ if (t == fl_Xatextplainutf ||
+ t == fl_Xatextplainutf2 ||
+ t == fl_Xatextplain ||
+ t == fl_XaUtf8String) {
+ retval = 1;
+ break;
+ }
+ }
+ }
+ else if (strcmp(type, Fl::clipboard_image) == 0) {
+ for (i = 0; i<count; i++) { // searching for image data
+ t = ((Atom*)portion)[i];
+ if (t == fl_XaImageBmp || t == fl_XaImagePNG) {
+ retval = 1;
+ break;
+ }
+ }
+ }
+ XFree(portion);
+ return retval;
+}
+
Window fl_dnd_source_window;
Atom *fl_dnd_source_types; // null-terminated list of data types being supplied
Atom fl_dnd_type;
@@ -859,7 +916,7 @@ static int get_xwinprop(Window wnd, Atom prop, long max_length,
////////////////////////////////////////////////////////////////
// Code for copying to clipboard and DnD out of the program:
-void Fl::copy(const char *stuff, int len, int clipboard) {
+void Fl::copy(const char *stuff, int len, int clipboard, const char *type) {
if (!stuff || len<0) return;
if (len+1 > fl_selection_buffer_length[clipboard]) {
delete[] fl_selection_buffer[clipboard];
@@ -870,6 +927,77 @@ void Fl::copy(const char *stuff, int len, int clipboard) {
fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
fl_selection_length[clipboard] = len;
fl_i_own_selection[clipboard] = 1;
+ fl_selection_type[clipboard] = Fl::clipboard_plain_text;
+ Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
+ XSetSelectionOwner(fl_display, property, fl_message_window, fl_event_time);
+}
+
+static void write_short(unsigned char **cp,short i){
+ unsigned char *c=*cp;
+ *c++=i&0xFF;i>>=8;
+ *c++=i&0xFF;i>>=8;
+ *cp=c;
+}
+
+static void write_int(unsigned char **cp,int i){
+ unsigned char *c=*cp;
+ *c++=i&0xFF;i>>=8;
+ *c++=i&0xFF;i>>=8;
+ *c++=i&0xFF;i>>=8;
+ *c++=i&0xFF;i>>=8;
+ *cp=c;
+}
+
+static unsigned char *create_bmp(const unsigned char *data, int W, int H, int *return_size){
+ int R=(3*W+3)/4 * 4; // the number of bytes per row, rounded up to multiple of 4
+ int s=H*R;
+ int fs=14+40+s;
+ unsigned char *b=new unsigned char[fs];
+ unsigned char *c=b;
+ // BMP header
+ *c++='B';
+ *c++='M';
+ write_int(&c,fs);
+ write_int(&c,0);
+ write_int(&c,14+40);
+ // DIB header:
+ write_int(&c,40);
+ write_int(&c,W);
+ write_int(&c,H);
+ write_short(&c,1);
+ write_short(&c,24);//bits ber pixel
+ write_int(&c,0);//RGB
+ write_int(&c,s);
+ write_int(&c,0);// horizontal resolution
+ write_int(&c,0);// vertical resolution
+ write_int(&c,0);//number of colors. 0 -> 1<<bits_per_pixel
+ write_int(&c,0);
+ // Pixel data
+ data+=3*W*H;
+ for (int y=0;y<H;++y){
+ data-=3*W;
+ const unsigned char *s=data;
+ unsigned char *p=c;
+ for (int x=0;x<W;++x){
+ *p++=s[2];
+ *p++=s[1];
+ *p++=s[0];
+ s+=3;
+ }
+ c+=R;
+ }
+ *return_size = fs;
+ return b;
+}
+
+void Fl::copy_image(const unsigned char *data, int W, int H, int clipboard){
+ if(!data || W<=0 || H<=0) return;
+ delete[] fl_selection_buffer[clipboard];
+ fl_selection_buffer[clipboard] = (char *) create_bmp(data,W,H,&fl_selection_length[clipboard]);
+ fl_selection_buffer_length[clipboard] = fl_selection_length[clipboard];
+ fl_i_own_selection[clipboard] = 1;
+ fl_selection_type[clipboard] = Fl::clipboard_image;
+
Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
XSetSelectionOwner(fl_display, property, fl_message_window, fl_event_time);
}
@@ -1047,6 +1175,62 @@ static int wasXExceptionRaised() {
}
+static bool getNextEvent(XEvent *event_return)
+{
+ time_t t = time(NULL);
+ while(!XPending(fl_display))
+ {
+ if(time(NULL) - t > 10.0)
+ {
+ //fprintf(stderr,"Error: The XNextEvent never came...\n");
+ return false;
+ }
+ }
+ XNextEvent(fl_display, event_return);
+ return true;
+}
+
+static long getIncrData(uchar* &data, const XSelectionEvent& selevent, long lower_bound)
+{
+//fprintf(stderr,"Incremental transfer starting due to INCR property\n");
+ size_t total = 0;
+ XEvent event;
+ XDeleteProperty(fl_display, selevent.requestor, selevent.property);
+ data = (uchar*)realloc(data, lower_bound);
+ for (;;)
+ {
+ if (!getNextEvent(&event)) break;
+ if (event.type == PropertyNotify)
+ {
+ if (event.xproperty.state != PropertyNewValue) continue;
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ unsigned char* prop = 0;
+ long offset = 0;
+ size_t num_bytes;
+ //size_t slice_size = 0;
+ do
+ {
+ XGetWindowProperty(fl_display, selevent.requestor, selevent.property, offset, 70000, True,
+ AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop);
+ num_bytes = nitems * (actual_format / 8);
+ offset += num_bytes/4;
+ //slice_size += num_bytes;
+ if (total + num_bytes > lower_bound) data = (uchar*)realloc(data, total + num_bytes);
+ memcpy(data + total, prop, num_bytes); total += num_bytes;
+ if (prop) XFree(prop);
+ } while (bytes_after != 0);
+//fprintf(stderr,"INCR data size:%ld\n", slice_size);
+ if (num_bytes == 0) break;
+ }
+ else break;
+ }
+ XDeleteProperty(fl_display, selevent.requestor, selevent.property);
+ return (long)total;
+}
+
int fl_handle(const XEvent& thisevent)
{
@@ -1139,6 +1323,7 @@ int fl_handle(const XEvent& thisevent)
case SelectionNotify: {
static unsigned char* sn_buffer = 0;
+ //static const char *buffer_format = 0;
if (sn_buffer) {XFree(sn_buffer); sn_buffer = 0;}
long bytesread = 0;
if (fl_xevent->xselection.property) for (;;) {
@@ -1150,7 +1335,7 @@ int fl_handle(const XEvent& thisevent)
if (XGetWindowProperty(fl_display,
fl_xevent->xselection.requestor,
fl_xevent->xselection.property,
- bytesread/4, 65536, 1, 0,
+ bytesread/4, 65536, 0/*1*/, AnyPropertyType,
&actual, &format, &count, &remaining,
&portion)) break; // quit on error
@@ -1168,9 +1353,24 @@ int fl_handle(const XEvent& thisevent)
}
if (actual == TARGETS || actual == XA_ATOM) {
- Atom type = XA_STRING;
- for (unsigned i = 0; i<count; i++) {
- Atom t = ((Atom*)portion)[i];
+/*for (unsigned i = 0; i<count; i++) {
+ fprintf(stderr," %s", XGetAtomName(fl_display, ((Atom*)portion)[i]) );
+ }
+fprintf(stderr,"\n");*/
+ Atom t, type = XA_STRING;
+ if (Fl::e_clipboard_type == Fl::clipboard_image) { // searching for image data
+ for (unsigned i = 0; i<count; i++) {
+ t = ((Atom*)portion)[i];
+ if (t == fl_XaImageBmp || t == fl_XaImagePNG) {
+ type = t;
+ goto found;
+ }
+ }
+ XFree(portion);
+ return true;
+ }
+ for (unsigned i = 0; i<count; i++) { // searching for text data
+ t = ((Atom*)portion)[i];
if (t == fl_Xatextplainutf ||
t == fl_Xatextplainutf2 ||
t == fl_Xatextplain ||
@@ -1183,14 +1383,33 @@ int fl_handle(const XEvent& thisevent)
t == fl_XaTextUriList ||
t == fl_XaCompoundText) type = t;
}
+ found:
XFree(portion); portion = 0;
Atom property = xevent.xselection.property;
XConvertSelection(fl_display, property, type, property,
fl_xid(Fl::first_window()),
fl_event_time);
+ if (type == fl_XaImageBmp) {
+ Fl::e_clipboard_type = Fl::clipboard_image;
+ //buffer_format = "image/bmp";
+ }
+ else if (type == fl_XaImagePNG) {
+ Fl::e_clipboard_type = Fl::clipboard_image;
+ //buffer_format = "image/png";
+ }
+ else {
+ Fl::e_clipboard_type = Fl::clipboard_plain_text;
+ //buffer_format = Fl::clipboard_plain_text;
+ }
+//fprintf(stderr,"used format=%s\n", buffer_format);
return true;
}
- // Make sure we got something sane...
+ if (actual == fl_INCR) {
+ bytesread = getIncrData(sn_buffer, xevent.xselection, *(long*)portion);
+ XFree(portion);
+ break;
+ }
+ // Make sure we got something sane...
if ((portion == NULL) || (format != 8) || (count == 0)) {
if (portion) { XFree(portion); portion = 0; }
return true;
@@ -1203,22 +1422,45 @@ int fl_handle(const XEvent& thisevent)
sn_buffer[bytesread] = '\0';
if (!remaining) break;
}
- if (sn_buffer) {
+ if (sn_buffer && Fl::e_clipboard_type == Fl::clipboard_plain_text) {
sn_buffer[bytesread] = 0;
convert_crlf(sn_buffer, bytesread);
}
-
+ if (Fl::e_clipboard_type == Fl::clipboard_image) {
+ if (bytesread == 0) return 0;
+ Fl_Image *image = 0;
+ static char tmp_fname[21];
+ static Fl_Shared_Image *shared = 0;
+ strcpy(tmp_fname, "/tmp/clipboardXXXXXX");
+ int fd = mkstemp(tmp_fname);
+ if (fd == -1) return 0;
+ uchar *p = sn_buffer; ssize_t towrite = bytesread, written;
+ while (towrite) {
+ written = write(fd, p, towrite);
+ p += written; towrite -= written;
+ }
+ close(fd);
+ free(sn_buffer); sn_buffer = 0;
+ shared = Fl_Shared_Image::get(tmp_fname);
+ unlink(tmp_fname);
+ if (!shared) return 0;
+ image = shared->copy();
+ shared->release();
+ Fl::e_clipboard_data = (void*)image;
+ }
if (!fl_selection_requestor) return 0;
- Fl::e_text = sn_buffer ? (char*)sn_buffer : (char *)"";
- Fl::e_length = bytesread;
+ if (Fl::e_clipboard_type == Fl::clipboard_plain_text) {
+ Fl::e_text = sn_buffer ? (char*)sn_buffer : (char *)"";
+ Fl::e_length = bytesread;
+ }
int old_event = Fl::e_number;
fl_selection_requestor->handle(Fl::e_number = FL_PASTE);
Fl::e_number = old_event;
// Detect if this paste is due to Xdnd by the property name (I use
// XA_SECONDARY for that) and send an XdndFinished message. It is not
// clear if this has to be delayed until now or if it can be done
- // immediatly after calling XConvertSelection.
+ // immediately after calling XConvertSelection.
if (fl_xevent->xselection.property == XA_SECONDARY &&
fl_dnd_source_window) {
fl_sendClientMessage(fl_dnd_source_window, fl_XdndFinished,
@@ -1242,32 +1484,51 @@ int fl_handle(const XEvent& thisevent)
e.target = fl_xevent->xselectionrequest.target;
e.time = fl_xevent->xselectionrequest.time;
e.property = fl_xevent->xselectionrequest.property;
- if (e.target == TARGETS) {
- Atom a[3] = {fl_XaUtf8String, XA_STRING, fl_XaText};
- XChangeProperty(fl_display, e.requestor, e.property,
- XA_ATOM, atom_bits, 0, (unsigned char*)a, 3);
- } else if (/*e.target == XA_STRING &&*/ fl_selection_length[clipboard]) {
- if (e.target == fl_XaUtf8String ||
- e.target == XA_STRING ||
- e.target == fl_XaCompoundText ||
- e.target == fl_XaText ||
- e.target == fl_Xatextplain ||
- e.target == fl_Xatextplainutf ||
- e.target == fl_Xatextplainutf2) {
- // clobber the target type, this seems to make some applications
- // behave that insist on asking for XA_TEXT instead of UTF8_STRING
- // Does not change XA_STRING as that breaks xclipboard.
- if (e.target != XA_STRING) e.target = fl_XaUtf8String;
+ if (fl_selection_type[clipboard] == Fl::clipboard_plain_text) {
+ if (e.target == TARGETS) {
+ Atom a[3] = {fl_XaUtf8String, XA_STRING, fl_XaText};
+ XChangeProperty(fl_display, e.requestor, e.property,
+ XA_ATOM, atom_bits, 0, (unsigned char*)a, 3);
+ } else {
+ if (/*e.target == XA_STRING &&*/ fl_selection_length[clipboard]) {
+ if (e.target == fl_XaUtf8String ||
+ e.target == XA_STRING ||
+ e.target == fl_XaCompoundText ||
+ e.target == fl_XaText ||
+ e.target == fl_Xatextplain ||
+ e.target == fl_Xatextplainutf ||
+ e.target == fl_Xatextplainutf2) {
+ // clobber the target type, this seems to make some applications
+ // behave that insist on asking for XA_TEXT instead of UTF8_STRING
+ // Does not change XA_STRING as that breaks xclipboard.
+ if (e.target != XA_STRING) e.target = fl_XaUtf8String;
+ XChangeProperty(fl_display, e.requestor, e.property,
+ e.target, 8, 0,
+ (unsigned char *)fl_selection_buffer[clipboard],
+ fl_selection_length[clipboard]);
+ }
+ } else {
+ // char* x = XGetAtomName(fl_display,e.target);
+ // fprintf(stderr,"selection request of %s\n",x);
+ // XFree(x);
+ e.property = 0;
+ }
+ }
+ } else { // image in clipboard
+ if (e.target == TARGETS) {
+ Atom a[1] = {fl_XaImageBmp};
XChangeProperty(fl_display, e.requestor, e.property,
- e.target, 8, 0,
- (unsigned char *)fl_selection_buffer[clipboard],
- fl_selection_length[clipboard]);
+ XA_ATOM, atom_bits, 0, (unsigned char*)a, 1);
+ } else {
+ if (e.target == fl_XaImageBmp && fl_selection_length[clipboard]) {
+ XChangeProperty(fl_display, e.requestor, e.property,
+ e.target, 8, 0,
+ (unsigned char *)fl_selection_buffer[clipboard],
+ fl_selection_length[clipboard]);
+ } else {
+ e.property = 0;
+ }
}
- } else {
-// char* x = XGetAtomName(fl_display,e.target);
-// fprintf(stderr,"selection request of %s\n",x);
-// XFree(x);
- e.property = 0;
}
XSendEvent(fl_display, e.requestor, 0, 0, (XEvent *)&e);}
return 1;
@@ -1517,13 +1778,13 @@ int fl_handle(const XEvent& thisevent)
// Display * display ;
// Bool detectable ;
// Bool * supported_rtrn ;
- // ...would be the easy way to corrct this isuue. Unfortunatly, this call is also
+ // ...would be the easy way to correct this issue. Unfortunately, this call is also
// broken on many Unix distros including Ubuntu and Solaris (as of Dec 2009)
// Bogus KeyUp events are generated by repeated KeyDown events. One
- // neccessary condition is an identical key event pending right after
+ // necessary condition is an identical key event pending right after
// the bogus KeyUp.
- // The new code introduced Dec 2009 differs in that it only check the very
+ // The new code introduced Dec 2009 differs in that it only checks the very
// next event in the queue, not the entire queue of events.
// This function wrongly detects a repeat key if a software keyboard
// sends a burst of events containing two consecutive equal keys. However,
diff --git a/src/Makefile b/src/Makefile
index 2c236f47a..f9f0cf8c7 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -31,6 +31,7 @@ CPPFILES = \
Fl_Choice.cxx \
Fl_Clock.cxx \
Fl_Color_Chooser.cxx \
+ Fl_Copy_Surface.cxx \
Fl_Counter.cxx \
Fl_Dial.cxx \
Fl_Device.cxx \
@@ -43,6 +44,7 @@ CPPFILES = \
Fl_Group.cxx \
Fl_Help_View.cxx \
Fl_Image.cxx \
+ Fl_Image_Surface.cxx \
Fl_Input.cxx \
Fl_Input_.cxx \
Fl_Light_Button.cxx \