From 3876bb03e1aaffdf0446531cdf53795c9aced89e Mon Sep 17 00:00:00 2001 From: Thomas Bernard Date: Wed, 19 Dec 2018 13:10:07 +0100 Subject: [PATCH] Load from clipboard --- src/const.h | 1 + src/filesel.c | 63 ++++++++-- src/loadsave.c | 314 ++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 297 insertions(+), 81 deletions(-) diff --git a/src/const.h b/src/const.h index 303086cf..87657e16 100644 --- a/src/const.h +++ b/src/const.h @@ -154,6 +154,7 @@ enum FILE_FORMATS FORMAT_MOTO, ///< Thomson MO/TO computers pictures FORMAT_HGR, ///< Apple II HGR and DHGR FORMAT_MISC, ///< Must be last of enum: others formats recognized by SDL_image (or recoil) + FORMAT_CLIPBOARD ///< To load/save from/to Clipboard }; /// Default format for 'save as' diff --git a/src/filesel.c b/src/filesel.c index b330d16f..71f3501b 100644 --- a/src/filesel.c +++ b/src/filesel.c @@ -1613,6 +1613,7 @@ byte Button_Load_or_Save(T_Selector_settings *settings, byte load, T_IO_Context char initial_comment[COMMENT_SIZE+1]; short window_shortcut; const char * directory_to_change_to = NULL; + int load_from_clipboard = 0; Selector=settings; @@ -1730,7 +1731,14 @@ byte Button_Load_or_Save(T_Selector_settings *settings, byte load, T_IO_Context Window_display_icon_sprite(bookmark_dropdown[temp]->Pos_X+3,bookmark_dropdown[temp]->Pos_Y+2,ICON_STAR); Display_bookmark(bookmark_dropdown[temp],temp); } - + +#if defined(WIN32) + if (load) + Window_set_normal_button(62,180,115,14,"From Clipboard",0,1,MOD_CTRL|KEY_v); // 14 + else + Window_set_normal_button(62,180,115,14,"To Clipboard",0,1,MOD_CTRL|KEY_c); // 14 +#endif + Change_directory(context->File_directory); Get_current_directory(Selector->Directory, Selector->Directory_unicode, MAX_PATH_CHARACTERS); @@ -2089,6 +2097,19 @@ byte Button_Load_or_Save(T_Selector_settings *settings, byte load, T_IO_Context New_preview_is_needed=1; Reset_quicksearch(); break; + case 14: // From/to clipboard + if (load) + { + // paste from clipboard + load_from_clipboard = 1; + New_preview_is_needed = 1; + Reset_quicksearch(); + } + else + { + // copy to clipboard + } + break; default: if (clicked_button>=10 && clicked_button<10+NB_BOOKMARKS) { @@ -2374,13 +2395,21 @@ byte Button_Load_or_Save(T_Selector_settings *settings, byte load, T_IO_Context if (Timer_state==1) // Il faut afficher la preview { - if ( (Selector->Position+Selector->Offset>=Filelist.Nb_directories) && (Filelist.Nb_elements) ) + if ( load_from_clipboard || ((Selector->Position+Selector->Offset>=Filelist.Nb_directories) && (Filelist.Nb_elements)) ) { T_IO_Context preview_context; - - Init_context_preview(&preview_context, Selector_filename, Selector->Directory); - preview_context.Format = Selector->Format_filter; - preview_context.File_name_unicode = Selector_filename_unicode; + + if (load_from_clipboard) + { + Init_context_preview(&preview_context, NULL, NULL); + preview_context.Format = FORMAT_CLIPBOARD; + } + else + { + Init_context_preview(&preview_context, Selector_filename, Selector->Directory); + preview_context.Format = Selector->Format_filter; + preview_context.File_name_unicode = Selector_filename_unicode; + } Hide_cursor(); if (context->Type == CONTEXT_PALETTE) preview_context.Type = CONTEXT_PREVIEW_PALETTE; @@ -2390,7 +2419,6 @@ byte Button_Load_or_Save(T_Selector_settings *settings, byte load, T_IO_Context Update_window_area(0,0,Window_width,Window_height); Display_cursor(); - } Timer_state=2; // On arrête le chrono @@ -2400,12 +2428,21 @@ byte Button_Load_or_Save(T_Selector_settings *settings, byte load, T_IO_Context if (has_clicked_ok) { - strcpy(context->File_name, Selector_filename); - if (context->File_name_unicode) - Unicode_strlcpy(context->File_name_unicode, Selector_filename_unicode, MAX_PATH_CHARACTERS); - strcpy(context->File_directory, Selector->Directory); - if (!load) - context->Format = Selector->Format_filter; + if (load_from_clipboard) + { + strcpy(context->File_name, "CLIPBOARD.GIF"); + context->File_name_unicode[0] = 0; + context->Format = FORMAT_CLIPBOARD; + } + else + { + strcpy(context->File_name, Selector_filename); + if (context->File_name_unicode) + Unicode_strlcpy(context->File_name_unicode, Selector_filename_unicode, MAX_PATH_CHARACTERS); + strcpy(context->File_directory, Selector->Directory); + if (!load) + context->Format = Selector->Format_filter; + } } else { diff --git a/src/loadsave.c b/src/loadsave.c index 62aaa170..f2b7710a 100644 --- a/src/loadsave.c +++ b/src/loadsave.c @@ -85,6 +85,9 @@ void Load_SDL_Image(T_IO_Context *); void Load_Recoil_Image(T_IO_Context *); #endif +// clipboard +static void Load_ClipBoard_Image(T_IO_Context *); + // ENUM Name TestFunc LoadFunc SaveFunc PalOnly Comment Layers Ext Exts const T_Format File_formats[] = { {FORMAT_ALL_IMAGES, "(all)", NULL, NULL, NULL, 0, 0, 0, "", @@ -578,82 +581,101 @@ void Load_image(T_IO_Context *context) // charger le format du fichier: File_error=1; - f = Open_file_read(context); - if (f == NULL) + if (context->Format == FORMAT_CLIPBOARD) { - Warning("Cannot open file for reading"); - Error(0); - return; - } - - if (context->Format>FORMAT_ALL_FILES) - { - format = Get_fileformat(context->Format); - if (format->Test) - format->Test(context, f); - } - - if (File_error) - { - // Sinon, on va devoir scanner les différents formats qu'on connait pour - // savoir à quel format est le fichier: - for (index=0; index < Nb_known_formats(); index++) + Load_ClipBoard_Image(context); + if (File_error != 0) { - format = Get_fileformat(index); - // Loadable format - if (format->Test == NULL) - continue; - - fseek(f, 0, SEEK_SET); // rewind - // On appelle le testeur du format: - format->Test(context, f); - // On s'arrête si le fichier est au bon format: - if (File_error==0) - break; - } - } - fclose(f); - - if (File_error) - { - context->Format = DEFAULT_FILEFORMAT; - // try with recoil -#ifndef NORECOIL - Load_Recoil_Image(context); - if (File_error) -#endif -#if defined(USE_SDL) || defined(USE_SDL2) - { - // Last try: with SDL_image - Load_SDL_Image(context); - } - - if (File_error) -#endif - { - // Sinon, l'appelant sera au courant de l'échec grace à File_error; - // et si on s'apprêtait à faire un chargement définitif de l'image (pas - // une preview), alors on flash l'utilisateur. - //if (Pixel_load_function!=Pixel_load_in_preview) - // Error(0); + Error(0); return; } } else - // Si on a su déterminer avec succès le format du fichier: { - context->Format = format->Identifier; - // On peut charger le fichier: - // Dans certains cas il est possible que le chargement plante - // après avoir modifié la palette. TODO - format->Load(context); - } - - if (File_error>0) - { - GFX2_Log(GFX2_WARNING, "Unable to load file %s (error %d)! format:%s\n", context->File_name, File_error, format->Label); - if (context->Type!=CONTEXT_SURFACE) + if (context->File_name == NULL || context->File_directory == NULL) + { + GFX2_Log(GFX2_ERROR, "Load_Image() called with NULL file name or directory\n"); Error(0); + return; + } + + f = Open_file_read(context); + if (f == NULL) + { + Warning("Cannot open file for reading"); + Error(0); + return; + } + + if (context->Format > FORMAT_ALL_FILES) + { + format = Get_fileformat(context->Format); + if (format->Test) + format->Test(context, f); + } + + if (File_error) + { + // Sinon, on va devoir scanner les différents formats qu'on connait pour + // savoir à quel format est le fichier: + for (index=0; index < Nb_known_formats(); index++) + { + format = Get_fileformat(index); + // Loadable format + if (format->Test == NULL) + continue; + + fseek(f, 0, SEEK_SET); // rewind + // On appelle le testeur du format: + format->Test(context, f); + // On s'arrête si le fichier est au bon format: + if (File_error==0) + break; + } + } + fclose(f); + + if (File_error) + { + context->Format = DEFAULT_FILEFORMAT; + // try with recoil +#ifndef NORECOIL + Load_Recoil_Image(context); + if (File_error) +#endif +#if defined(USE_SDL) || defined(USE_SDL2) + { + // Last try: with SDL_image + Load_SDL_Image(context); + } + + if (File_error) +#endif + { + // Sinon, l'appelant sera au courant de l'échec grace à File_error; + // et si on s'apprêtait à faire un chargement définitif de l'image (pas + // une preview), alors on flash l'utilisateur. + //if (Pixel_load_function!=Pixel_load_in_preview) + // Error(0); + return; + } + } + else + // Si on a su déterminer avec succès le format du fichier: + { + context->Format = format->Identifier; + // On peut charger le fichier: + // Dans certains cas il est possible que le chargement plante + // après avoir modifié la palette. TODO + format->Load(context); + } + + if (File_error>0) + { + GFX2_Log(GFX2_WARNING, "Unable to load file %s (error %d)! format:%s\n", context->File_name, File_error, format->Label); + if (context->Type!=CONTEXT_SURFACE) + Error(0); + } } // Post-load @@ -1186,6 +1208,162 @@ T_GFX2_Surface * Load_surface(char *full_name, T_Gradient_array *gradients) } +static void Load_ClipBoard_Image(T_IO_Context * context) +{ +#ifdef WIN32 + UINT format; + HANDLE clipboard; + + if (!OpenClipboard(GFX2_Get_Window_Handle())) + { + GFX2_Log(GFX2_ERROR, "Failed to open Clipboard\n"); + return; + } + + format = EnumClipboardFormats(0); + while (format != 0) + { + const char * format_name = NULL; + char format_name_buffer[256]; + + switch (format) + { + case CF_TEXT: + format_name = "TEXT"; + break; + case CF_OEMTEXT: + format_name = "OEMTEXT"; + break; + case CF_UNICODETEXT: + format_name = "UNICODE TEXT"; + break; + case CF_DIB: + format_name = "DIB (BITMAPINFO)"; + break; + case CF_DIBV5: + format_name = "DIBV5 (BITMAPV5HEADER)"; + break; + case CF_BITMAP: + format_name = "HBITMAP"; + break; + case CF_METAFILEPICT: + format_name = "METAFILEPICT"; + break; + case CF_PALETTE: + format_name = "Palette"; + break; + case CF_TIFF: + format_name = "TIFF"; + break; + case CF_ENHMETAFILE: + format_name = "HENMETAFILE"; + break; + default: + if (GetClipboardFormatNameA(format, format_name_buffer, sizeof(format_name_buffer)) <= 0) + GFX2_Log(GFX2_WARNING, "Failed to get name for clipboard format %u\n", format); + else + format_name = format_name_buffer; + } + if (format_name != NULL) + GFX2_Log(GFX2_DEBUG, "Available format %5u \"%s\"\n", format, format_name); + + format = EnumClipboardFormats(format); // get next format + } + clipboard = GetClipboardData(CF_DIB); + if (clipboard != NULL) + { + const PBITMAPINFO bmi = (PBITMAPINFO)GlobalLock(clipboard); + if (bmi != NULL) + { + unsigned long width, height; + width = bmi->bmiHeader.biWidth; + height = (bmi->bmiHeader.biHeight > 0) ? bmi->bmiHeader.biHeight : -bmi->bmiHeader.biHeight; + + GFX2_Log(GFX2_DEBUG, "DIB %ldx%ld planes=%u bpp=%u compression=%u size=%u ClrUsed=%u\n", + bmi->bmiHeader.biWidth, bmi->bmiHeader.biHeight, + bmi->bmiHeader.biPlanes, bmi->bmiHeader.biBitCount, + bmi->bmiHeader.biCompression, bmi->bmiHeader.biSizeImage, + bmi->bmiHeader.biClrUsed); + if (bmi->bmiHeader.biCompression != BI_RGB) + GFX2_Log(GFX2_INFO, "Unsupported DIB compression %u\n", bmi->bmiHeader.biCompression); + else if (width > 9999 || height > 9999) + GFX2_Log(GFX2_INFO, "Image too big : %lux%lu\n", width, height); + else + { + unsigned i, color_count; + const byte * pixels; + unsigned int x, y; + + File_error = 0; // have to be set before calling Pre_load() + Pre_load(context, width, height, bmi->bmiHeader.biSizeImage, FORMAT_CLIPBOARD, PIXEL_SIMPLE, bmi->bmiHeader.biBitCount); + + color_count = bmi->bmiHeader.biClrUsed; + if (bmi->bmiHeader.biBitCount <= 8) + { // get palette + if (color_count == 0) + color_count = 1 << bmi->bmiHeader.biBitCount; + for (i = 0; i < color_count; i++) + { + context->Palette[i].R = bmi->bmiColors[i].rgbRed; + context->Palette[i].G = bmi->bmiColors[i].rgbGreen; + context->Palette[i].B = bmi->bmiColors[i].rgbBlue; + } + } + pixels = (const byte *)(&bmi->bmiColors[color_count]); + switch (bmi->bmiHeader.biBitCount) + { + case 8: + for (y = 0; y < height; y++) + { + const byte * line; + if (bmi->bmiHeader.biHeight > 0) + line = pixels + (height - y - 1) * bmi->bmiHeader.biWidth; + else + line = pixels + y * bmi->bmiHeader.biWidth; + for (x = 0; x < width; x++) + Set_pixel(context, x, y, line[x]); + } + break; + case 24: + for (y = 0; y < height; y++) + { + const byte * line; + if (bmi->bmiHeader.biHeight > 0) + line = pixels + (height - y - 1) * bmi->bmiHeader.biWidth * 3; + else + line = pixels + y * bmi->bmiHeader.biWidth * 3; + for (x = 0; x < width; x++) + Set_pixel_24b(context, x, y, line[x*3 + 2], line[x*3 + 1], line[x*3]); + } + break; + case 32: + for (y = 0; y < height; y++) + { + const byte * line; + if (bmi->bmiHeader.biHeight > 0) + line = pixels + (height - y - 1) * bmi->bmiHeader.biWidth * 4; + else + line = pixels + y * bmi->bmiHeader.biWidth * 4; + for (x = 0; x < width; x++) + Set_pixel_24b(context, x, y, line[x*4 + 2], line[x*4 + 1], line[x*4]); + } + break; + default: + GFX2_Log(GFX2_ERROR, "Loading %ubpp pictures from Clipboard is not implemented yet!\n", bmi->bmiHeader.biBitCount); + File_error = 1; + } + } + GlobalUnlock(clipboard); + } + } + CloseClipboard(); +#else + GFX2_Log(GFX2_ERROR, "Load_ClipBoard_Image() not implemented on this platform yet\n"); + File_error = 1; +#endif +} + + /// Saves an image. /// This routine will only be called when all hope is lost, memory thrashed, etc /// It's the last chance to save anything, but the code has to be extremely