mirror of
https://github.com/fltk/fltk.git
synced 2026-02-05 07:49:50 +08:00
- replace FL_OVERRIDE and NULL with 'override' and 'nullptr', resp. - remove useless global variable (declare it locally in main) - standardize callbacks using standard callback signatures - rename some callbacks with more appropriate names - fix layout of image display and image size info - improve alignment of text display - set window size_range (minimal window size)
277 lines
8.7 KiB
C++
277 lines
8.7 KiB
C++
//
|
|
// Clipboard display test application for the Fast Light Tool Kit (FLTK).
|
|
//
|
|
// Copyright 1998-2025 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:
|
|
//
|
|
// https://www.fltk.org/COPYING.php
|
|
//
|
|
// Please see the following page on how to report bugs and issues:
|
|
//
|
|
// https://www.fltk.org/bugs.php
|
|
//
|
|
|
|
#include <FL/Fl.H>
|
|
#include <FL/Fl_Window.H>
|
|
#include <FL/Fl_Box.H>
|
|
#include <FL/Fl_PNG_Image.H>
|
|
#include <FL/Fl_Shared_Image.H>
|
|
#include <FL/Fl_Text_Display.H>
|
|
#include <FL/Fl_Tabs.H>
|
|
#include <FL/Fl_Flex.H>
|
|
#include <FL/Fl_Button.H>
|
|
#include <FL/Fl_Check_Button.H>
|
|
#include <FL/Fl_Native_File_Chooser.H>
|
|
#include <FL/fl_draw.H>
|
|
#include <FL/fl_ask.H>
|
|
|
|
#include <stdio.h>
|
|
|
|
// optional: display extra technical info about clipboard content if defined
|
|
// #define DEBUG_CLIPBOARD_DATA
|
|
|
|
#if defined(_WIN32) && defined(DEBUG_CLIPBOARD_DATA)
|
|
#include <windows.h>
|
|
#endif // _WIN32 && DEBUG_CLIPBOARD_DATA
|
|
|
|
/* Displays and follows the content of the clipboard with either image or text data
|
|
*/
|
|
|
|
Fl_Box *image_box; // to view an image
|
|
Fl_Box *image_size; // to view image size
|
|
Fl_Text_Display *display; // to view clipboard text
|
|
Fl_Flex *flex; // flexible button layout
|
|
Fl_Button *save; // save PNG
|
|
Fl_Check_Button *wrap; // wrap mode
|
|
Fl_RGB_Image *cl_img; // image from clipboard
|
|
|
|
inline int fl_min(int a, int b) {
|
|
return (a < b ? a : b);
|
|
}
|
|
|
|
// a box with a chess-like pattern below its image
|
|
class chess : public Fl_Box {
|
|
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() override {
|
|
draw_box();
|
|
Fl_Image *img = image();
|
|
if (img) { // draw the chess pattern below the box centered image
|
|
int X = x() + (w() - img->w()) / 2;
|
|
int Y = y() + (h() - img->h()) / 2;
|
|
int W = img->w();
|
|
int H = img->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
|
|
}
|
|
};
|
|
|
|
#define TAB_COLOR FL_DARK3
|
|
|
|
// use tabs to display either the image or textual content of the clipboard
|
|
class clipboard_viewer : public Fl_Tabs {
|
|
public:
|
|
clipboard_viewer(int x, int y, int w, int h)
|
|
: Fl_Tabs(x, y, w, h) {}
|
|
|
|
void layout() { // re-arrange buttons depending on tab
|
|
if (value() == display) { // text
|
|
save->hide();
|
|
wrap->show();
|
|
} else { // image
|
|
save->show();
|
|
wrap->hide();
|
|
}
|
|
flex->layout();
|
|
}
|
|
|
|
int handle(int event) override {
|
|
if (event != FL_PASTE) {
|
|
auto val = value();
|
|
int ret = Fl_Tabs::handle(event);
|
|
if (value() != val) { // tabs have been changed
|
|
layout(); // re-arrange buttons
|
|
}
|
|
return ret;
|
|
}
|
|
if (strcmp(Fl::event_clipboard_type(), Fl::clipboard_image) == 0) { // an image is being pasted
|
|
cl_img = (Fl_RGB_Image *)Fl::event_clipboard(); // get it as an Fl_RGB_Image object
|
|
if (!cl_img)
|
|
return 1;
|
|
char title[300];
|
|
snprintf(title, 300, "%dx%d", cl_img->w(), cl_img->h()); // display the image original size
|
|
|
|
// optional: display extra technical info about clipboard content
|
|
|
|
#if defined(_WIN32) && defined(DEBUG_CLIPBOARD_DATA)
|
|
|
|
OpenClipboard(nullptr); //
|
|
char *p = title + strlen(title);
|
|
int format = EnumClipboardFormats(0);
|
|
if (format && format < CF_MAX) {
|
|
snprintf(p, sizeof(title) - strlen(title), " %d", format);
|
|
p += strlen(p);
|
|
}
|
|
while (format) {
|
|
format = EnumClipboardFormats(format);
|
|
if (format && format < CF_MAX) {
|
|
snprintf(p, sizeof(title) - strlen(title), " %d", format);
|
|
p += strlen(p);
|
|
}
|
|
}
|
|
HANDLE h;
|
|
if ((h = GetClipboardData(CF_DIB))) {
|
|
LPBITMAPINFO lpBI = (LPBITMAPINFO)GlobalLock(h);
|
|
snprintf(p, sizeof(title) - strlen(title), " biBitCount=%d biCompression=%d biClrUsed=%d",
|
|
lpBI->bmiHeader.biBitCount,
|
|
(int)lpBI->bmiHeader.biCompression,
|
|
(int)lpBI->bmiHeader.biClrUsed);
|
|
}
|
|
CloseClipboard();
|
|
|
|
#endif // _WIN32 && DEBUG_CLIPBOARD_DATA
|
|
|
|
Fl_Image *oldimg = image_box->image();
|
|
delete oldimg;
|
|
if (cl_img->w() > image_box->w() || cl_img->h() > image_box->h())
|
|
cl_img->scale(image_box->w(), image_box->h());
|
|
image_box->image(cl_img); // 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();
|
|
}
|
|
layout();
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
// clipboard viewer refresh callback:
|
|
// 2nd argument must be `clipboard_viewer *`
|
|
void refresh_cb(Fl_Widget *, void *v) {
|
|
auto tabs = (clipboard_viewer *)v;
|
|
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
|
|
}
|
|
|
|
// "Save PNG" callback
|
|
void save_cb(Fl_Widget *wid, void *) {
|
|
if (cl_img && !cl_img->fail()) {
|
|
Fl_Native_File_Chooser fnfc;
|
|
fnfc.title("Please select a .png file");
|
|
fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
|
|
fnfc.filter("PNG\t*.png\n");
|
|
fnfc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT);
|
|
if (fnfc.show())
|
|
return;
|
|
const char *filename = fnfc.filename();
|
|
if (filename)
|
|
fl_write_png(filename, cl_img);
|
|
} else {
|
|
fl_message("%s", "No image available");
|
|
}
|
|
}
|
|
|
|
// "wrap mode" callback (switch wrapping on/off)
|
|
void wrap_cb(Fl_Widget *w, void *d) {
|
|
auto display = (Fl_Text_Display *)d;
|
|
auto wrap = (Fl_Check_Button *)w;
|
|
if (wrap->value()) {
|
|
display->wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0);
|
|
} else {
|
|
display->wrap_mode(Fl_Text_Display::WRAP_NONE, 0);
|
|
}
|
|
display->redraw();
|
|
}
|
|
|
|
// called after clipboard was changed or at application activation
|
|
void clipboard_cb(int source, void *data) {
|
|
if (source == 1)
|
|
refresh_cb(nullptr, data);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
fl_register_images(); // required for the X11 platform to allow pasting of images
|
|
Fl_Window *win = new Fl_Window(500, 550, "FLTK Clipboard Viewer");
|
|
auto tabs = new clipboard_viewer(0, 0, 500, 500);
|
|
Fl_Group *g = new Fl_Group(5, 30, 490, 460, Fl::clipboard_image); // will display the image form
|
|
g->box(FL_FLAT_BOX);
|
|
image_box = new chess(5, 30, 490, 440);
|
|
image_size = new Fl_Box(FL_NO_BOX, 5, 472, 490, 16, 0);
|
|
image_size->align(FL_ALIGN_CENTER | FL_ALIGN_CLIP);
|
|
g->end();
|
|
g->resizable(image_box);
|
|
g->selection_color(TAB_COLOR);
|
|
|
|
Fl_Text_Buffer *buffer = new Fl_Text_Buffer();
|
|
display = new Fl_Text_Display(5, 40, 490, 455, Fl::clipboard_plain_text); // will display the text form
|
|
display->buffer(buffer);
|
|
display->selection_color(TAB_COLOR);
|
|
display->textfont(FL_COURIER); // use fixed font for text display
|
|
tabs->end();
|
|
tabs->resizable(display);
|
|
|
|
flex = new Fl_Flex(0, 510, 550, 25); // flexible button layout
|
|
flex->type(Fl_Flex::HORIZONTAL);
|
|
flex->margin(10, 0, 10, 0); // margins: left, top, right, bottom
|
|
flex->gap(10);
|
|
|
|
auto refresh = new Fl_Button(0, 0, 0, 0, "Refresh from clipboard");
|
|
flex->fixed(refresh, 200);
|
|
refresh->callback(refresh_cb, (void *)tabs);
|
|
|
|
save = new Fl_Button(0, 0, 0, 0 , "Save PNG");
|
|
flex->fixed(save, 120);
|
|
save->callback(save_cb);
|
|
|
|
wrap = new Fl_Check_Button(0, 0, 0, 0 , "wrap mode");
|
|
flex->fixed(wrap, 120);
|
|
wrap->callback(wrap_cb, display);
|
|
wrap->box(FL_UP_BOX);
|
|
wrap->visible_focus(0);
|
|
wrap->value(0);
|
|
|
|
flex->end();
|
|
win->end();
|
|
win->resizable(tabs);
|
|
win->size_range(380, 300);
|
|
win->show(argc, argv);
|
|
// TEST: set another default background color
|
|
#if (0)
|
|
if (argc < 2) {
|
|
Fl::set_color(FL_BACKGROUND_COLOR, 0xff, 0xee, 0xdd);
|
|
Fl::set_color(FL_BACKGROUND2_COLOR, 0xdd, 0xee, 0xff);
|
|
}
|
|
#endif
|
|
clipboard_cb(1, tabs); // use clipboard content at start
|
|
Fl::add_clipboard_notify(clipboard_cb, tabs); // will update with new clipboard content
|
|
Fl_Image::RGB_scaling(FL_RGB_SCALING_BILINEAR); // set bilinear image scaling method
|
|
return Fl::run();
|
|
}
|