summaryrefslogtreecommitdiff
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
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
-rw-r--r--CHANGES4
-rw-r--r--FL/Fl.H78
-rw-r--r--FL/Fl_Copy_Surface.H135
-rw-r--r--FL/Fl_Image_Surface.H90
-rw-r--r--FL/mac.H1
-rw-r--r--examples/Makefile3
-rw-r--r--examples/clipboard.cxx168
-rw-r--r--ide/Xcode3/FLTK.xcodeproj/project.pbxproj12
-rw-r--r--ide/Xcode4/FLTK.xcodeproj/project.pbxproj12
-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
-rw-r--r--test/device.cxx120
17 files changed, 1744 insertions, 166 deletions
diff --git a/CHANGES b/CHANGES
index d09a47e03..360c49da6 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,9 @@
CHANGES IN FLTK 1.3.3 RELEASED: MMM DD YYYY
+ - added class Fl_Copy_Surface allowing to copy graphical data to the clipboard
+ in a cross-platform way.
+ - added support for pasting graphical data from the clipboard to an FLTK widget.
+ - added class Fl_Image_Surface allowing to draw into an Fl_Image object.
- removed constraint that lines are limited to 1024 chars in widget labels and browser lines (STR #2990).
- fix crash if Fl_Window::flush() was called, but window not shown() (STR #3028).
- new method Fl::scheme_is(const char *name) returns 1 if current scheme is name.
diff --git a/FL/Fl.H b/FL/Fl.H
index 148bca0ee..020f38cce 100644
--- a/FL/Fl.H
+++ b/FL/Fl.H
@@ -137,6 +137,8 @@ public: // should be private!
static int e_keysym;
static char* e_text;
static int e_length;
+ static void *e_clipboard_data;
+ static const char *e_clipboard_type;
static Fl_Event_Dispatch e_dispatch;
static Fl_Widget* belowmouse_;
static Fl_Widget* pushed_;
@@ -151,7 +153,9 @@ public: // should be private!
static void reset_marked_text(); // resets marked text
static void insertion_point_location(int x, int y, int height); // sets window coordinates & height of insertion point
#endif
-#endif
+#endif // FL_DOXYGEN
+
+
/**
If true then flush() will do something.
*/
@@ -729,6 +733,16 @@ public:
you paste a nul character.
*/
static int event_length() {return e_length;}
+
+ /** During an FL_PASTE event of non-textual data, returns a pointer to the pasted data.
+ The returned data is an Fl_Image * when the result of Fl::event_clipboard_type() is Fl::clipboard_image.
+ */
+ static void *event_clipboard() { return e_clipboard_data; }
+ /** Returns the type of the pasted data during an FL_PASTE event.
+ This type can be Fl::clipboard_plain_text or Fl::clipboard_image.
+ */
+ static const char *event_clipboard_type() {return e_clipboard_type; }
+
static int compose(int &del);
static void compose_reset();
@@ -763,19 +777,39 @@ public:
/**
Copies the data pointed to by \p stuff to the selection buffer
(\p destination is 0) or
- the clipboard (\p destination is 1); \p len is the number of relevant
- bytes in \p stuff.
+ the clipboard (\p destination is 1).
+ \p len is the number of relevant bytes in \p stuff.
+ \p type is always Fl::clipboard_plain_text.
The selection buffer is used for
middle-mouse pastes and for drag-and-drop selections. The
clipboard is used for traditional copy/cut/paste operations.
+
+ \note This function is, at present, intended only to copy UTF-8 encoded textual data.
+ To copy graphical data, use the Fl_Copy_Surface class. The \p type argument may allow
+ in the future to copy other kinds of data.
*/
- static void copy(const char* stuff, int len, int destination = 0); // platform dependent
+#if FLTK_ABI_VERSION >= 10303 || defined(FL_DOXYGEN)
+ static void copy(const char* stuff, int len, int destination = 0, const char *type = Fl::clipboard_plain_text); // platform dependent
+#else
+ static void copy(const char* stuff, int len, int destination, const char *type);
+ static void copy(const char* stuff, int len, int destination = 0);
+#endif
+
+#if !(defined(__APPLE__) || defined(WIN32) || defined(FL_DOXYGEN))
+ static void copy_image(const unsigned char* data, int W, int H, int destination = 0); // platform dependent
+#endif
/**
Pastes the data from the selection buffer (\p source is 0) or the clipboard
- (\p source is 1) into \p receiver.
- Set things up so the receiver widget will be called with an FL_PASTE event some
- time in the future with the data from the specified \p source in Fl::event_text()
- and the number of characters in Fl::event_length().
+ (\p source is 1) into \p receiver. If \p source is 1,
+ the optional \p type argument indicates what type of data is requested from the clipboard
+ (at present, Fl::clipboard_plain_text - requesting text data - and
+ Fl::clipboard_image - requesting image data - are possible).
+ Set things up so the handle function of the \p receiver widget will be called with an FL_PASTE event some
+ time in the future if the clipboard does contain data of the requested type. During processing of this event,
+ and if \p type is Fl::clipboard_plain_text, the text data from the specified \p source are in Fl::event_text()
+ with UTF-8 encoding, and the number of characters in Fl::event_length();
+ if \p type is Fl::clipboard_image, Fl::event_clipboard() returns a pointer to the
+ image data, as an Fl_Image *.
The receiver
should be prepared to be called \e directly by this, or for
it to happen \e later, or possibly <i>not at all</i>. This
@@ -786,8 +820,21 @@ public:
The selection buffer is used for middle-mouse pastes and for
drag-and-drop selections. The clipboard is used for traditional
copy/cut/paste operations.
- */
- static void paste(Fl_Widget &receiver, int source /*=0*/); // platform dependent
+
+ \par Platform details for image data:
+ \li Unix/Linux platform: Image data in PNG or BMP formats are recognized. Requires linking with the fltk_images library.
+ \li MSWindows platform: Both bitmap and vectorial (Enhanced metafile) data from clipboard
+ can be pasted as image data.
+ \li Mac OS X platform: Both bitmap (TIFF) and vectorial (PDF) data from clipboard
+ can be pasted as image data.
+
+ */
+#if FLTK_ABI_VERSION >= 10303 || defined(FL_DOXYGEN)
+ static void paste(Fl_Widget &receiver, int source, const char *type = Fl::clipboard_plain_text); // platform dependent
+#else
+ static void paste(Fl_Widget &receiver, int source, const char *type);
+ static void paste(Fl_Widget &receiver, int source /*=0*/);
+#endif
/**
FLTK will call the registered callback whenever there is a change to the
selection buffer or the clipboard. The source argument indicates which
@@ -815,6 +862,17 @@ public:
buffer or the clipboard.
*/
static void remove_clipboard_notify(Fl_Clipboard_Notify_Handler h);
+ /** Returns non 0 if the clipboard contains data matching \p type.
+ \p type can be Fl::clipboard_plain_text or Fl::clipboard_image.
+ */
+ static int clipboard_contains(const char *type);
+ /** Denotes plain textual data
+ */
+ static char const * const clipboard_plain_text;
+ /** Denotes image data
+ */
+ static char const * const clipboard_image;
+
/**
Initiate a Drag And Drop operation. The selection buffer should be
filled with relevant data before calling this method. FLTK will
diff --git a/FL/Fl_Copy_Surface.H b/FL/Fl_Copy_Surface.H
new file mode 100644
index 000000000..fc116f3b5
--- /dev/null
+++ b/FL/Fl_Copy_Surface.H
@@ -0,0 +1,135 @@
+//
+// "$Id: Fl_Copy_Surface.H 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
+//
+
+#ifndef Fl_Copy_Surface_H
+#define Fl_Copy_Surface_H
+
+#include <FL/Fl_Paged_Device.H>
+#include <FL/Fl_Printer.H>
+
+#if defined(__APPLE__)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+/** Supports copying of graphical data to the clipboard.
+
+ <br> After creation of an Fl_Copy_Surface object, call set_current() on it, and all subsequent graphics requests
+ will be recorded in the clipboard. It's possible to draw widgets (using Fl_Copy_Surface::draw()
+ ) or to use any of the \ref fl_drawings or the \ref fl_attributes.
+ Finally, delete the Fl_Copy_Surface object to load the clipboard with the graphical data.
+ <br> Fl_GL_Window 's can be copied to the clipboard as well.
+ <br> Usage example:
+ \code
+ Fl_Widget *g = ...; // a widget you want to copy to the clipboard
+ Fl_Copy_Surface *copy_surf = new Fl_Copy_Surface(g->w(), g->h()); // create an Fl_Copy_Surface object
+ copy_surf->set_current(); // direct graphics requests to the clipboard
+ fl_color(FL_WHITE); fl_rectf(0, 0, g->w(), g->h()); // draw a white background
+ copy_surf->draw(g); // draw the g widget in the clipboard
+ delete copy_surf; // after this, the clipboard is loaded
+ Fl_Display_Device::display_device()->set_current(); // direct graphics requests back to the display
+ \endcode
+ Platform details:
+ \li MSWindows: Transparent RGB images copy without transparency.
+ The graphical data is copied to the clipboard as an 'enhanced metafile'.
+ \li Mac OS: The graphical data is copied to the clipboard (a.k.a. pasteboard) in 2 'flavors':
+ 1) in vectorial form as PDF data; 2) in bitmap form as a TIFF image (or PICT for Mac OS 10.3).
+ Applications to which the clipboard content is pasted can use the flavor that suits them best.
+ \li X11: the graphical data is copied to the clipboard as an image in BMP format.
+*/
+class Fl_Copy_Surface : public Fl_Surface_Device {
+private:
+ int width;
+ int height;
+ Fl_Paged_Device *helper;
+#ifdef __APPLE__
+ CFMutableDataRef pdfdata;
+ CGContextRef oldgc;
+ CGContextRef gc;
+ void prepare_copy_pdf_and_tiff(int w, int h);
+ void complete_copy_pdf_and_tiff();
+ void init_PDF_context(int w, int h);
+ static size_t MyPutBytes(void* info, const void* buffer, size_t count);
+#elif defined(WIN32)
+ HDC oldgc;
+ HDC gc;
+#else // Xlib
+ Fl_Offscreen xid;
+ Window oldwindow;
+ Fl_Surface_Device *_ss;
+#endif
+public:
+ static const char *class_id;
+ const char *class_name() {return class_id;};
+ Fl_Copy_Surface(int w, int h);
+ ~Fl_Copy_Surface();
+ void set_current();
+ void draw(Fl_Widget* widget, int delta_x = 0, int delta_y = 0);
+};
+
+#if defined(__APPLE__)
+
+/* Mac class to reimplement Fl_Paged_Device::printable_rect() */
+class Fl_Quartz_Surface_ : public Fl_System_Printer {
+protected:
+ int width;
+ int height;
+public:
+ static const char *class_id;
+ const char *class_name() {return class_id;};
+ Fl_Quartz_Surface_(int w, int h);
+ virtual int printable_rect(int *w, int *h);
+ virtual ~Fl_Quartz_Surface_() {};
+};
+
+#elif defined(WIN32)
+
+/* Win class to implement translate()/untranslate() */
+class Fl_GDI_Surface_ : public Fl_Paged_Device {
+ int width;
+ int height;
+ int depth;
+ POINT origins[10];
+public:
+ static const char *class_id;
+ const char *class_name() {return class_id;};
+ Fl_GDI_Surface_();
+ virtual void translate(int x, int y);
+ virtual void untranslate();
+ virtual ~Fl_GDI_Surface_();
+};
+
+#elif !defined(FL_DOXYGEN)
+
+/* Xlib class to implement translate()/untranslate() */
+class Fl_Xlib_Surface_ : public Fl_Paged_Device {
+public:
+ static const char *class_id;
+ const char *class_name() {return class_id;};
+ Fl_Xlib_Surface_();
+ virtual void translate(int x, int y);
+ virtual void untranslate();
+ virtual ~Fl_Xlib_Surface_();
+};
+
+#endif
+
+#endif // Fl_Copy_Surface_H
+
+//
+// End of "$Id: $".
+//
diff --git a/FL/Fl_Image_Surface.H b/FL/Fl_Image_Surface.H
new file mode 100644
index 000000000..2d82238c2
--- /dev/null
+++ b/FL/Fl_Image_Surface.H
@@ -0,0 +1,90 @@
+//
+// "$Id: Fl_Image_Surface.H 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
+//
+
+#ifndef Fl_Image_Surface_H
+#define Fl_Image_Surface_H
+
+#include <FL/Fl_Copy_Surface.H>
+#include <FL/Fl_Image.H>
+
+
+/** Directs all graphics requests to an Fl_Image.
+
+ After creation of an Fl_Image_Surface object, call set_current() on it, and all subsequent graphics requests
+ will be recorded in the image. It's possible to draw widgets (using Fl_Image_Surface::draw())
+ or to use any of the \ref fl_drawings or the \ref fl_attributes.
+ Finally, call image() on the object to obtain a newly allocated Fl_Image object.
+ <br> Fl_GL_Window objects can be drawn in the image as well.
+
+ <br> Usage example:
+ \code
+ Fl_Widget *g = ...; // a widget you want to draw in an image
+ Fl_Image_Surface *img_surf = new Fl_Image_Surface(g->w(), g->h()); // create an Fl_Image_Surface object
+ img_surf->set_current(); // direct graphics requests to the image
+ fl_color(FL_WHITE); fl_rectf(0, 0, g->w(), g->h()); // draw a white background
+ img_surf->draw(g); // draw the g widget in the image
+ Fl_Image* image = img_surf->image(); // get the resulting image
+ delete img_surf; // delete the img_surf object
+ Fl_Display_Device::display_device()->set_current(); // direct graphics requests back to the display
+ \endcode
+*/
+class Fl_Image_Surface : public Fl_Surface_Device {
+private:
+ Fl_Offscreen offscreen;
+ int width;
+ int height;
+ Fl_Paged_Device *helper;
+#ifdef __APPLE__
+#elif defined(WIN32)
+ HDC _sgc;
+ Window _sw;
+ Fl_Surface_Device *_ss;
+ int _savedc;
+#else
+ Fl_Surface_Device *previous;
+ Window pre_window;
+ GC gc;
+#endif
+public:
+ static const char *class_id;
+ const char *class_name() {return class_id;};
+ Fl_Image_Surface(int w, int h);
+ ~Fl_Image_Surface();
+ void set_current();
+ void draw(Fl_Widget*, int delta_x = 0, int delta_y = 0);
+ Fl_Image *image();
+};
+
+#ifdef __APPLE__
+/* Mac class to implement translate()/untranslate() for a flipped bitmap graphics context */
+class Fl_Quartz_Flipped_Surface_ : public Fl_Quartz_Surface_ {
+public:
+ static const char *class_id;
+ const char *class_name() {return class_id;};
+ Fl_Quartz_Flipped_Surface_(int w, int h);
+ void translate(int x, int y);
+ void untranslate();
+ virtual ~Fl_Quartz_Flipped_Surface_() {};
+};
+#endif
+
+#endif // Fl_Image_Surface_H
+
+//
+// End of "$Id: $".
+//
diff --git a/FL/mac.H b/FL/mac.H
index 8ea1bb0ad..734786e45 100644
--- a/FL/mac.H
+++ b/FL/mac.H
@@ -145,6 +145,7 @@ public:
void set_cursor(Fl_Cursor);
static CGImageRef CGImage_from_window_rect(Fl_Window *win, int x, int y, int w, int h);
static unsigned char *bitmap_from_window_rect(Fl_Window *win, int x, int y, int w, int h, int *bytesPerPixel);
+ static CFDataRef CGBitmapContextToTIFF(CGContextRef c);
static Fl_Region intersect_region_and_rect(Fl_Region current, int x,int y,int w, int h);
static CGContextRef watch_cursor_image(void);
static CGContextRef help_cursor_image(void);
diff --git a/examples/Makefile b/examples/Makefile
index 1320c3755..74e61c0f2 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -5,7 +5,8 @@ SHELL = /bin/sh
.SILENT:
# Executables
-ALL = howto-add_fd-and-popen$(EXEEXT) \
+ALL = clipboard$(EXEEXT) \
+ howto-add_fd-and-popen$(EXEEXT) \
howto-browser-with-icons$(EXEEXT) \
howto-drag-and-drop$(EXEEXT) \
howto-parse-args$(EXEEXT) \
diff --git a/examples/clipboard.cxx b/examples/clipboard.cxx
new file mode 100644
index 000000000..aa71a3998
--- /dev/null
+++ b/examples/clipboard.cxx
@@ -0,0 +1,168 @@
+//
+// "$Id: clipboard.cxx 9869 2013-04-09 20:11:28Z greg.ercolano $"
+//
+// Clipboard display test application 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_Window.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Image.H>
+#include <FL/Fl_Text_Buffer.H>
+#include <FL/Fl_Text_Display.H>
+#include <FL/Fl_Tabs.H>
+#include <FL/Fl_Button.H>
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
+
+/* Displays and follows the content of the clipboard with either image or text data
+ */
+
+Fl_Box *image_box;
+Fl_Box *image_size;
+Fl_Text_Display *display;
+
+class chess : public Fl_Box { // a box with a chess-like pattern below its image
+public:
+ chess(int x, int y, int w, int h) : Fl_Box(FL_FLAT_BOX,x,y,w,h,0) {
+ align(FL_ALIGN_CENTER | FL_ALIGN_CLIP);
+ }
+ void draw() {
+ draw_box();
+ Fl_Image *im = image();
+ if (im) { // draw the chess pattern below the box centered image
+ int X = x() + (w()-im->w())/2, Y = y() + (h()-im->h())/2, W = im->w(), H = im->h();
+ fl_push_clip(X,Y,W,H);
+ fl_push_clip(x(),y(),w(),h());
+ fl_color(FL_WHITE); fl_rectf(X,Y,W,H);
+ fl_color(FL_LIGHT2);
+ const int side = 4, side2 = 2*side;
+ for (int j=Y; j<Y+H; j+=side) {
+ for (int i=X + (j-Y)%side2; i<X+W; i+=side2) {
+ fl_rectf(i,j,side,side);
+ }
+ }
+ fl_pop_clip();
+ fl_pop_clip();
+ }
+ draw_label(); // draw the box image
+ }
+} *chess_obj;
+
+class clipboard_viewer : public Fl_Tabs { // use tabs to display as appropriate the image or textual content of the clipboard
+public:
+ clipboard_viewer(int x, int y, int w, int h) : Fl_Tabs(x,y,w,h) {};
+ virtual int handle(int event) {
+ if (event != FL_PASTE) return Fl_Tabs::handle(event);
+ if (strcmp(Fl::event_clipboard_type(), Fl::clipboard_image) == 0) { // an image is being pasted
+ Fl_Image *im = (Fl_Image*)Fl::event_clipboard(); // get it as an Fl_Image object
+ if (!im) return 1;
+ char title[300];
+ sprintf(title, "%dx%d",im->w(), im->h()); // display the image original size
+#ifdef WIN32
+ OpenClipboard(NULL); // display extra technical info about clipboard content
+ char *p=title + strlen(title);
+ int format = EnumClipboardFormats(0);
+ if (format && format < CF_MAX) { sprintf(p, " %d",format); p += strlen(p); }
+ while (format) {
+ format = EnumClipboardFormats(format);
+ if (format && format < CF_MAX) { sprintf(p, " %d",format); p += strlen(p); }
+ }
+ HANDLE h;
+ if (h = GetClipboardData(CF_DIB)) {
+ LPBITMAPINFO lpBI = (LPBITMAPINFO)GlobalLock(h);
+ sprintf(p, " biBitCount=%d biCompression=%d biClrUsed=%d",
+ lpBI->bmiHeader.biBitCount, lpBI->bmiHeader.biCompression, lpBI->bmiHeader.biClrUsed);
+ }
+ CloseClipboard();
+#endif
+ float scale_x = (float)im->w() / image_box->w(); // rescale the image if larger than the display box
+ float scale_y = (float)im->h() / image_box->h();
+ float scale = scale_x;
+ if (scale_y > scale) scale = scale_y;
+ if (scale > 1) {
+ Fl_Image *im2 = im->copy(im->w()/scale, im->h()/scale);
+ delete im;
+ im = im2;
+ }
+ Fl_Image *oldim = image_box->image();
+ if (oldim) delete oldim;
+ image_box->image(im); // show the scaled image
+ image_size->copy_label(title);
+ value(image_box->parent());
+ window()->redraw();
+ }
+ else { // text is being pasted
+ display->buffer()->text(Fl::event_text());
+ value(display);
+ display->redraw();
+ }
+ return 1;
+ }
+} *tabs;
+
+
+void cb(Fl_Widget *wid, clipboard_viewer *tabs)
+{
+ if (Fl::clipboard_contains(Fl::clipboard_image)) {
+ Fl::paste(*tabs, 1, Fl::clipboard_image); // try to find image in the clipboard
+ return;
+ }
+ if (Fl::clipboard_contains(Fl::clipboard_plain_text)) Fl::paste(*tabs, 1, Fl::clipboard_plain_text); // also try to find text
+}
+
+void clip_callback(int source, void *data) { // called after clipboard was changed or at application activation
+ if ( source == 1 ) cb(NULL, (clipboard_viewer *)data);
+}
+
+int main()
+{
+#if !(defined(__APPLE__) || defined(WIN32))
+ extern void fl_register_images();
+ fl_register_images(); // required to allow pasting of images
+#endif
+ Fl_Window* win = new Fl_Window(500, 550, "clipboard viewer");
+ tabs = new clipboard_viewer(0, 0, 500, 500);
+ Fl_Group *g = new Fl_Group( 5, 30, 490, 460, Fl::clipboard_image); // g will display the image form
+ g->box(FL_FLAT_BOX);
+ image_box = new chess(5, 30, 490, 450);
+ image_size = new Fl_Box(FL_NO_BOX, 5, 485, 490, 10, 0);
+ g->end();
+ g->selection_color(FL_INACTIVE_COLOR);
+
+ Fl_Text_Buffer *buffer = new Fl_Text_Buffer();
+ display = new Fl_Text_Display(5,30,490, 460, Fl::clipboard_plain_text); // display will display the text form
+ display->buffer(buffer);
+ display->selection_color(FL_INACTIVE_COLOR);
+ tabs->end();
+ tabs->resizable(display);
+
+ Fl_Group *g2 = new Fl_Group( 10,510,200,25);
+ Fl_Button *refresh = new Fl_Button(10,510,200,25, "Refresh from clipboard");
+ refresh->callback((Fl_Callback*)cb, tabs);
+ g2->end();
+ g2->resizable(NULL);
+ win->end();
+ win->resizable(tabs);
+ win->show();
+#if defined(__APPLE__) || defined(WIN32)
+ clip_callback(1, tabs); // use clipboard content at start
+#endif
+ Fl::add_clipboard_notify(clip_callback, tabs); // will update with new clipboard content immediately or at application activation
+ return Fl::run();
+}
+
+//
+// End of "$Id: $".
+//
diff --git a/ide/Xcode3/FLTK.xcodeproj/project.pbxproj b/ide/Xcode3/FLTK.xcodeproj/project.pbxproj
index fd40c50ea..69b65cbc0 100644
--- a/ide/Xcode3/FLTK.xcodeproj/project.pbxproj
+++ b/ide/Xcode3/FLTK.xcodeproj/project.pbxproj
@@ -329,6 +329,8 @@
7F66B1DA12BB924C00C67B59 /* Fl_Native_File_Chooser_MAC.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7F66B1D712BB924C00C67B59 /* Fl_Native_File_Chooser_MAC.mm */; };
7F66B1DB12BB924C00C67B59 /* Fl_Quartz_Printer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7F66B1D812BB924C00C67B59 /* Fl_Quartz_Printer.mm */; };
7F74F393190D7F0500C56950 /* fl_gleam.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 299E717718BC503400FF83F2 /* fl_gleam.cxx */; };
+ 7F76E016192FAD420071728B /* Fl_Copy_Surface.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 7F76E015192FAD420071728B /* Fl_Copy_Surface.cxx */; };
+ 7F76E018192FAD590071728B /* Fl_Image_Surface.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 7F76E017192FAD590071728B /* Fl_Image_Surface.cxx */; };
7FFDE4AD171D8AA3008753A3 /* Fl_Sys_Menu_Bar.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7FFDE4AC171D8AA3008753A3 /* Fl_Sys_Menu_Bar.mm */; };
812129561A1981D6DEFBCBFB /* Fl_Positioner.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 05BBBFE4BED0452E5D6A81F7 /* Fl_Positioner.cxx */; };
812761E94039F13357F56EE6 /* fltk_png.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 98A16A4EC098BA7DB21E13DC /* fltk_png.framework */; };
@@ -4338,6 +4340,10 @@
7F66B1D612BB924C00C67B59 /* Fl_cocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Fl_cocoa.mm; path = ../../src/Fl_cocoa.mm; sourceTree = SOURCE_ROOT; };
7F66B1D712BB924C00C67B59 /* Fl_Native_File_Chooser_MAC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Fl_Native_File_Chooser_MAC.mm; path = ../../src/Fl_Native_File_Chooser_MAC.mm; sourceTree = SOURCE_ROOT; };
7F66B1D812BB924C00C67B59 /* Fl_Quartz_Printer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Fl_Quartz_Printer.mm; path = ../../src/Fl_Quartz_Printer.mm; sourceTree = SOURCE_ROOT; };
+ 7F76E015192FAD420071728B /* Fl_Copy_Surface.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Fl_Copy_Surface.cxx; path = ../../src/Fl_Copy_Surface.cxx; sourceTree = SOURCE_ROOT; };
+ 7F76E017192FAD590071728B /* Fl_Image_Surface.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Fl_Image_Surface.cxx; path = ../../src/Fl_Image_Surface.cxx; sourceTree = SOURCE_ROOT; };
+ 7F76E019192FAD740071728B /* Fl_Copy_Surface.H */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Fl_Copy_Surface.H; path = ../../FL/Fl_Copy_Surface.H; sourceTree = SOURCE_ROOT; };
+ 7F76E01A192FAD840071728B /* Fl_Image_Surface.H */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Fl_Image_Surface.H; path = ../../FL/Fl_Image_Surface.H; sourceTree = SOURCE_ROOT; };
7F784151AF1B748D0F3DB1C0 /* forms.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = forms.cxx; path = ../../test/forms.cxx; sourceTree = SOURCE_ROOT; };
7FAC914955D699539F73B996 /* bitmap.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bitmap.cxx; path = ../../test/bitmap.cxx; sourceTree = SOURCE_ROOT; };
7FC91721DA7888C8A1FD762E /* input_choice.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = input_choice.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -5459,6 +5465,7 @@
4D55C9FE101986BC47029A16 /* Fl_Choice.cxx */,
F5CAE7DC565B8398F02ED7BA /* Fl_Clock.cxx */,
058BCBC36ADE724A418F1C43 /* Fl_Color_Chooser.cxx */,
+ 7F76E015192FAD420071728B /* Fl_Copy_Surface.cxx */,
E9893274B0B6C5F24730235F /* Fl_Counter.cxx */,
D9A7DCBAFF41CBC3DCB67C6F /* Fl_Device.cxx */,
3AFC31503AB99F6D00BAC647 /* Fl_Dial.cxx */,
@@ -5471,6 +5478,7 @@
806103D71A8CD0075BF8E1DA /* Fl_Group.cxx */,
790419B5A0AD64D66F9B19E1 /* Fl_Help_View.cxx */,
6DCFF326B588B1B27618F28C /* Fl_Image.cxx */,
+ 7F76E017192FAD590071728B /* Fl_Image_Surface.cxx */,
63CB19652C470F1E58DCF01E /* Fl_Input.cxx */,
D531F77A15AACC9E297B4490 /* Fl_Input_.cxx */,
BA7B1E9C7AA7316E98D369C2 /* Fl_Light_Button.cxx */,
@@ -6219,6 +6227,7 @@
C8AE10A8DDF53B8B27E3215A /* Fl_Choice.H */,
9BCF393F94482AE7C7421397 /* Fl_Clock.H */,
2CD1EF8B4BFD0820E9A42641 /* Fl_Color_Chooser.H */,
+ 7F76E019192FAD740071728B /* Fl_Copy_Surface.H */,
CE97D58B5B0F1A2A7DB2A3FF /* Fl_Counter.H */,
82863AEFE086C3469C386C22 /* Fl_Device.H */,
02C21BB31E7DDFE9E76F4997 /* Fl_Dial.H */,
@@ -6241,6 +6250,7 @@
AE1717E43F50EBA343960B1E /* Fl_Hor_Slider.H */,
5B8BFBDF9C48A998F0781C9E /* Fl_Hor_Value_Slider.H */,
332598626430923370C48554 /* Fl_Image.H */,
+ 7F76E01A192FAD840071728B /* Fl_Image_Surface.H */,
EFEEE679374D7E4191873447 /* Fl_Input.H */,
A5166C3C9311628F6E450095 /* Fl_Input_.H */,
648E9C3B61328280244FCCA5 /* Fl_Input_Choice.H */,
@@ -9515,6 +9525,8 @@
299CB8A2848CB844BCEC7829 /* Fl_Paged_Device.cxx in Sources */,
7F74F393190D7F0500C56950 /* fl_gleam.cxx in Sources */,
01C68DB0192C6089000BD75C /* Fl_sleep.cxx in Sources */,
+ 7F76E016192FAD420071728B /* Fl_Copy_Surface.cxx in Sources */,
+ 7F76E018192FAD590071728B /* Fl_Image_Surface.cxx in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/ide/Xcode4/FLTK.xcodeproj/project.pbxproj b/ide/Xcode4/FLTK.xcodeproj/project.pbxproj
index a38e95c12..ce8e93d1b 100644
--- a/ide/Xcode4/FLTK.xcodeproj/project.pbxproj
+++ b/ide/Xcode4/FLTK.xcodeproj/project.pbxproj
@@ -328,6 +328,8 @@
7F66B1D912BB924C00C67B59 /* Fl_cocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7F66B1D612BB924C00C67B59 /* Fl_cocoa.mm */; };
7F66B1DA12BB924C00C67B59 /* Fl_Native_File_Chooser_MAC.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7F66B1D712BB924C00C67B59 /* Fl_Native_File_Chooser_MAC.mm */; };
7F66B1DB12BB924C00C67B59 /* Fl_Quartz_Printer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7F66B1D812BB924C00C67B59 /* Fl_Quartz_Printer.mm */; };
+ 7FA5C2BE192FAEBB00519823 /* Fl_Copy_Surface.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 7FA5C2BD192FAEBB00519823 /* Fl_Copy_Surface.cxx */; };
+ 7FA5C2C0192FAECA00519823 /* Fl_Image_Surface.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 7FA5C2BF192FAECA00519823 /* Fl_Image_Surface.cxx */; };
7FDBB8F416B2D1EA00AE76EF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7F92032516B1A90A000FC50F /* Localizable.strings */; };
7FDBB8F516B2D1EE00AE76EF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7F92032216B1A909000FC50F /* Localizable.strings */; };
7FDBB8F616B2D1FA00AE76EF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7F92031F16B1A909000FC50F /* Localizable.strings */; };
@@ -4334,6 +4336,10 @@
7F92032016B1A909000FC50F /* German */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = German; path = Localizable.strings; sourceTree = "<group>"; };
7F92032316B1A90A000FC50F /* Italian */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = Italian; path = Localizable.strings; sourceTree = "<group>"; };
7F92032616B1A90A000FC50F /* Spanish */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = Spanish; path = Localizable.strings; sourceTree = "<group>"; };
+ 7FA5C2BD192FAEBB00519823 /* Fl_Copy_Surface.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Fl_Copy_Surface.cxx; path = ../../src/Fl_Copy_Surface.cxx; sourceTree = SOURCE_ROOT; };
+ 7FA5C2BF192FAECA00519823 /* Fl_Image_Surface.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Fl_Image_Surface.cxx; path = ../../src/Fl_Image_Surface.cxx; sourceTree = SOURCE_ROOT; };
+ 7FA5C2C1192FAEE300519823 /* Fl_Image_Surface.H */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Fl_Image_Surface.H; path = ../../FL/Fl_Image_Surface.H; sourceTree = SOURCE_ROOT; };
+ 7FA5C2C2192FAEF200519823 /* Fl_Copy_Surface.H */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Fl_Copy_Surface.H; path = ../../FL/Fl_Copy_Surface.H; sourceTree = SOURCE_ROOT; };
7FAC914955D699539F73B996 /* bitmap.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bitmap.cxx; path = ../../test/bitmap.cxx; sourceTree = SOURCE_ROOT; };
7FC91721DA7888C8A1FD762E /* input_choice.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = input_choice.app; sourceTree = BUILT_PRODUCTS_DIR; };
7FFDE551171D8D0D008753A3 /* Fl_Sys_Menu_Bar.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Fl_Sys_Menu_Bar.mm; path = ../../src/Fl_Sys_Menu_Bar.mm; sourceTree = SOURCE_ROOT; };
@@ -5453,6 +5459,7 @@
4D55C9FE101986BC47029A16 /* Fl_Choice.cxx */,
F5CAE7DC565B8398F02ED7BA /* Fl_Clock.cxx */,
058BCBC36ADE724A418F1C43 /* Fl_Color_Chooser.cxx */,
+ 7FA5C2BD192FAEBB00519823 /* Fl_Copy_Surface.cxx */,
E9893274B0B6C5F24730235F /* Fl_Counter.cxx */,
D9A7DCBAFF41CBC3DCB67C6F /* Fl_Device.cxx */,
3AFC31503AB99F6D00BAC647 /* Fl_Dial.cxx */,
@@ -5465,6 +5472,7 @@
806103D71A8CD0075BF8E1DA /* Fl_Group.cxx */,
790419B5A0AD64D66F9B19E1 /* Fl_Help_View.cxx */,
6DCFF326B588B1B27618F28C /* Fl_Image.cxx */,
+ 7FA5C2BF192FAECA00519823 /* Fl_Image_Surface.cxx */,
63CB19652C470F1E58DCF01E /* Fl_Input.cxx */,
D531F77A15AACC9E297B4490 /* Fl_Input_.cxx */,
BA7B1E9C7AA7316E98D369C2 /* Fl_Light_Button.cxx */,
@@ -6258,6 +6266,7 @@
C8AE10A8DDF53B8B27E3215A /* Fl_Choice.H */,
9BCF393F94482AE7C7421397 /* Fl_Clock.H */,
2CD1EF8B4BFD0820E9A42641 /* Fl_Color_Chooser.H */,
+ 7FA5C2C2192FAEF200519823 /* Fl_Copy_Surface.H */,
CE97D58B5B0F1A2A7DB2A3FF /* Fl_Counter.H */,
82863AEFE086C3469C386C22 /* Fl_Device.H */,
02C21BB31E7DDFE9E76F4997 /* Fl_Dial.H */,
@@ -6280,6 +6289,7 @@
AE1717E43F50EBA343960B1E /* Fl_Hor_Slider.H */,
5B8BFBDF9C48A998F0781C9E /* Fl_Hor_Value_Slider.H */,
332598626430923370C48554 /* Fl_Image.H */,
+ 7FA5C2C1192FAEE300519823 /* Fl_Image_Surface.H */,
EFEEE679374D7E4191873447 /* Fl_Input.H */,
A5166C3C9311628F6E450095 /* Fl_Input_.H */,
648E9C3B61328280244FCCA5 /* Fl_Input_Choice.H */,
@@ -9552,6 +9562,8 @@
CE14EC6653D4EF4779992758 /* is_right2left.c in Sources */,
9DD7A2B6D63D30C07781F446 /* is_spacing.c in Sources */,
299CB8A2848CB844BCEC7829 /* Fl_Paged_Device.cxx in Sources */,
+ 7FA5C2BE192FAEBB00519823 /* Fl_Copy_Surface.cxx in Sources */,
+ 7FA5C2C0192FAECA00519823 /* Fl_Image_Surface.cxx in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
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 \
diff --git a/test/device.cxx b/test/device.cxx
index 8abb99d08..babf34d8c 100644
--- a/test/device.cxx
+++ b/test/device.cxx
@@ -21,6 +21,7 @@
#include <FL/Fl_Overlay_Window.H>
#include <FL/Fl_Light_Button.H>
+#include <FL/Fl_Radio_Round_Button.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Clock.H>
#include "pixmaps/porsche.xpm"
@@ -30,8 +31,8 @@
#include <FL/Fl_Printer.H>
-
-//#include "fl_printer_chooser.H"
+#include <FL/Fl_Copy_Surface.H>
+#include <FL/Fl_Image_Surface.H>
#include <FL/Fl_File_Chooser.H>
#include <FL/fl_draw.H>
@@ -448,7 +449,6 @@ class MyWidget5: public Fl_Box{
protected:
void draw(){
Fl_Box::draw();
- //fl_push_clip(x(),y(),w(),h());
fl_push_matrix();
fl_translate(x(),y());
@@ -512,11 +512,6 @@ protected:
fl_end_complex_polygon();
fl_pop_matrix();
-
- // fl_color(FL_BLACK);
- // fl_line_style(0);
- //fl_pop_clip();
-
};
public:
MyWidget5(int x, int y):Fl_Box(x,y,230,250, "Complex (double) drawings:\nBlue ellipse may not be\ncorrectly transformed\ndue to non-orthogonal\ntransformation"){
@@ -551,37 +546,53 @@ void make_image() {
}
-void print(Fl_Widget *, void *w) {
- Fl_Widget * g = (Fl_Widget *)w;
+Fl_Widget *target;
+const char *operation;
+
+void copy(Fl_Widget *, void *data) {
+ if (strcmp(operation, "Fl_Image_Surface") == 0) {
+ Fl_Image_Surface *rgb_surf = new Fl_Image_Surface(target->w()+20, target->h()+10);
+ rgb_surf->set_current();
+ fl_color(FL_BLUE);fl_rectf(0,0,1000,1000);
+ rgb_surf->draw(target,10,5);
+ Fl_Image *img = rgb_surf->image();
+ delete rgb_surf;
+ Fl_Display_Device::display_device()->set_current();
+ Fl_Window* g2 = new Fl_Window(img->w(), img->h());
+ Fl_Box *b = new Fl_Box(FL_NO_BOX,0,0,img->w(), img->h(),0);
+ b->image(img);
+ g2->end();
+ g2->show();
+ return;
+ }
- Fl_Printer * p = new Fl_Printer();
- if (!p->start_job(1)) {
- p->start_page();
- p->print_window(g->window());
- p->end_page();
- p->end_job();
+
+ if (strcmp(operation, "Fl_Copy_Surface") == 0) {
+ Fl_Copy_Surface *copy_surf = new Fl_Copy_Surface(target->w()+10, target->h()+20);
+ copy_surf->set_current();
+ fl_color(FL_YELLOW);fl_rectf(0,0,1000,1000);
+ copy_surf->draw(target, 5, 10);
+ delete copy_surf;
+ Fl_Display_Device::display_device()->set_current();
+ }
+
+ if (strcmp(operation, "Fl_Printer") == 0) {
+ Fl_Printer * p = new Fl_Printer();
+ if (!p->start_job(1)) {
+ p->start_page();
+ if (target->as_window()) p->print_window(target->as_window());
+ else p->print_widget(target);
+ p->end_page();
+ p->end_job();
+ }
+ delete p;
}
- delete p;
}
-/*void print2(Fl_Widget *, void *w) {
- Fl_Widget * g = (Fl_Widget *)w;
- Fl_Printer * p = fl_printer_chooser();
- if(!p) return;
- p->page(Fl_Printer::A4);
- // fitting inside margins 1 inch wide
- p->place(g, FL_INCH, FL_INCH, p->page_width() - 2 * FL_INCH, p->page_height() - 2 * FL_INCH, FL_ALIGN_CENTER);
- Fl_Device * c = p->set_current();
- fl_draw(g);
- c->set_current();
- delete p;
- };*/
-
class My_Button:public Fl_Button{
protected:
void draw(){
- // Fl_Button::draw();
if (type() == FL_HIDDEN_BUTTON) return;
Fl_Color col = value() ? selection_color() : color();
draw_box(value() ? (down_box()?down_box():fl_down(box())) : box(), col);
@@ -598,15 +609,22 @@ public:
};
+void target_cb(Fl_Widget* wid, void *data)
+{
+ target = (Fl_Widget*)data;
+}
+
+void operation_cb(Fl_Widget* wid, void *data)
+{
+ operation = wid->label();
+}
+
int main(int argc, char ** argv) {
- //Fl::scheme("plastic");
-
-
+ //Fl::scheme("plastic");
Fl_Window * w2 = new Fl_Window(500,560,"Graphics test");
-
Fl_Group *c2 =new Fl_Group(3, 43, 494, 514 );
new MyWidget(10,140);
@@ -643,14 +661,12 @@ int main(int argc, char ** argv) {
but5.labelfont(FL_BOLD|FL_ITALIC);
but5.labeltype(FL_SHADOW_LABEL);
but5.box(FL_ROUND_UP_BOX);
- // but5.selection_color(FL_WHITE);
Fl_Button but6(360, 460, 120, 30, "Plastic");
but6.box(FL_PLASTIC_UP_BOX);
- //Fl_Button but7(, 480, 120, 30, "Engraved box");
- //but7.box(FL_ENGRAVED_BOX);
- { Fl_Group* o = new Fl_Group(360, 495, 120, 40);
+ Fl_Group *group;
+ { Fl_Group* o = new Fl_Group(360, 495, 120, 40); group=o;
o->box(FL_UP_BOX);
{ Fl_Group* o = new Fl_Group(365, 500, 110, 30);
o->box(FL_THIN_UP_FRAME);
@@ -673,16 +689,30 @@ int main(int argc, char ** argv) {
tx.hide();
c2->end();
- Fl_Button *b4 = new Fl_Button(10,5, 150, 25, "Print");
- b4->callback(print,c2);
- /*Fl_Button *b5 = new Fl_Button(165,5, 90, 25, "Print");
- b5->tooltip("This is a tooltip");
- b5->callback(print2,c2);*/
+
+ Fl_Radio_Round_Button *rb;
+ Fl_Window *w3 = new Fl_Window(2,5,w2->w()-10,55);
+ w3->box(FL_DOWN_BOX);
+ Fl_Group *g1 = new Fl_Group(w3->x(),w3->y(),w3->w(),w3->h());
+ rb = new Fl_Radio_Round_Button(5,5,150,12, "Fl_Image_Surface");
+ rb->set(); rb->callback(operation_cb, NULL); operation = rb->label();
+ rb = new Fl_Radio_Round_Button(5,22,150,12, "Fl_Copy_Surface"); rb->callback(operation_cb, NULL);
+ rb = new Fl_Radio_Round_Button(5,39,150,12, "Fl_Printer"); rb->callback(operation_cb, NULL);
+ g1->end();
+
+ Fl_Group *g2 = new Fl_Group(w3->x(),w3->y(),w3->w(),w3->h());
+ rb = new Fl_Radio_Round_Button(170,5,150,12, "Window");
+ rb->set(); rb->callback(target_cb, w2); target = w2;
+ rb = new Fl_Radio_Round_Button(170,22,150,12, "Sub-window"); rb->callback(target_cb, w3);
+ rb = new Fl_Radio_Round_Button(170,39,150,12, "Group"); rb->callback(target_cb, group);
+ g2->end();
+ Fl_Button *b4 = new Fl_Button(330, (w3->h() - 25)/2, 150, 25, "GO");
+ b4->callback((Fl_Callback*)copy,NULL);
+ w3->end();
w2->end();
w2->show(argc, argv);
-
Fl::run();
return 0;
}