/* * AbiPaint plugin * * AbiWord plugin to ease editing embedded images via an * External Image Editing program. The image editing program * used and optional image format conversion may be specified, * though sensible (platform specific) defaults are defined. * */ /* The default image editor and exported image type depends * on the platform. * * For Windows: * The PNG images by default are converted to BMP files * and the default image editor is Microsoft's (R) Paint (MSPAINT.EXE) * PNG to BMP conversion done using: * PNGDIB - a mini DIB-PNG conversion library for Win32 * By Jason Summers * * For Unix Systems (and similar) * The images are exported as PNG files * and the default image editor is the GIMP (gimp) * GNU Image Manipulation Program - see http://www.gimp.org/ */ /* * Based on AbiGimp copyright Martin Sevior which in turn * is based on AiksaurusABI - Abiword plugin for Aiksaurus * Copyright (C) 2001 by Jared Davis * Also tidbits taken from ImageMagick plugin, copyright 2002 * by Dom Lachowicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ // Define this to lock out Abi's gui while editing the image [A GOOD thing] // Without this there may possibly be concurrency issues & bad user actions (eg quitting or closing) // but it lets you open an image for editing and leave the image // editor open while you make other text edits. You may also be able to edit multiple images. // *** For public builds, please make sure this is defined, Thanks *** #define ABIPAINT_LOCKGUI #include "ut_assert.h" #include "ut_debugmsg.h" #include "ut_path.h" #include "ut_sleep.h" #include "xap_App.h" #include "xap_DialogFactory.h" #include "xap_Dlg_FileOpenSaveAs.h" #include "xap_Frame.h" #include "xap_Menu_Layouts.h" #include "xap_Module.h" #include "ap_Dialog_Id.h" #include "ap_Menu_Id.h" #include "ev_EditMethod.h" #include "ev_Menu.h" #include "ev_Menu_Actions.h" #include "ev_Menu_Labels.h" #include "ev_Menu_Layouts.h" #include "fg_Graphic.h" #include "fl_BlockLayout.h" #include "fp_Run.h" #include "fv_View.h" #include "ie_imp.h" #include "ie_impGraphic.h" #include "ie_exp.h" #include "ie_types.h" #include #include #ifdef WIN32 #include #define ENABLE_BMP static BOOL CreateChildProcess(char * appName, char *cmdline, PROCESS_INFORMATION *procInfo, STARTUPINFO *startInfo); #define PROCESS_STILL_ALIVE ((GetExitCodeProcess(procInfo.hProcess, &status))?(status == STILL_ACTIVE):0) #else /* UNIX */ #include #include #include #include #define PROCESS_STILL_ALIVE (pid != waitpid (pid, &status, WNOHANG)) #endif #ifdef ENABLE_BMP #include "ie_impGraphic_BMP.h" #include "pngdib.h" #endif #ifndef MAX_PATH #define MAX_PATH 2048 #endif ABI_PLUGIN_DECLARE(AbiPaint); // various helper functions static bool getFileName(UT_String &szFile, XAP_Frame * pFrame, XAP_Dialog_Id id, const char **szDescList, const char **szSuffixList, int *ft); static void getDefaultApp(UT_String &imageApp, bool &bLeaveImageAsPNG); #ifdef ENABLE_BMP static int convertPNG2BMP(const char *pngfn, const char *bmpfn); #endif // use preference file instead of registry XAP_PrefsScheme * prefsScheme = NULL; // our Scheme name in preference file const XML_Char * szAbiPaintSchemeName = "_plugin_AbiPaint"; // and settings within our scheme const XML_Char * ABIPAINT_PREF_KEY_bLeaveImageAsPNG = "bLeaveImageAsPNG"; const UT_String ABIPAINT_PREF_KEY_szProgramName = "szImageEditor"; /*! * Macro to declare an image editor methods */ #define DECLARE_ABIPAINT_EDITOR_METHOD(m) \ bool AbiPaint_Editor_##m (AV_View* v, EV_EditMethodCallData *d) #define ABIPAINT_METHOD(m) AbiPaint_Editor_##m #define ABIPAINT_mkstr(x) # x #define ABIPAINT_METHOD_STR(m) ABIPAINT_mkstr(ABIPAINT_METHOD(m)) DECLARE_ABIPAINT_EDITOR_METHOD(invoke); Defun_EV_GetMenuItemComputedLabel_Fn(getEditImageMenuName); Defun_EV_GetMenuItemState_Fn(imageSelMenuState); DECLARE_ABIPAINT_EDITOR_METHOD(saveAsBmp); DECLARE_ABIPAINT_EDITOR_METHOD(specify); DECLARE_ABIPAINT_EDITOR_METHOD(requiresBmp); Defun_EV_GetMenuItemState_Fn(requiresBMPState); typedef struct { const char * methodName; EV_EditMethod_pFn method; const char * label; // use & in label to set key binding, needs only be unique to this plugin const char * description; EV_Menu_LayoutFlags flags; // usually EV_MLF_Normal bool hasSubMenu; bool hasDialog; bool checkBox; EV_GetMenuItemState_pFn pfnGetState; EV_GetMenuItemComputedLabel_pFn pfnGetDynLabel; bool inContextMenu; // true if should show in context menu, otherwise will only appear in main menu } AbiMenuOptions ; /* Note: Make sure the methodName field is Not NULL, otherwise * AbiWord.exe will probably segfault -- actual results * depend on where other plugins add menu items. * Note2: Make sure the label is unique across ALL plugins (and * internal menu labels), any duplicates [including NULL] * may cause the menu to display incorrectly. This is * most notable on the end of a submenu; its probably ok * for separators to share a NULL label. */ const static AbiMenuOptions amo [] = { { ABIPAINT_METHOD_STR(submenu_start), NULL, "AbiPaint", "Allows in place editing of image via external program.", EV_MLF_BeginSubMenu, true, false, false, NULL, NULL, false }, { ABIPAINT_METHOD_STR(invoke), ABIPAINT_METHOD(invoke), "(AbiPaint) &Edit Image", "Opens the selected image for modification (in specified image editing program).", EV_MLF_Normal, false, true, false, imageSelMenuState, getEditImageMenuName, true }, #ifdef ENABLE_BMP { ABIPAINT_METHOD_STR(saveAsBmp), ABIPAINT_METHOD(saveAsBmp), "Save Image &As BMP", "Saves the selected image as a BMP file.", EV_MLF_Normal, false, true, false, imageSelMenuState, NULL, true }, #endif { ABIPAINT_METHOD_STR(separator1), NULL, NULL, NULL, EV_MLF_Separator, false, false, false, NULL, NULL, false }, { ABIPAINT_METHOD_STR(specify), ABIPAINT_METHOD(specify), "&Specify Image Editor", "Allows you to specify what image editing program to use, results stored in registry.", EV_MLF_Normal, false, true, false, NULL, NULL, false }, #ifdef ENABLE_BMP { ABIPAINT_METHOD_STR(requiresBmp), ABIPAINT_METHOD(requiresBmp), "Image Editor Requires &BMP", "Indicates the specified image editing program must use a BMP file instead of PNG (default is enabled).", EV_MLF_Normal, false, false, true, requiresBMPState, NULL, false }, #endif { ABIPAINT_METHOD_STR(submenu_end), NULL, "AbiPaint Submenu End", NULL, EV_MLF_EndSubMenu, true, false, false, NULL, NULL, false }, } ; #define NUM_MENUITEMS sizeof(amo)/sizeof(amo[0]) const char * prevMM = "&Word Count"; // Main menu item we place our 1st menu item after const char * prevCM = "Format &Image"; // Context menu item we place our 1st image context menu item after // // AbiPaint_addToMenus // ----------------------- // Adds all of this pluins menu items to AbiPaint submenu in Tools // static void AbiPaint_addToMenus() { UT_uint32 i; // First we need to get a pointer to the application itself. XAP_App *pApp = XAP_App::getApp(); // Now we need to get the EditMethod container for the application. // This holds a series of Edit Methods and links names to callbacks. EV_EditMethodContainer* pEMC = pApp->getEditMethodContainer(); // We need to go through and add the menu element to each "frame" // of the application. We can iterate through the frames by doing // XAP_App::getFrameCount() to tell us how many frames there are, // then calling XAP_App::getFrame(i) to get the i-th frame. UT_uint32 frameCount = pApp->getFrameCount(); XAP_Menu_Factory * pFact = pApp->getMenuFactory(); // Now we need to grab an ActionSet. This is going to be used later // on in our for loop. Take a look near the bottom. EV_Menu_ActionSet* pActionSet = pApp->getMenuActionSet(); for (i = 0; i < NUM_MENUITEMS; i++) { // Create an EditMethod that will link our method's name with // it's callback function. This is used to link the name to // the callback. EV_EditMethod *myEditMethod = new EV_EditMethod( amo[i].methodName, // name of callback function amo[i].method, // callback function itself. 0, // no additional data required. "" // description -- allegedly never used for anything ); // We have to add our EditMethod to the application's EditMethodList // so that the application will know what callback to call when a call // to amo[i].methodName is received. pEMC->addEditMethod(myEditMethod); // // Generate an unique id for this menu item // (We could also pass 0 or leave off newID and have addNewMenuAfter return one) // XAP_Menu_Id newID = pFact->getNewID(); // // Put it in the main menu (Tools, after Word Count). // pFact->addNewMenuAfter("Main",NULL,prevMM, amo[i].flags, newID); prevMM = amo[i].label; pFact->addNewLabel(NULL,newID,amo[i].label, amo[i].description); // // Put it in the context menu. // if (amo[i].inContextMenu) { pFact->addNewMenuAfter("ContextImageT",NULL,prevCM, amo[i].flags, newID); prevCM = amo[i].label; } // Create the Action that will be called. EV_Menu_Action* myAction = new EV_Menu_Action( newID, // id that the layout said we could use amo[i].hasSubMenu, // do we have a sub menu. amo[i].hasDialog, // do we raise a dialog (or in case a whole new program). amo[i].checkBox, // do we have a checkbox. amo[i].methodName, // name of callback function to call. amo[i].pfnGetState, // something about menu state, usually NULL amo[i].pfnGetDynLabel // dynamic menu label ); // Now what we need to do is add this particular action to the ActionSet // of the application. This forms the link between our new ID that we // got for this particular frame with the EditMethod that knows how to // call our callback function. pActionSet->addAction(myAction); } // rebuild the menus for(i = 0; i < frameCount; i++) { // Get the current frame that we're iterating through. XAP_Frame* pFrame = pApp->getFrame(i); pFrame->rebuildMenus(); } } static void AbiPaint_RemoveFromMenus () { UT_uint32 i; // MSVC is going to treat it this way regardless... // First we need to get a pointer to the application itself. XAP_App *pApp = XAP_App::getApp(); EV_EditMethodContainer* pEMC = pApp->getEditMethodContainer() ; // now remove crap from the menus UT_uint32 frameCount = pApp->getFrameCount(); XAP_Menu_Factory * pFact = pApp->getMenuFactory(); for (i = 0; i < NUM_MENUITEMS; i++) { // remove the edit method EV_EditMethod * pEM = ev_EditMethod_lookup ( amo[i].methodName ) ; pEMC->removeEditMethod ( pEM ) ; DELETEP( pEM ) ; // remove the contextual & tools menu items pFact->removeMenuItem("Main",NULL, amo[i].label); if (amo[i].inContextMenu) pFact->removeMenuItem("ContextImageT", NULL, amo[i].label); } // rebuild the menus for(i = 0; i < frameCount; i++) { // Get the current frame that we're iterating through. XAP_Frame* pFrame = pApp->getFrame(i); pFrame->rebuildMenus(); } } // ----------------------------------------------------------------------- // // Abiword Plugin Interface // // ----------------------------------------------------------------------- ABI_FAR_CALL int abi_plugin_register (XAP_ModuleInfo * mi) { mi->name = "AbiPaint"; mi->desc = "Allows editting an embedded image via external image editing program."; mi->version = ABI_VERSION_STRING; mi->author = "Abi the Ant"; mi->usage = "Select Image 1st, then select the action from AbiPaint menu. ;-)"; // Get XAP_Prefs object for retrieving/storing image editor and related preferences XAP_Prefs * prefs = XAP_App::getApp()->getPrefs(); if (prefs == NULL) return 0; // if we can't get preference file, then bail if ((prefsScheme = prefs->getScheme(szAbiPaintSchemeName)) == NULL) { // it may not exist so try creating & adding it. prefs->addScheme(new XAP_PrefsScheme(prefs, szAbiPaintSchemeName)); // if it still isn't there then fail if ((prefsScheme = prefs->getScheme(szAbiPaintSchemeName)) == NULL) return 0; // go ahead and set our default values UT_String szProgramName; bool bLeaveImageAsPNG; getDefaultApp(szProgramName, bLeaveImageAsPNG); prefsScheme->setValue(ABIPAINT_PREF_KEY_szProgramName.c_str(), szProgramName.c_str()); prefsScheme->setValueBool(ABIPAINT_PREF_KEY_bLeaveImageAsPNG, bLeaveImageAsPNG); } // Add the image editor to AbiWord's menus. AbiPaint_addToMenus(); return 1; } ABI_FAR_CALL int abi_plugin_unregister (XAP_ModuleInfo * mi) { mi->name = 0; mi->desc = 0; mi->version = 0; mi->author = 0; mi->usage = 0; AbiPaint_RemoveFromMenus (); return 1; } ABI_FAR_CALL int abi_plugin_supports_version (UT_uint32 major, UT_uint32 minor, UT_uint32 release) { // doesn't really matter as it will fail to load if the imports don't match. return 1; } // ----------------------------------------------------------------------- // // AbiPaint Invocation Code // // ----------------------------------------------------------------------- // returns true only if user requested to cancel save, pass suggested path in static bool getFileName(UT_String &szFile, XAP_Frame * pFrame, XAP_Dialog_Id id, const char **szDescList, const char **szSuffixList, int *ft) { UT_ASSERT(pFrame); XAP_DialogFactory * pDialogFactory = (XAP_DialogFactory *)(pFrame->getDialogFactory()); XAP_Dialog_FileOpenSaveAs * pDialog = (XAP_Dialog_FileOpenSaveAs *)(pDialogFactory->requestDialog(id)); UT_ASSERT(pDialog); pDialog->setCurrentPathname(szFile.c_str()); pDialog->setSuggestFilename(false); pDialog->setFileTypeList(szDescList, szSuffixList, ft); pDialog->runModal(pFrame); XAP_Dialog_FileOpenSaveAs::tAnswer ans = pDialog->getAnswer(); bool bOK = (ans == XAP_Dialog_FileOpenSaveAs::a_OK); if (bOK) szFile = pDialog->getPathname(); else szFile.clear(); pDialogFactory->releaseDialog(pDialog); return !bOK; } // // AbiPaint_Editor_specify // ------------------- // This is the function sets which image editor will (at least attempted) be invoked. // static bool AbiPaint_Editor_specify(AV_View* v, EV_EditMethodCallData *d) { // get current value UT_String szProgramName = ""; prefsScheme->getValue(ABIPAINT_PREF_KEY_szProgramName, szProgramName); // Get a frame in case we need to show an error message XAP_Frame *pFrame = XAP_App::getApp()->getLastFocussedFrame(); { const char * szDescList[3]; const char * szSuffixList[3]; int ft[3]; szDescList[0] = "Image Editing Programs (*.exe,*.com)"; szSuffixList[0] = "*.exe; *.com"; szDescList[1] = szSuffixList[1] = NULL; ft[0] = ft[1] = ft[2] = IEGFT_Unknown; if (getFileName(szProgramName, pFrame, XAP_DIALOG_ID_FILE_OPEN, szDescList, szSuffixList, ft)) return false; UT_DEBUGMSG(("ABIPAINT: szProgramName to use is %s\n", szProgramName.c_str())); } // now write it to the preference prefsScheme->setValue(ABIPAINT_PREF_KEY_szProgramName.c_str(), szProgramName.c_str()); return true; } #ifdef ENABLE_BMP // // requiresBMPState // ------------------- // This function returns current settings, ie whether image editor requires BMP or can work with PNG static EV_Menu_ItemState requiresBMPState(AV_View * pAV_View, XAP_Menu_Id id) { bool bLeaveImageAsPNG = false; // if value not found, then assume we must convert prefsScheme->getValueBool(ABIPAINT_PREF_KEY_bLeaveImageAsPNG, &bLeaveImageAsPNG); if (bLeaveImageAsPNG) return EV_MIS_ZERO; else return EV_MIS_Toggled; } // // AbiPaint_Editor_requiresBmp // ------------------- // This function sets whether image editor can work with PNG files // or requires conversion to platforms default image format (eg requires BMP on Windows) // Note: the conversion may advserly alter the image static bool ABIPAINT_METHOD(requiresBmp)(AV_View* v, EV_EditMethodCallData *d) { bool bLeaveImageAsPNG = false; // if value not found, then assume we must convert // see what current value is prefsScheme->getValueBool(ABIPAINT_PREF_KEY_bLeaveImageAsPNG, &bLeaveImageAsPNG); // toggle it bLeaveImageAsPNG = !bLeaveImageAsPNG; // now write it to the preference prefsScheme->setValueBool(ABIPAINT_PREF_KEY_bLeaveImageAsPNG, bLeaveImageAsPNG); return true; } #endif // // imageSelMenuState // ----------------- // When no image is selected, we gray out this menu item static bool isImageSelected (void) { // Get the current view that the user is in. XAP_Frame *pFrame = XAP_App::getApp()->getLastFocussedFrame(); FV_View* pView = static_cast(pFrame->getCurrentView()); if(!pView->isSelectionEmpty()) { PT_DocPosition pos = pView->getSelectionAnchor(); fp_Run* pRun = NULL; UT_Vector vBlock; pView->getBlocksInSelection( &vBlock); UT_uint32 i=0; UT_uint32 count = vBlock.getItemCount(); fl_BlockLayout * pBlock = NULL; for(i=0; (i< count); i++) { if(i==0) { if(pView->getPoint() < pos) { pos = pView->getPoint(); } pBlock = pView->getBlockAtPosition(pos); UT_sint32 x,y,x2,y2,height; bool bEOL = false; bool bDirection; pRun = pBlock->findPointCoords(pos,bEOL,x,y,x2,y2,height,bDirection); } else { pBlock = (fl_BlockLayout *) vBlock.getNthItem(i); pRun = pBlock->getFirstRun(); } while(pRun && pRun->getType() != FPRUN_IMAGE) { pRun = pRun->getNext(); } if(pRun && pRun->getType() == FPRUN_IMAGE) return true; } return false; } else { return false; } } static EV_Menu_ItemState imageSelMenuState(AV_View * pAV_View, XAP_Menu_Id id) { if (isImageSelected()) return EV_MIS_ZERO; else return EV_MIS_Gray; } #ifdef ENABLE_BMP // // AbiPaint_Editor_saveAsBmp // ------------------------- // This is the function exports selected image as a Windows BMP file. // static bool ABIPAINT_METHOD(saveAsBmp)(AV_View* v, EV_EditMethodCallData *d) { // Get a frame (for error messages) and (to) get the current view that the user is in. XAP_Frame *pFrame = XAP_App::getApp()->getLastFocussedFrame(); FV_View* pView = static_cast(pFrame->getCurrentView()); char szTempFileName[ MAX_PATH ]; UT_tmpnam(szTempFileName); UT_String szTmpPng = szTempFileName; szTmpPng += ".png"; remove(szTempFileName); PT_DocPosition pos = pView->saveSelectedImage((const char *)szTmpPng.c_str()); if(pos == 0) { pFrame->showMessageBox("You must select an Image before trying to save it as a BMP file!", XAP_Dialog_MessageBox::b_O,XAP_Dialog_MessageBox::a_OK); return false; } // // Convert png into bmp // NOTE: probably looses detail/information though!!! // UT_String szBMPFile = pFrame->getFilename(); // perhaps a different default directory should be used??? { const char * szDescList[2]; const char * szSuffixList[2]; IEGraphicFileType ft[2]; IE_ImpGraphicBMP_Sniffer tmp; tmp.getDlgLabels(szDescList, szSuffixList, ft); szDescList[1] = szSuffixList[1] = NULL; ft[1] = IEGFT_Unknown; if (getFileName(szBMPFile, pFrame, XAP_DIALOG_ID_FILE_SAVEAS, szDescList, szSuffixList, ft)) { // user canceled remove(szTmpPng.c_str()); return true; } } if (convertPNG2BMP(szTmpPng.c_str(), szBMPFile.c_str())) { pFrame->showMessageBox("Unable to convert PNG image data to BMP.", XAP_Dialog_MessageBox::b_O,XAP_Dialog_MessageBox::a_OK); UT_ASSERT(UT_SHOULD_NOT_HAPPEN); remove(szTmpPng.c_str()); return false; } remove(szTmpPng.c_str()); return true; } #endif // // getEditImageMenuName // ---------------------- // returns menu name to edit image with (eg Edit Image via ) // Note: While this allows us to optionally include the program or whatever, // the primary purposes is to allow us to use the same name as // any other plugin (specifically AbiGimp) without them colliding. // [we use a different static name and possibly identical dynamic name] const char * getEditImageMenuName(XAP_Frame * pFrame, const EV_Menu_Label * pLabel, XAP_Menu_Id id) { UT_String szProgramName; static UT_String MenuName; MenuName = "&Edit Image"; // give user some indication of program that will be executed if (prefsScheme->getValue(ABIPAINT_PREF_KEY_szProgramName, szProgramName)) { // we now have the full program name (with path & extension), so prune MenuName += " via "; MenuName += UT_basename(szProgramName.c_str()); // limit menu length to max of 33 (31 characters + two dots ..) if (MenuName.size() > 33) { MenuName = MenuName.substr(0, 31); MenuName += ".. "; // note the space is to separate these dots from the menu dots } } return MenuName.c_str(); } // // AbiPaint_Editor_invoke // ------------------- // This is the function that we actually call to invoke the image editor. // static bool ABIPAINT_METHOD(invoke)(AV_View* v, EV_EditMethodCallData *d) { // Get the current view that the user is in. XAP_Frame *pFrame = XAP_App::getApp()->getLastFocussedFrame(); FV_View* pView = static_cast(pFrame->getCurrentView()); // // get values from registry, if not found use sensible defaults // UT_String imageApp; // holds MAXPATH\appName MAXPATH\imagefilename bool bLeaveImageAsPNG; // read stuff from the preference value if (!prefsScheme->getValue(ABIPAINT_PREF_KEY_szProgramName, imageApp)) { getDefaultApp(imageApp, bLeaveImageAsPNG); } // now that we have program name, try to get other flag (allows overriding default value) // Note: we allow overriding, otherwise if we don't adhere to user's setting // then the use BMP or not menu should be greyed to note it has no effect prefsScheme->getValueBool(ABIPAINT_PREF_KEY_bLeaveImageAsPNG, &bLeaveImageAsPNG); // // generate a temp file name... // char szTempFileName[ MAX_PATH ]; UT_tmpnam(szTempFileName); UT_String szTmpPng = szTempFileName; szTmpPng += ".png"; UT_String szTmp = szTmpPng; // default: our temp file is the created png file PT_DocPosition pos = pView->saveSelectedImage((const char *)szTmpPng.c_str()); if(pos == 0) { remove(szTempFileName); pFrame->showMessageBox("You must select an Image before editing it", XAP_Dialog_MessageBox::b_O,XAP_Dialog_MessageBox::a_OK); return false; } #ifdef ENABLE_BMP // // Convert png into bmp for best compatibility with Windows programs // NOTE: probably looses detail/information though!!! so if possible use PNG // if (!bLeaveImageAsPNG) { szTmp = szTempFileName; szTmp += ".bmp"; // our temp file is a bmp file if (convertPNG2BMP(szTmpPng.c_str(), szTmp.c_str())) { pFrame->showMessageBox("Unable to convert PNG image data to BMP for external program use!", XAP_Dialog_MessageBox::b_O,XAP_Dialog_MessageBox::a_OK); UT_ASSERT(UT_SHOULD_NOT_HAPPEN); remove(szTempFileName); remove(szTmpPng.c_str()); return false; } remove(szTmpPng.c_str()); } #endif // remove the temp file (that lacks proper extension) remove(szTempFileName); // // Get the initial file status. // struct stat myFileStat; int ok = stat(szTmp.c_str(),&myFileStat); if(ok < 0) { UT_ASSERT(UT_SHOULD_NOT_HAPPEN); remove(szTempFileName); return false; } time_t mod_time = myFileStat.st_mtime; #ifdef ABIPAINT_LOCKGUI // // Get some pointers so we can call the editMethod to lock out GUI // operations on AbiWord while editting the image. // XAP_App *pApp = XAP_App::getApp(); // Now we need to get the EditMethod container for the application. // This holds a series of Edit Methods and links names to callbacks. EV_EditMethodContainer* pEMC = pApp->getEditMethodContainer(); // // OK now get the methods to lock and unlock GUI operations // const EV_EditMethod * lockGUI = pEMC->findEditMethodByName("lockGUI"); const EV_EditMethod * unlockGUI = pEMC->findEditMethodByName("unlockGUI"); #endif // // Fire up the image editor... // #ifdef WIN32 UT_String cmdline = imageApp + " \"" + szTmp + "\""; PROCESS_INFORMATION procInfo; STARTUPINFO startInfo; if (!CreateChildProcess(NULL, const_cast(cmdline.c_str()), &procInfo, &startInfo)) { UT_String msg = "Unable to run program: "; msg += cmdline; pFrame->showMessageBox(msg.c_str(), XAP_Dialog_MessageBox::b_O,XAP_Dialog_MessageBox::a_OK); procInfo.hProcess = 0; goto Cleanup; } DWORD status; #else char * gimpArgs[3]; gimpArgs[0] = imageApp.c_str(); gimpArgs[1] = const_cast(szTmp.c_str()); gimpArgs[2] = NULL; UT_sint32 pid; if((pid = fork())== 0) { execvp(imageApp.c_str(),gimpArgs); } int status; #endif #ifdef ABIPAINT_LOCKGUI // // Lock out the GUI in AbiWord // ev_EditMethod_invoke(lockGUI,d); #endif while (PROCESS_STILL_ALIVE) { UT_usleep(10000); // wait 10 milliseconds pFrame->nullUpdate(); ok = stat(szTmp.c_str(),&myFileStat); if(ok == 0) { if(myFileStat.st_mtime != mod_time) { // wait for changes to settle (program done writing changes) // we use both modified time & file size, but really we // could just use file size as mod time doesn't appear to change for small images mod_time = myFileStat.st_mtime; off_t size = myFileStat.st_size; UT_usleep(100000); // wait 100 milliseconds (so program may have time to write something) ok = stat(szTmp.c_str(),&myFileStat); while((mod_time != myFileStat.st_mtime) || !size || (size > 0 && size != myFileStat.st_size)) { mod_time = myFileStat.st_mtime; size = myFileStat.st_size; ok = stat(szTmp.c_str(),&myFileStat); UT_usleep(500000); // wait a while, let program write its data // just make sure the program is still running, otherwise we could get stuck in a loop if (!PROCESS_STILL_ALIVE) { pFrame->showMessageBox("External image editor appears to have been terminated unexpectedly.", XAP_Dialog_MessageBox::b_O,XAP_Dialog_MessageBox::a_OK); //procInfo.hProcess = 0; goto Cleanup; } } mod_time = myFileStat.st_mtime; UT_usleep(100000); // wait a while just to make sure program is done with file // // OK replace the current image with this. // IEGraphicFileType iegft = IEGFT_Unknown; IE_ImpGraphic *pIEG; FG_Graphic* pFG; UT_Error errorCode; errorCode = IE_ImpGraphic::constructImporter(szTmp.c_str(),iegft, &pIEG); if(errorCode) { pFrame->showMessageBox("Error constructing importer. Could not put image back into Abiword", XAP_Dialog_MessageBox::b_O,XAP_Dialog_MessageBox::a_OK); UT_ASSERT(UT_SHOULD_NOT_HAPPEN); goto Cleanup; } errorCode = pIEG->importGraphic(szTmp.c_str(), &pFG); if(errorCode) { UT_ASSERT(UT_SHOULD_NOT_HAPPEN); pFrame->showMessageBox("Error making pFG. Could not put image back into Abiword", XAP_Dialog_MessageBox::b_O,XAP_Dialog_MessageBox::a_OK); DELETEP(pIEG); goto Cleanup; } DELETEP(pIEG); #ifdef ABIPAINT_LOCKGUI // // UnLock the GUI in AbiWord so we can do our thing. // ev_EditMethod_invoke(unlockGUI,d); #endif pView->cmdUnselectSelection(); pView->setPoint(pos); pView->extSelHorizontal(true, 1); // move point forward one errorCode = pView->cmdInsertGraphic(pFG,szTmp.c_str()); if (errorCode) { pFrame->showMessageBox("Could not put image back into Abiword", XAP_Dialog_MessageBox::b_O,XAP_Dialog_MessageBox::a_OK); UT_ASSERT(UT_SHOULD_NOT_HAPPEN); DELETEP(pFG); goto Cleanup; } DELETEP(pFG); // // Reselect the image // pView->setPoint(pos); pView->extSelHorizontal(true, 1); // move point forward one #ifdef ABIPAINT_LOCKGUI // // Lock out the GUI in AbiWord again // ev_EditMethod_invoke(lockGUI,d); #endif } } } // // Now delete the tempfile // ok = remove(szTmp.c_str()); #ifdef ABIPAINT_LOCKGUI // // UnLock the GUI in AbiWord // ev_EditMethod_invoke(unlockGUI,d); #endif // // Done! // return true; // // Something went wrong. // Cleanup: ok = remove(szTmp.c_str()); #ifdef ABIPAINT_LOCKGUI // // UnLock the GUI in AbiWord // ev_EditMethod_invoke(unlockGUI,d); #endif // // Kill the image editor. // #ifdef WIN32 if (procInfo.hProcess) TerminateProcess(procInfo.hProcess, -1); #else kill(pid,9); #endif return false; } static void getDefaultApp(UT_String &imageApp, bool &bLeaveImageAsPNG) { #ifdef WIN32 bLeaveImageAsPNG = false; imageApp.clear(); char buffer[MAX_PATH]; // for WinNT mspaint is most likely in the system directory (eg C:\WINNT\SYSTEM32\MSPAINT.EXE) if (GetSystemDirectory(buffer, MAX_PATH)) { imageApp = buffer; imageApp += "\\MSPAINT.EXE"; UT_DEBUGMSG(("ABIPAINT: Checking if %s exists\n", imageApp.c_str())); if (!UT_isRegularFile(imageApp.c_str())) imageApp.clear(); } // if not there, try in Win95b directory (eg %PROGRAMFILES%\ACCESSORIES\MSPAINT.EXE) if (imageApp.empty()) { HKEY hKey; unsigned long lType; DWORD dwSize; unsigned char* szValue = NULL; if( ::RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", 0, KEY_READ, &hKey) == ERROR_SUCCESS ) { if( ::RegQueryValueEx( hKey, "ProgramFilesDir", NULL, &lType, NULL, &dwSize) == ERROR_SUCCESS ) { szValue = new unsigned char[dwSize + 1]; ::RegQueryValueEx( hKey, "ProgramFilesDir", NULL, &lType, szValue, &dwSize); imageApp = (char*) szValue; delete[] szValue; imageApp += "\\ACCESSORIES\\MSPAINT.EXE"; UT_DEBUGMSG(("ABIPAINT: Checking if %s exists\n", imageApp.c_str())); if (!UT_isRegularFile(imageApp.c_str())) imageApp.clear(); } ::RegCloseKey(hKey); } } // if we still haven't found the file, then simply try mspaint.exe if (imageApp.empty()) { imageApp = "mspaint.exe"; UT_DEBUGMSG(("ABIPAINT: Falling back to %s (will probably fail)\n", imageApp.c_str())); } #else // for other platforms default to the GIMP, assume in path bLeaveImageAsPNG = true; imageApp = "gimp"; #endif } #ifdef WIN32 // our equivalent of fork() & exec BOOL CreateChildProcess(char * appName, char *cmdline, PROCESS_INFORMATION *procInfo, STARTUPINFO *startInfo) { //initialize structures used to return info ZeroMemory( procInfo, sizeof(PROCESS_INFORMATION) ); ZeroMemory( startInfo, sizeof(STARTUPINFO) ); startInfo->cb = sizeof(STARTUPINFO); // Create the child process. return CreateProcess( appName, // application module to execute cmdline, // command line NULL, // process security attributes NULL, // primary thread security attributes FALSE, // handles not are inherited 0, // creation flags NULL, // use parent's environment NULL, // use parent's current directory startInfo, // STARTUPINFO pointer procInfo // receives PROCESS_INFORMATION ); } #endif #ifdef ENABLE_BMP //for now PNG->BMP conversion is Windows specific // ----------------------------------------------------------------------- // // The next functions are based on png2bmp.c from PNGDIB-2.1.0 library // // ----------------------------------------------------------------------- /** PNGDIB - a mini DIB-PNG conversion library for Win32 By Jason Summers Version 2.1.0, Feb. 2002 Web site: http://pobox.com/~jason1/imaging/pngdib/ This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Permission is hereby granted to use, copy, modify, and distribute this source code for any purpose, without fee. **/ // #include #include "png.h" #include "pngdib.h" int write_dib_to_bmp(const char *bmpfn, LPBITMAPINFOHEADER lpdib, int dibsize, int bitsoffset) { HANDLE hfile; BITMAPFILEHEADER h; DWORD written, err; ZeroMemory((void*)&h,sizeof(h)); h.bfType= MAKEWORD('B','M'); h.bfSize= sizeof(BITMAPFILEHEADER)+dibsize; h.bfOffBits= sizeof(BITMAPFILEHEADER)+bitsoffset; hfile=CreateFile(bmpfn,GENERIC_WRITE,FILE_SHARE_READ,NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if(hfile==INVALID_HANDLE_VALUE) return 1; if(!WriteFile(hfile,(void*)&h,sizeof(BITMAPFILEHEADER),&written,NULL)) { err=GetLastError(); CloseHandle(hfile); return 1; } if(!WriteFile(hfile,(void*)lpdib,dibsize,&written,NULL)) { err=GetLastError(); CloseHandle(hfile); return 1; } CloseHandle(hfile); return 0; } // returns 0 on success, nonzero of failure int convertPNG2BMP(const char *pngfn, const char *bmpfn) { PNGD_P2DINFO p2d; int ret; char errmsg[100]; ZeroMemory((void*)&p2d,sizeof(PNGD_P2DINFO)); p2d.structsize=sizeof(PNGD_P2DINFO); p2d.flags=0; p2d.pngfn=pngfn; // name of the file to read p2d.errmsg=errmsg; strcpy(errmsg,""); if ((ret=read_png_to_dib(&p2d))) return ret; // returns 0 on success // see pngdib.h for a list of error codes // printf("Error: %s (%d)\n",errmsg,ret); if(write_dib_to_bmp(bmpfn, p2d.lpdib, p2d.dibsize, p2d.bits_offs)) { // printf("Can't write BMP file\n"); ret = PNGD_E_WRITE; // yeah I know, BMP != PNG, but write error is better than generic error } // The DIB will have been allocated with GlobalAlloc(GMEM_FIXED...). // You should free it with GlobalFree when you're done with it. GlobalFree((void*)p2d.lpdib); return ret; } #endif