/* AbiWord * Copyright (C) 2003 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. */ #include #include #include #include "ut_assert.h" #include "ut_string.h" #include "ut_debugmsg.h" #include "ap_Dialog_Stylist.h" #include "ap_Dialog_EditStyle.h" #include "ap_Strings.h" #include "xap_App.h" #include "xap_Dialog_Id.h" #include "ap_Dialog_Id.h" #include "xap_DialogFactory.h" #include "xap_Dlg_MessageBox.h" #include "fv_View.h" #include "pd_Document.h" #include "pd_Style.h" #include "pt_Types.h" #include "fp_Line.h" #include "fp_Run.h" #include "fp_ContainerObject.h" #include "fp_TableContainer.h" #include "fl_TableLayout.h" #include "fl_BlockLayout.h" #include "fl_DocLayout.h" #include "ut_timer.h" #include "pd_Document.h" AP_Dialog_Stylist::AP_Dialog_Stylist(XAP_DialogFactory * pDlgFactory, XAP_Dialog_Id id) : XAP_Dialog_Modeless(pDlgFactory,id), m_bShowAll(false), m_bIsModal(false), m_pDoc(NULL), m_pAutoUpdater(0), m_iTick(0), m_sCurStyle(""), m_pStyleTree(NULL), m_bStyleTreeChanged(true), m_bStyleChanged(true) { } AP_Dialog_Stylist::~AP_Dialog_Stylist(void) { stopUpdater(); DELETEP(m_pStyleTree); } void AP_Dialog_Stylist::setActiveFrame(XAP_Frame * /*pFrame*/) { updateDialog(); notifyActiveFrame(getActiveFrame()); } void AP_Dialog_Stylist::startUpdater(void) { m_pAutoUpdater = UT_Timer::static_constructor(autoUpdate,this); m_pAutoUpdater->set(500); m_pAutoUpdater->start(); } /*! * Apply current style to the current selection in the current view */ void AP_Dialog_Stylist::Apply(void) { if(!getActiveFrame()) return; FV_View * pView = static_cast(getActiveFrame()->getCurrentView()); if(pView->getPoint() == 0) { return; } pView->setStyle(getCurStyle()->utf8_str()); pView->notifyListeners(AV_CHG_MOTION | AV_CHG_HDRFTR); } void AP_Dialog_Stylist::stopUpdater(void) { if(m_pAutoUpdater == NULL) { return; } m_pAutoUpdater->stop(); DELETEP(m_pAutoUpdater); } UT_sint32 AP_Dialog_Stylist::getNumStyles(void) const { if(m_pStyleTree == NULL) { return 0; } return m_pStyleTree->getNumStyles(); } /*! * Autoupdater of the dialog. */ void AP_Dialog_Stylist::autoUpdate(UT_Worker * pTimer) { UT_return_if_fail (pTimer); // this is a static callback method and does not have a 'this' pointer AP_Dialog_Stylist * pDialog = static_cast(pTimer->getInstanceData()); pDialog->updateDialog(); } /*! * This method actually updates the dialog, in particular the style Tree and * the current style. */ void AP_Dialog_Stylist::updateDialog(void) { XAP_Frame * frame = getActiveFrame(); if (frame) { // Handshaking code FV_View * pView = static_cast(frame->getCurrentView()); if(pView->getPoint() == 0) { return; } PD_Document * pDoc = pView->getDocument(); if(m_pStyleTree == NULL) { m_pStyleTree = new Stylist_tree(pDoc); } if((m_iTick != pView->getTick()) || (m_pDoc != pDoc)) { m_iTick = pView->getTick(); if((pDoc != m_pDoc) || (static_cast(pDoc->getStyleCount()) != getNumStyles())) { m_pDoc = pDoc; m_pStyleTree->buildStyles(pDoc); if(!m_bIsModal) // fill the current style if Modeless { const char * pszStyle; pView->getStyle(&pszStyle); m_sCurStyle = pszStyle; } m_bStyleTreeChanged =true; setStyleInGUI(); return; } const char * pszStyle; pView->getStyle(&pszStyle); UT_UTF8String sCurViewStyle; if(!m_bIsModal) { sCurViewStyle = pszStyle; } else { m_bStyleChanged =true; setStyleInGUI(); return; } if((sCurViewStyle.size ()) > 0 && m_sCurStyle.size() == 0) { m_sCurStyle = sCurViewStyle; m_bStyleChanged =true; setStyleInGUI(); return; } if(sCurViewStyle != m_sCurStyle) { m_sCurStyle = sCurViewStyle; m_bStyleChanged =true; setStyleInGUI(); return; } } } } /*! * Create a new style, using the properties at the caret. */ bool AP_Dialog_Stylist::createStyleFromDocument() { // // Get properties at the caret location. // gchar * sz_caretProps = getPropsAtCaret (); if (!sz_caretProps) return false; // // Create a name/label // // When the GUI returns, it will call _createNamedStyle to finish. return _getNameForNewStyle(sz_caretProps);; // if we made it here, we win } bool AP_Dialog_Stylist::_createNamedStyle(gchar * name, gchar * props) { const gchar * a[] = { \ PT_NAME_ATTRIBUTE_NAME, name, \ PT_LABEL_ATTRIBUTE_NAME, name, \ PT_TYPE_ATTRIBUTE_NAME, "P", \ PT_BASEDON_ATTRIBUTE_NAME, "Normal", \ PT_FOLLOWEDBY_ATTRIBUTE_NAME, "Current Settings", \ PT_PROPS_ATTRIBUTE_NAME, props, \ 0}; // // Add style to the document // // Dupes are dropped silently UT_return_val_if_fail(getDoc()->appendStyle(a), false); // // Update stylist dialog - code modified from updateDialog // Change style of current [UNIT_OF_TEXT] to the new one. // m_pStyleTree->buildStyles(m_pDoc); buildCommonStyles (m_pDoc); m_sCurStyle = name; m_bStyleTreeChanged =true; setStyleInGUI(); Apply(); return true; } /*! * Called by platform dialog code to redefine selected style in dialog * with the properties of the current caret location. */ bool AP_Dialog_Stylist::redefineStyleFromDocument(void) { // // Get properties at the caret location. // gchar * sz_caretProps = getPropsAtCaret (); if (!sz_caretProps) return false; // // Get our currently selected style // PD_Style * p_curStyle = 0; if (getDoc()) getDoc()->getStyle(getSelectedStyle().utf8_str(), & p_curStyle); else return false; UT_GenericVector vAttributes; UT_return_val_if_fail(p_curStyle->getAllAttributes(& vAttributes), false); // // Apply changes to style and to example text // UT_return_val_if_fail(p_curStyle->setAllProperties(sz_caretProps), false); Apply(); return true; } /*! * This method builds the simple list of styles for the dialog. Since this is * just building a vector, we store the data in the AP_Dialog_Stylist class * instead of building a new class like for the tree layout. */ void AP_Dialog_Stylist::buildCommonStyles(PD_Document * pDoc) { UT_DEBUGMSG(("In Build Common Styles num styles\n")); PD_Style * pStyle = NULL; UT_uint32 k; const gchar * szName = NULL; m_vCommonStyles.clear(); // RP-GSOC08 TODO: This needs to be fixed, localizing style labels // as required. for (k=0; (pDoc->enumStyles(k,&szName,const_cast(&pStyle))); k++) { if (!pStyle) { UT_DEBUGMSG(("no style instance for '%s'\n", szName)); } else { if (!pStyle->isDisplayed() && !(dynamic_cast(pStyle) && pStyle->isList() && pStyle->isUsed())) { continue; } } m_vCommonStyles.push_back(szName); } std::sort(m_vCommonStyles.begin(), m_vCommonStyles.end()); } gchar * AP_Dialog_Stylist::getPropsAtCaret() const { // // Load the properties at the current caret location. // if(!getActiveFrame()) return 0; FV_View * pView = static_cast(getActiveFrame()->getCurrentView()); const gchar ** paraProps = NULL; UT_return_val_if_fail(pView->getBlockFormat(¶Props,true), 0); const gchar ** charProps = NULL; UT_return_val_if_fail(pView->getCharFormat(&charProps,true), 0); // // Find the unique ones (not in the Normal or original style of run) // // Right now just putting them all together std::string sProps; const gchar * szName = NULL; const gchar * szValue = NULL; // Paragraph Properties int i=0; while(paraProps[i] != NULL) { szName = paraProps[i]; szValue = paraProps[i+1]; if(strstr(szName,"toc-") == NULL) { sProps += std::string(szName) + ":" + std::string(szValue) + ";"; } i = i + 2; } // Character Properties i=0; while(charProps[i] != NULL) { szName = charProps[i]; szValue = charProps[i+1]; if(strstr(szName,"toc-") == NULL) { sProps += std::string(szName) + ":" + std::string(szValue) + ";"; } i = i + 2; } // Remove trailing semicolon // property string has semicolon delimiters but a trailing semicolon will cause a failure. if (sProps.size() > 0 && sProps[sProps.size()-1] == ';') { sProps.resize(sProps.size()-1); } // duplicate the string and return it return g_strdup(sProps.c_str()); } /*! * Simplified style and invokes the manual style property editor. */ bool AP_Dialog_Stylist::editSelectedStyle() { UT_UTF8String sStyle = getSelectedStyle(); FV_View * pView = static_cast( getActiveFrame()->getCurrentView() ); UT_return_val_if_fail(pView, false); PD_Style * pStyle; m_pDoc->getStyle(sStyle.utf8_str(), & pStyle); UT_return_val_if_fail(pStyle, false); pStyle->simplifyProperties(); XAP_Frame * pFrame = static_cast ( pView->getParentData()); UT_return_val_if_fail(pFrame, false); XAP_DialogFactory * pDialogFactory = static_cast(pFrame->getDialogFactory()); AP_Dialog_EditStyle * pDialog = static_cast(pDialogFactory->requestDialog(AP_DIALOG_ID_EDITSTYLE)); UT_return_val_if_fail(pDialog, false); pDialog->setDialogData(sStyle, pStyle, m_pDoc); pDialog->runModal(pFrame); AP_Dialog_EditStyle::tAnswer ans = pDialog->getAnswer(); bool bOK = (ans == AP_Dialog_EditStyle::a_OK); return bOK; } /*! * Pointer to the PD_Document for the current document we're working with. */ PD_Document * AP_Dialog_Stylist::getDoc(void) const { return m_pDoc; } /*! * Finalize the dialog. */ void AP_Dialog_Stylist::finalize(void) { stopUpdater(); modeless_cleanup(); } /////////////////////////////////////////////////////////////////////////// /*! * This class holds the current style tree and provides a useful interface * to the tree for the GUI. */ Stylist_tree::Stylist_tree(PD_Document *pDoc) { m_vecAllStyles.clear(); m_vecStyleRows.clear(); buildStyles(pDoc); } Stylist_tree::~Stylist_tree(void) { UT_DEBUGMSG(("Deleteing Stylist_tree %x \n",this)); UT_VECTOR_PURGEALL(Stylist_row *, m_vecStyleRows); } /*! * This method builds the tree of styles from the Document given. */ void Stylist_tree::buildStyles(PD_Document * pDoc) { UT_sint32 numStyles = static_cast(pDoc->getStyleCount()); UT_sint32 i = 0; m_vecAllStyles.clear(); UT_VECTOR_PURGEALL(Stylist_row *, m_vecStyleRows); m_vecStyleRows.clear(); UT_GenericVector vecStyles; const PD_Style * pStyle = NULL; UT_DEBUGMSG(("In Build styles num styles in doc %d \n",numStyles)); UT_GenericVector * pStyles = NULL; pDoc->enumStyles(pStyles); UT_return_if_fail( pStyles ); for(i=0; i < numStyles; i++) { pStyle = pStyles->getNthItem(i); m_vecAllStyles.addItem(pStyle); vecStyles.addItem(pStyle); } delete pStyles; // // OK now build the tree of Styles // // Start with the heading styles. // const XAP_StringSet * pSS = XAP_App::getApp()->getStringSet (); Stylist_row * pStyleRow = new Stylist_row(); UT_UTF8String sTmp; pSS->getValueUTF8(AP_STRING_ID_DLG_Stylist_HeadingStyles, sTmp); pStyleRow->setRowName(sTmp); m_vecStyleRows.addItem(pStyleRow); for(i=0; i< numStyles; i++) { pStyle = vecStyles.getNthItem(i); if(isHeading(pStyle)) { sTmp = pStyle->getName(); pStyleRow->addStyle(sTmp); vecStyles.setNthItem(i,NULL,NULL); UT_DEBUGMSG(("Adding heading style %s \n",sTmp.utf8_str())); } } // // Next the list styles. // pStyleRow = new Stylist_row(); pSS->getValueUTF8(AP_STRING_ID_DLG_Stylist_ListStyles, sTmp); pStyleRow->setRowName(sTmp); m_vecStyleRows.addItem(pStyleRow); for(i=0; i< numStyles; i++) { pStyle = vecStyles.getNthItem(i); if(pStyle && isList(pStyle)) { sTmp = pStyle->getName(); pStyleRow->addStyle(sTmp); vecStyles.setNthItem(i,NULL,NULL); UT_DEBUGMSG(("Adding List style %s \n",sTmp.utf8_str())); } } // // Now the Footnote/Endnote. // pStyleRow = new Stylist_row(); pSS->getValueUTF8(AP_STRING_ID_DLG_Stylist_FootnoteStyles, sTmp); pStyleRow->setRowName(sTmp); m_vecStyleRows.addItem(pStyleRow); for(i=0; i< numStyles; i++) { pStyle = vecStyles.getNthItem(i); if(pStyle && isFootnote(pStyle)) { sTmp = pStyle->getName(); pStyleRow->addStyle(sTmp); vecStyles.setNthItem(i,NULL,NULL); UT_DEBUGMSG(("Adding Footnote style %s \n",sTmp.utf8_str())); } } // // Now the user-defined // pStyleRow = new Stylist_row(); pSS->getValueUTF8(AP_STRING_ID_DLG_Stylist_UserStyles, sTmp); pStyleRow->setRowName(sTmp); UT_sint32 iCount = 0; for(i=0; i< numStyles; i++) { pStyle = vecStyles.getNthItem(i); if(pStyle && isUser(pStyle)) { sTmp = pStyle->getName(); pStyleRow->addStyle(sTmp); vecStyles.setNthItem(i,NULL,NULL); iCount++; UT_DEBUGMSG(("Adding User-defined style %s \n",sTmp.utf8_str())); } } if(iCount > 0) { m_vecStyleRows.addItem(pStyleRow); } else { DELETEP(pStyleRow); } // // Now everything else // pSS->getValueUTF8(AP_STRING_ID_DLG_Stylist_MiscStyles, sTmp); pStyleRow = new Stylist_row(); pStyleRow->setRowName(sTmp); m_vecStyleRows.addItem(pStyleRow); for(i=0; i< numStyles; i++) { pStyle = vecStyles.getNthItem(i); if(pStyle) { sTmp = pStyle->getName(); pStyleRow->addStyle(sTmp); vecStyles.setNthItem(i,NULL,NULL); UT_DEBUGMSG(("Adding style %s \n",sTmp.utf8_str())); } } } /*! * Returns true if the style is withinthe "Heading" type of styles. */ bool Stylist_tree::isHeading(const PD_Style * pStyle, UT_sint32 iDepth) const { if(pStyle == NULL) { return false; } if(strstr(pStyle->getName(),"Heading") != 0) { return true; } PD_Style * pUpStyle = pStyle->getBasedOn(); if(pUpStyle != NULL && iDepth > 0) { return isHeading(pUpStyle,iDepth-1); } return false; } /*! * Returns true if the style is withinthe "List" type of styles. */ bool Stylist_tree::isList(const PD_Style * pStyle, UT_sint32 iDepth) const { if(pStyle == NULL) { return false; } if(strstr(pStyle->getName(),"List") != 0) { return true; } PD_Style * pUpStyle = pStyle->getBasedOn(); if(pUpStyle != NULL && iDepth > 0) { return isList(pUpStyle,iDepth-1); } return false; } /*! * Returns true if the style is withinthe "Footnote/Endnote" type of styles. */ bool Stylist_tree::isFootnote(const PD_Style * pStyle, UT_sint32 iDepth) const { if(pStyle == NULL) { return false; } if((strstr(pStyle->getName(),"Footnote") != 0) || (strstr(pStyle->getName(),"Endnote") != 0)) { return true; } PD_Style * pUpStyle = pStyle->getBasedOn(); if(pUpStyle != NULL && iDepth > 0) { return isFootnote(pUpStyle,iDepth-1); } return false; } /*! * Returns true if the style is a userdefined style. */ bool Stylist_tree::isUser(const PD_Style * pStyle) const { if(pStyle == NULL) { return false; } return pStyle->isUserDefined(); } /*! * Return the number of rows in the tree. */ UT_sint32 Stylist_tree::getNumRows(void) { return static_cast(m_vecStyleRows.getItemCount()); } /*! * Return the row and column address of the style given. If not found return * false. */ bool Stylist_tree::findStyle(UT_UTF8String & sStyleName,UT_sint32 & row, UT_sint32 & col) { UT_sint32 i =0; UT_sint32 numRows = getNumRows(); bool bFound = false; for(i=0; (i(m_vecStyleRows.getNthItem(i)); bFound = pStyleRow->findStyle(sStyleName,col); if(bFound) { row = i; return true; } } row = -1; col = -1; return false; } /*! * Return the style at the row and column given. If the (row,col) address is * valid, return false. */ bool Stylist_tree::getStyleAtRowCol(UT_UTF8String & sStyle,UT_sint32 row, UT_sint32 col) { if(row > getNumRows() || (row < 0)) { return false; } Stylist_row * pStyleRow = static_cast(m_vecStyleRows.getNthItem(row)); bool bFound = pStyleRow->getStyle(sStyle,col); return bFound; } /*! * return the number of columns at the row given. * If the row is invalid return 0. */ UT_sint32 Stylist_tree::getNumCols(UT_sint32 row) { if(row > getNumRows() || (row < 0)) { return 0; } Stylist_row * pStyleRow = static_cast(m_vecStyleRows.getNthItem(row)); return pStyleRow->getNumCols(); } /*! * Return the total number of styles in the tree. */ UT_sint32 Stylist_tree::getNumStyles(void) const { return static_cast(m_vecAllStyles.getItemCount()); } /*! * return the name of the row given. If the row is invalid return false; */ bool Stylist_tree::getNameOfRow(UT_UTF8String &sName, UT_sint32 row) { if(row > getNumRows() || (row<0) ) { return false; } Stylist_row * pStyleRow = static_cast(m_vecStyleRows.getNthItem(row)); pStyleRow->getRowName(sName); return true; } ////////////////////////////////////////////////////////////////////////// /*! * This class holds a row of style names and a useful API to access them. */ Stylist_row::Stylist_row(void): m_sRowName("") { m_vecStyles.clear(); UT_DEBUGMSG(("Creating Stylist_row %x \n",this)); } Stylist_row::~Stylist_row(void) { UT_DEBUGMSG(("Deleteing Stylist_row %x num styles %d\n",this,m_vecStyles.getItemCount())); UT_VECTOR_PURGEALL(UT_UTF8String *, m_vecStyles); } void Stylist_row::addStyle(UT_UTF8String & sStyle) { UT_UTF8String * psStyle = new UT_UTF8String(sStyle); m_vecStyles.addItem(psStyle); } void Stylist_row::setRowName(UT_UTF8String & sRowName) { m_sRowName = sRowName; } void Stylist_row::getRowName(UT_UTF8String & sRowName) { sRowName = m_sRowName; } UT_sint32 Stylist_row::getNumCols(void) { return static_cast(m_vecStyles.getItemCount()); } bool Stylist_row::findStyle(UT_UTF8String & sStyleName, UT_sint32 & col) { UT_sint32 i = 0; UT_sint32 numCols = getNumCols(); bool bFound = false; for(i=0; (i getNumCols()) || (col < 0)) { return false; } UT_UTF8String * psStyle = m_vecStyles.getNthItem(col); sStyleName = *psStyle; return true; }