diff options
| author | Manolo Gouy <Manolo> | 2014-05-23 16:47:21 +0000 |
|---|---|---|
| committer | Manolo Gouy <Manolo> | 2014-05-23 16:47:21 +0000 |
| commit | 07dd8ba328117a2599cb39dbaa9f17d4f279f923 (patch) | |
| tree | 6e4976f79f015d70a9e540b6e66262219fd300a2 /src | |
| parent | 85af2efe09d6ac88bfc18f8a991ea59af9a5b24b (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.cxx | 18 | ||||
| -rw-r--r-- | src/Fl_Copy_Surface.cxx | 399 | ||||
| -rw-r--r-- | src/Fl_Image_Surface.cxx | 158 | ||||
| -rw-r--r-- | src/Fl_cocoa.mm | 223 | ||||
| -rw-r--r-- | src/Fl_win32.cxx | 148 | ||||
| -rw-r--r-- | src/Fl_x.cxx | 339 | ||||
| -rw-r--r-- | src/Makefile | 2 |
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 \ |
