/* * * Copyright (C) 2004 Luca Padovani * Copyright (C) 2005 Martin Sevior * * 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. */ #ifdef ABI_PLUGIN_BUILTIN #define abi_plugin_register abipgn_abimathview_register #define abi_plugin_unregister abipgn_abimathview_unregister #define abi_plugin_supports_version abipgn_abimathview_supports_version #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "gr_Abi_MathGraphicDevice.h" #include "gr_Abi_RenderingContext.h" #include "AbiMathView.h" #include "gr_Abi_DefaultShaper.h" #include "gr_Abi_StandardSymbolsShaper.h" #include "gr_Abi_RenderingContext.h" #include "pd_Document.h" #include "pp_Property.h" #include "pp_AttrProp.h" #include "ut_mbtowc.h" #include "gr_Painter.h" #include "xad_Document.h" #include "xap_Module.h" #include "ap_App.h" #include "ie_imp_MathML.h" #include "ie_imp.h" #include "ie_impGraphic.h" #include "ut_assert.h" #include "ut_debugmsg.h" #include "xap_Module.h" #include "xap_App.h" #include "xap_Frame.h" #include "fv_View.h" #include "ap_Menu_Id.h" #include "ev_Menu_Actions.h" #include "ev_Menu.h" #include "ev_Menu_Layouts.h" #include "ev_Menu_Labels.h" #include "ev_EditMethod.h" #include "xap_Menu_Layouts.h" #include "ie_exp.h" #include "ie_types.h" #include "xap_Dialog_Id.h" #include "ap_Dialog_Id.h" #include "xap_Dlg_FileOpenSaveAs.h" #include "xap_DialogFactory.h" #include "xap_Dlg_MessageBox.h" #include "ap_Strings.h" #include "ut_sleep.h" #include #include #ifdef WIN32 #include #else #include #include #include #include "ut_files.h" #endif // // AbiMathView_addToMenus // ----------------------- // Adds "Insert Equation" option to AbiWord's Main Menu. // static bool AbiMathView_insert(AV_View* v, EV_EditMethodCallData *d); // FIXME make these translatable strings static const char* AbiMathView_MenuLabel = "Equation"; static const char* AbiMathView_MenuTooltip = "Insert MathML from a file"; static void AbiMathView_addToMenus() { // First we need to get a pointer to the application itself. XAP_App *pApp = XAP_App::getApp(); // 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( "AbiMathView_insert", // name of callback function AbiMathView_insert, // callback function itself. 0, // no additional data required. "" // description -- allegedly never used for anything ); // 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 have to add our EditMethod to the application's EditMethodList // so that the application will know what callback to call when a call // to "AbiMathView_insert" is received. pEMC->addEditMethod(myEditMethod); // 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(); // 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. int frameCount = pApp->getFrameCount(); XAP_Menu_Factory * pFact = pApp->getMenuFactory(); // Put it after Insert Picture in the Main menu XAP_Menu_Id newID= pFact->addNewMenuAfter("Main",NULL,"&Endnote",EV_MLF_Normal); pFact->addNewLabel(NULL,newID,AbiMathView_MenuLabel, AbiMathView_MenuTooltip); // Create the Action that will be called. EV_Menu_Action* myAction = new EV_Menu_Action( newID, // id that the layout said we could use 0, // no, we don't have a sub menu. 1, // yes, we raise a dialog. 0, // no, we don't have a checkbox. 0, // no radio buttons for me, thank you "AbiMathView_insert", // name of callback function to call. NULL, // don't know/care what this is for NULL // don't know/care what this is for ); // 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); for(int i = 0;i < frameCount;++i) { // Get the current frame that we're iterating through. XAP_Frame* pFrame = pApp->getFrame(i); pFrame->rebuildMenus(); } } static void AbiMathView_removeFromMenus () { // First we need to get a pointer to the application itself. XAP_App *pApp = XAP_App::getApp(); // remove the edit method EV_EditMethodContainer* pEMC = pApp->getEditMethodContainer() ; EV_EditMethod * pEM = ev_EditMethod_lookup ( "AbiMathView_insert" ) ; pEMC->removeEditMethod ( pEM ) ; DELETEP( pEM ) ; // now remove crap from the menus int frameCount = pApp->getFrameCount(); XAP_Menu_Factory * pFact = pApp->getMenuFactory(); pFact->removeMenuItem("Main",NULL,AbiMathView_MenuLabel); for(int i = 0;i < frameCount;++i) { // Get the current frame that we're iterating through. XAP_Frame* pFrame = pApp->getFrame(i); pFrame->rebuildMenus(); } } XAP_Dialog_MessageBox::tAnswer s_CouldNotLoadFileMessage(XAP_Frame * pFrame, const char * pNewFile, UT_Error errorCode) { XAP_String_Id String_id; switch (errorCode) { case -301: String_id = AP_STRING_ID_MSG_IE_FileNotFound; break; case -302: String_id = AP_STRING_ID_MSG_IE_NoMemory; break; case -303: String_id = AP_STRING_ID_MSG_IE_UnsupportedType; //AP_STRING_ID_MSG_IE_UnknownType; break; case -304: String_id = AP_STRING_ID_MSG_IE_BogusDocument; break; case -305: String_id = AP_STRING_ID_MSG_IE_CouldNotOpen; break; case -306: String_id = AP_STRING_ID_MSG_IE_CouldNotWrite; break; case -307: String_id = AP_STRING_ID_MSG_IE_FakeType; break; case -311: String_id = AP_STRING_ID_MSG_IE_UnsupportedType; break; default: String_id = AP_STRING_ID_MSG_ImportError; } return pFrame->showMessageBox(String_id, XAP_Dialog_MessageBox::b_O, XAP_Dialog_MessageBox::a_OK, pNewFile); } static bool s_AskForMathMLPathname(XAP_Frame * pFrame, char ** ppPathname, IEGraphicFileType * iegft) { // raise the file-open dialog for inserting a MathML equation. // return a_OK or a_CANCEL depending on which button // the user hits. // return a pointer a UT_strdup()'d string containing the // pathname the user entered -- ownership of this goes // to the caller (so free it when you're done with it). UT_DEBUGMSG(("s_AskForMathMLPathname: frame %p\n", pFrame)); UT_return_val_if_fail (ppPathname, false); *ppPathname = NULL; pFrame->raise(); XAP_DialogFactory * pDialogFactory = static_cast(pFrame->getDialogFactory()); XAP_Dialog_FileOpenSaveAs * pDialog = static_cast(pDialogFactory->requestDialog(XAP_DIALOG_ID_INSERTMATHML)); UT_return_val_if_fail (pDialog, false); pDialog->setCurrentPathname(NULL); pDialog->setSuggestFilename(false); // to fill the file types popup list, we need to convert AP-level // ImpGraphic descriptions, suffixes, and types into strings. UT_uint32 filterCount = IE_ImpGraphic::getImporterCount(); const char ** szDescList = static_cast(UT_calloc(filterCount + 1, sizeof(char *))); const char ** szSuffixList = static_cast(UT_calloc(filterCount + 1, sizeof(char *))); IEGraphicFileType * nTypeList = (IEGraphicFileType *) UT_calloc(filterCount + 1, sizeof(IEGraphicFileType)); UT_uint32 k = 0; while (IE_ImpGraphic::enumerateDlgLabels(k, &szDescList[k], &szSuffixList[k], &nTypeList[k])) k++; pDialog->setFileTypeList(szDescList, szSuffixList, static_cast(nTypeList)); if (iegft != NULL) pDialog->setDefaultFileType(*iegft); pDialog->runModal(pFrame); XAP_Dialog_FileOpenSaveAs::tAnswer ans = pDialog->getAnswer(); bool bOK = (ans == XAP_Dialog_FileOpenSaveAs::a_OK); if (bOK) { const char * szResultPathname = pDialog->getPathname(); UT_DEBUGMSG(("MATHML Path Name selected = %s \n",szResultPathname)); if (szResultPathname && *szResultPathname) UT_cloneString(*ppPathname,szResultPathname); UT_sint32 type = pDialog->getFileType(); // If the number is negative, it's a special type. // Some operating systems which depend solely on filename // suffixes to identify type (like Windows) will always // want auto-detection. if (type < 0) switch (type) { case XAP_DIALOG_FILEOPENSAVEAS_FILE_TYPE_AUTO: // do some automagical detecting *iegft = IEGFT_Unknown; break; default: // it returned a type we don't know how to handle UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } else *iegft = static_cast(pDialog->getFileType()); } FREEP(szDescList); FREEP(szSuffixList); FREEP(nTypeList); pDialogFactory->releaseDialog(pDialog); return bOK; } // // AbiMathView_insert // ------------------- // This is the function that we actually call to insert the MathML. // bool AbiMathView_insert(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()); PD_Document * pDoc = static_cast(pFrame->getCurrentDoc()); char* pNewFile = NULL; IEGraphicFileType iegft = IEGFT_Unknown; bool bOK = s_AskForMathMLPathname(pFrame,&pNewFile,&iegft); if (!bOK || !pNewFile) { UT_DEBUGMSG(("ARRG! bOK = %d pNewFile = %x \n",bOK,pNewFile)); return false; } UT_UTF8String sNewFile = pNewFile; // we own storage for pNewFile and must free it. FREEP(pNewFile); UT_DEBUGMSG(("fileInsertMathML: loading [%s]\n",sNewFile.utf8_str())); IE_Imp_MathML * pImpMathML = new IE_Imp_MathML(pDoc); UT_Error errorCode = pImpMathML->importFile(sNewFile.utf8_str()); if(errorCode != UT_OK) { s_CouldNotLoadFileMessage(pFrame, sNewFile.utf8_str(), errorCode); DELETEP(pImpMathML); return false; } /* Create the data item */ const char* mimetypeMATHML = NULL; mimetypeMATHML = UT_strdup("application/mathml+xml"); pDoc->createDataItem(sNewFile.utf8_str(), false, pImpMathML->getByteBuf(), static_cast(const_cast(mimetypeMATHML)), NULL); // Insert the MathML Object pView->cmdInsertMathML(sNewFile.utf8_str(),pView->getPoint()); DELETEP(pImpMathML); return true; } GR_AbiMathItems::GR_AbiMathItems(void): m_iAPI(0), m_bHasSnapshot(false) { } GR_AbiMathItems::~GR_AbiMathItems(void) { } GR_MathManager::GR_MathManager(GR_Graphics* pG) : GR_EmbedManager(pG), m_CurrentUID(-1), m_pLogger(NULL), m_pMathGraphicDevice(NULL), m_pAbiContext(NULL), m_pOperatorDictionary(NULL), m_pDoc(NULL) { m_vecMathView.clear(); m_vecItems.clear(); } GR_MathManager::~GR_MathManager() { DELETEP(m_pAbiContext); m_pAbiContext = 0; UT_VECTOR_PURGEALL(GR_AbiMathItems *,m_vecItems); } GR_EmbedManager * GR_MathManager::create(GR_Graphics * pG) { return static_cast(new GR_MathManager(pG)); } const char * GR_MathManager::getObjectType(void) const { return "mathml"; } void GR_MathManager::initialize(void) { SmartPtr logger = Logger::create(); m_pLogger = logger; logger->setLogLevel(LOG_INFO); SmartPtr mathGraphicDevice = GR_Abi_MathGraphicDevice::create(getGraphics()); m_pMathGraphicDevice = mathGraphicDevice; m_pAbiContext = new GR_Abi_RenderingContext(getGraphics()); SmartPtr dictionary = MathMLOperatorDictionary::create(); m_pOperatorDictionary = dictionary; libxml2_MathView::loadOperatorDictionary(logger, dictionary, libxml2_MathView::getDefaultOperatorDictionaryPath()); } UT_sint32 GR_MathManager::_makeMathView(void) { SmartPtr pMathView = libxml2_MathView::create(); m_vecMathView.addItem(pMathView); pMathView->setLogger(m_pLogger); pMathView->setOperatorDictionary(m_pOperatorDictionary); pMathView->setMathMLNamespaceContext( MathMLNamespaceContext::create(pMathView, m_pMathGraphicDevice)); return static_cast(m_vecMathView.getItemCount()-1); } void GR_MathManager::_loadMathML(UT_sint32 uid, UT_UTF8String & sMathBuf) { SmartPtr pMathView = m_vecMathView.getNthItem(uid); UT_return_if_fail(pMathView); pMathView->loadBuffer(sMathBuf.utf8_str()); } void GR_MathManager::setDefaultFontSize(UT_sint32 uid, UT_sint32 iSize) { SmartPtr pMathView = m_vecMathView.getNthItem(uid); UT_return_if_fail(pMathView); pMathView->setDefaultFontSize(iSize); } UT_sint32 GR_MathManager::makeEmbedView(AD_Document * pDoc, UT_uint32 api, const char * szDataID) { if(m_pDoc == NULL) { m_pDoc = static_cast(pDoc); } else { UT_ASSERT(m_pDoc == static_cast(pDoc)); } UT_sint32 iNew = _makeMathView(); GR_AbiMathItems * pItem = new GR_AbiMathItems(); pItem->m_iAPI = api; pItem->m_bHasSnapshot = false; m_vecItems.addItem(pItem); UT_ASSERT(static_cast(m_vecItems.getItemCount()) == (iNew+1)); return iNew; } void GR_MathManager::makeSnapShot(UT_sint32 uid, UT_Rect & rec) { if(!getGraphics()->queryProperties(GR_Graphics::DGP_SCREEN)) { return; } GR_AbiMathItems * pItem = m_vecItems.getNthItem(uid); UT_return_if_fail(pItem); const PP_AttrProp * pSpanAP = NULL; PT_AttrPropIndex api = pItem->m_iAPI; /* bool b = */ m_pDoc->getAttrProp(api, &pSpanAP); const char * pszDataID = NULL; bool bFoundDataID = pSpanAP->getAttribute("dataid", pszDataID); if(pItem->m_bHasSnapshot) { updatePNGSnapshot(static_cast(m_pDoc),rec,pszDataID); } else { createPNGSnapshot(static_cast(m_pDoc),rec,pszDataID); pItem->m_bHasSnapshot = true; } } bool GR_MathManager::isDefault(void) { return false; } bool GR_MathManager::createPNGSnapshot(AD_Document * pDoc, UT_Rect & rec, const char * szDataID) { if(isDefault()) { return false; } GR_Painter painter(getGraphics()); GR_Image * pImage = painter.genImageFromRectangle(rec); UT_ByteBuf * pBuf = NULL; pImage->convertToBuffer(&pBuf); UT_UTF8String sID = "snapshot-png-"; sID += szDataID; const char* mimetypePNG = NULL; mimetypePNG = UT_strdup("image/png"); printf("Making data-item of name %s \n",sID.utf8_str()); pDoc->createDataItem(sID.utf8_str(),false,reinterpret_cast< const UT_ByteBuf *>(pBuf),mimetypePNG,NULL); delete pBuf; delete pImage; return true; } bool GR_MathManager::updatePNGSnapshot(AD_Document * pDoc, UT_Rect & rec, const char * szDataID) { if(isDefault()) { return false; } GR_Painter painter(getGraphics()); GR_Image * pImage = painter.genImageFromRectangle(rec); UT_ByteBuf * pBuf = NULL; pImage->convertToBuffer(&pBuf); UT_UTF8String sID = "snapshot-png-"; sID += szDataID; printf("Updating data-item of name %s \n",sID.utf8_str()); pDoc->replaceDataItem(sID.utf8_str(),reinterpret_cast< const UT_ByteBuf *>(pBuf)); delete pBuf; delete pImage; return true; } bool GR_MathManager::modify(UT_sint32 uid) { return false; } void GR_MathManager::initializeEmbedView(UT_sint32 uid) { SmartPtr pMathView = m_vecMathView.getNthItem(uid); UT_return_if_fail(pMathView); } void GR_MathManager::loadEmbedData(UT_sint32 uid) { SmartPtr pMathView = m_vecMathView.getNthItem(uid); UT_return_if_fail(pMathView); const PP_AttrProp * pSpanAP = NULL; GR_AbiMathItems * pItem = m_vecItems.getNthItem(uid); UT_return_if_fail(pItem); PT_AttrPropIndex api = pItem->m_iAPI; /* bool b = */ m_pDoc->getAttrProp(api, &pSpanAP); const char * pszDataID = NULL; bool bFoundDataID = pSpanAP->getAttribute("dataid", pszDataID); printf("DataID val =%s \n",pszDataID); UT_UTF8String sMathML; if (bFoundDataID && pszDataID) { const UT_ByteBuf * pByteBuf = NULL; bFoundDataID = m_pDoc->getDataItemDataByName(pszDataID, const_cast(&pByteBuf), NULL, NULL); UT_UCS4_mbtowc myWC; sMathML.appendBuf( *pByteBuf, myWC); } UT_return_if_fail(bFoundDataID); UT_return_if_fail(pszDataID); UT_DEBUGMSG(("MATH ML string is... \n %s \n",sMathML.utf8_str())); _loadMathML(uid, sMathML); } UT_sint32 GR_MathManager::getWidth(UT_sint32 uid) { SmartPtr pMathView = m_vecMathView.getNthItem(uid); BoundingBox box = pMathView->getBoundingBox(); return m_pAbiContext->toAbiLayoutUnits(box.width); } UT_sint32 GR_MathManager::getAscent(UT_sint32 uid) { SmartPtr pMathView = m_vecMathView.getNthItem(uid); BoundingBox box = pMathView->getBoundingBox(); return m_pAbiContext->toAbiLayoutUnits(box.height); } UT_sint32 GR_MathManager::getDescent(UT_sint32 uid) { SmartPtr pMathView = m_vecMathView.getNthItem(uid); BoundingBox box = pMathView->getBoundingBox(); return m_pAbiContext->toAbiLayoutUnits(box.depth); } void GR_MathManager::setColor(UT_sint32 uid, UT_RGBColor c) { m_pAbiContext->setColor(c); } void GR_MathManager::render(UT_sint32 uid, UT_Rect & rec) { scaled x = m_pAbiContext->fromAbiX(-rec.left); scaled y = m_pAbiContext->fromAbiLayoutUnits(rec.top);// should be fromAbiY() SmartPtr pMathView = m_vecMathView.getNthItem(uid); UT_return_if_fail(pMathView); pMathView->render(*m_pAbiContext, x, y); } void GR_MathManager::releaseEmbedView(UT_sint32 uid) { SmartPtr pMathView = m_vecMathView.getNthItem(uid); UT_return_if_fail(pMathView); pMathView->resetRootElement(); } static UT_uint32 MathManagerUID = 0; static GR_MathManager *pMathManager = NULL; ABI_PLUGIN_DECLARE(AbiMathView) // ----------------------------------------------------------------------- // // Abiword Plugin Interface // // ----------------------------------------------------------------------- ABI_FAR_CALL int abi_plugin_register (XAP_ModuleInfo * mi) { mi->name = "AbiMathView"; mi->desc = "The plugin allows AbiWord to be Grammar checked"; mi->version = ABI_VERSION_STRING; mi->author = "Martin Sevior "; mi->usage = "No Usage"; // Add to AbiWord's plugin listeners XAP_App * pApp = XAP_App::getApp(); pMathManager = new GR_MathManager(NULL); MathManagerUID = pApp->registerEmbeddable(pMathManager); UT_DEBUGMSG(("Class %x created! Listener Id %d \n",pAbiGrammar,listenerID)); // Add to AbiWord's menus. AbiMathView_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; XAP_App * pApp = XAP_App::getApp(); pApp->unRegisterEmbeddable(MathManagerUID); DELETEP(pMathManager); AbiMathView_removeFromMenus(); return 1; } ABI_FAR_CALL int abi_plugin_supports_version (UT_uint32 major, UT_uint32 minor, UT_uint32 release) { return 1; }