libcmis/UnpackedTarball_cmis.mk | 1 libcmis/libcmis-0.3.0-lotus-live-fix.patch | 122 +++++++++++++++++++ sfx2/source/view/sfxbasecontroller.cxx | 2 sw/inc/IDocumentStatistics.hxx | 17 ++ sw/inc/doc.hxx | 16 ++ sw/inc/ndtxt.hxx | 4 sw/source/core/doc/doc.cxx | 184 ++++++++++++++++------------- sw/source/core/doc/docnew.cxx | 4 sw/source/core/txtnode/txtedt.cxx | 18 +- sw/source/ui/dialog/wordcountdialog.cxx | 2 sw/source/ui/inc/view.hxx | 6 sw/source/ui/uiview/view.cxx | 1 sw/source/ui/uiview/view2.cxx | 9 + ucb/source/ucp/cmis/cmis_content.cxx | 138 +++++++++++++++++---- ucb/source/ucp/cmis/cmis_content.hxx | 2 ucb/source/ucp/cmis/cmis_url.cxx | 29 +++- 16 files changed, 425 insertions(+), 130 deletions(-)
New commits: commit 57d87496d0ac8c35597acc3cf81609eb4aaa55df Author: Cédric Bosdonnat <cedric.bosdon...@free.fr> Date: Thu Feb 14 10:27:21 2013 +0100 CMIS: made it work with Lotus Live Making libcmis and LibreOffice work with Lotus Live service needed a few hacks to either better implement CMIS or workaround some bad implementations. As a general improvement, the CheckOut InfoBar isn't shown if the document can't be checked out. Change-Id: I7bb4211db0506998cef40ac1fb6375e647234085 diff --git a/libcmis/UnpackedTarball_cmis.mk b/libcmis/UnpackedTarball_cmis.mk index a25c316..0a9f9f8 100644 --- a/libcmis/UnpackedTarball_cmis.mk +++ b/libcmis/UnpackedTarball_cmis.mk @@ -18,6 +18,7 @@ $(eval $(call gb_UnpackedTarball_add_patches,cmis, \ libcmis/libcmis-0.3.0-win.patch \ libcmis/libcmis-0.3.0.patch \ libcmis/libcmis-0.3.0-proxy.patch \ + libcmis/libcmis-0.3.0-lotus-live-fix.patch \ )) ifeq ($(OS)$(COM),WNTMSC) diff --git a/libcmis/libcmis-0.3.0-lotus-live-fix.patch b/libcmis/libcmis-0.3.0-lotus-live-fix.patch new file mode 100644 index 0000000..2aca934 --- /dev/null +++ b/libcmis/libcmis-0.3.0-lotus-live-fix.patch @@ -0,0 +1,122 @@ +diff --git src/libcmis/atom-folder.cxx src/libcmis/atom-folder.cxx +index 68fb124..2756a5d 100644 +--- src/libcmis/atom-folder.cxx ++++ src/libcmis/atom-folder.cxx +@@ -57,8 +57,11 @@ vector< libcmis::ObjectPtr > AtomFolder::getChildren( ) throw ( libcmis::Excepti + { + AtomLink* childrenLink = getLink( "down", "application/atom+xml;type=feed" ); + ++ // Some servers aren't giving the GetChildren properly... if not defined, we need to try ++ // as we may have the right to proceed. + if ( ( NULL == childrenLink ) || ( getAllowableActions( ).get() && +- !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetChildren ) ) ) ++ ( !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetChildren ) && ++ getAllowableActions()->isDefined( libcmis::ObjectAction::GetChildren ) ) ) ) + throw libcmis::Exception( string( "GetChildren not allowed on node " ) + getId() ); + + vector< libcmis::ObjectPtr > children; +@@ -182,7 +185,8 @@ libcmis::DocumentPtr AtomFolder::createDocument( const map< string, libcmis::Pro + AtomLink* childrenLink = getLink( "down", "application/atom+xml;type=feed" ); + + if ( ( NULL == childrenLink ) || ( getAllowableActions( ).get() && +- !getAllowableActions()->isAllowed( libcmis::ObjectAction::CreateDocument ) ) ) ++ !getAllowableActions()->isAllowed( libcmis::ObjectAction::CreateDocument ) && ++ getAllowableActions()->isDefined( libcmis::ObjectAction::CreateDocument ) ) ) + throw libcmis::Exception( string( "CreateDocument not allowed on folder " ) + getId() ); + + xmlBufferPtr buf = xmlBufferCreate( ); +@@ -210,9 +214,37 @@ libcmis::DocumentPtr AtomFolder::createDocument( const map< string, libcmis::Pro + } + + string respBuf = response->getStream( )->str( ); +- xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, 0 ); ++ xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, XML_PARSE_NOERROR ); + if ( NULL == doc ) +- throw libcmis::Exception( "Failed to parse object infos" ); ++ { ++ // We may not have the created document entry in the response body: this is ++ // the behaviour of some servers, but the standard says we need to look for ++ // the Location header. ++ map< string, string >& headers = response->getHeaders( ); ++ map< string, string >::iterator it = headers.find( "Location" ); ++ ++ // Some servers like Lotus Live aren't sending Location header, but Content-Location ++ if ( it == headers.end( ) ) ++ it = headers.find( "Content-Location" ); ++ ++ if ( it != headers.end() ) ++ { ++ try ++ { ++ response = getSession( )->httpGetRequest( it->second ); ++ respBuf = response->getStream( )->str( ); ++ doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL, XML_PARSE_NOERROR ); ++ } ++ catch ( const CurlException& e ) ++ { ++ throw e.getCmisException( ); ++ } ++ } ++ ++ // if doc is still NULL after that, then throw an exception ++ if ( NULL == doc ) ++ throw libcmis::Exception( "Missing expected response from server" ); ++ } + + libcmis::ObjectPtr created = getSession( )->createObjectFromEntryDoc( doc ); + xmlFreeDoc( doc ); +diff --git src/libcmis/atom-object.cxx src/libcmis/atom-object.cxx +index b7b3b4a..812951d 100644 +--- src/libcmis/atom-object.cxx ++++ src/libcmis/atom-object.cxx +@@ -140,6 +140,34 @@ libcmis::ObjectPtr AtomObject::updateProperties( const map< string, libcmis::Pro + return updated; + } + ++libcmis::AllowableActionsPtr AtomObject::getAllowableActions( ) ++{ ++ if ( !m_allowableActions ) ++ { ++ // For some reason we had no allowable actions before, get them now. ++ AtomLink* link = getLink( "http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions", "application/cmisallowableactions+xml" ); ++ if ( link ) ++ { ++ try ++ { ++ libcmis::HttpResponsePtr response = getSession()->httpGetRequest( link->getHref() ); ++ string buf = response->getStream()->str(); ++ xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), link->getHref().c_str(), NULL, 0 ); ++ xmlNodePtr actionsNode = xmlDocGetRootElement( doc ); ++ if ( actionsNode ) ++ m_allowableActions.reset( new libcmis::AllowableActions( actionsNode ) ); ++ ++ xmlFreeDoc( doc ); ++ } ++ catch ( libcmis::Exception& ) ++ { ++ } ++ } ++ } ++ ++ return libcmis::Object::getAllowableActions(); ++} ++ + void AtomObject::refreshImpl( xmlDocPtr doc ) throw ( libcmis::Exception ) + { + bool createdDoc = ( NULL == doc ); +diff --git src/libcmis/atom-object.hxx src/libcmis/atom-object.hxx +index 2d1761d..452b4f5 100644 +--- src/libcmis/atom-object.hxx ++++ src/libcmis/atom-object.hxx +@@ -69,6 +69,8 @@ class AtomObject : public virtual libcmis::Object + virtual libcmis::ObjectPtr updateProperties( + const std::map< std::string, libcmis::PropertyPtr >& properties ) throw ( libcmis::Exception ); + ++ virtual libcmis::AllowableActionsPtr getAllowableActions( ); ++ + /** Reload the data from the server. + */ + virtual void refresh( ) throw ( libcmis::Exception ) { refreshImpl( NULL ); } +-- +1.7.10.4 + diff --git a/sfx2/source/view/sfxbasecontroller.cxx b/sfx2/source/view/sfxbasecontroller.cxx index f1b2b01..c97d014 100644 --- a/sfx2/source/view/sfxbasecontroller.cxx +++ b/sfx2/source/view/sfxbasecontroller.cxx @@ -1439,7 +1439,7 @@ void SfxBaseController::ShowInfoBars( ) { // CMIS verifications REFERENCE< document::XCmisDocument > xCmisDoc( m_pData->m_pViewShell->GetObjectShell()->GetModel(), uno::UNO_QUERY ); - if ( xCmisDoc.is( ) ) + if ( xCmisDoc.is( ) && xCmisDoc->canCheckOut( ) ) { beans::PropertyValues aCmisProperties = xCmisDoc->getCmisPropertiesValues( ); diff --git a/ucb/source/ucp/cmis/cmis_content.cxx b/ucb/source/ucp/cmis/cmis_content.cxx index 5f94d7a..2a78b4e 100644 --- a/ucb/source/ucp/cmis/cmis_content.cxx +++ b/ucb/source/ucp/cmis/cmis_content.cxx @@ -307,7 +307,46 @@ namespace cmis if ( NULL == m_pObjectType.get( ) && m_bTransient ) { string typeId = m_bIsFolder ? "cmis:folder" : "cmis:document"; - m_pObjectType = getSession( xEnv )->getType( typeId ); + // The type to create needs to be fetched from the possible children types + // defined in the parent folder. Then, we'll pick up the first one we find matching + // cmis:folder or cmis:document (depending what we need to create). + // The easy case will work in most cases, but not on some servers (like Lotus Live) + libcmis::Folder* pParent = NULL; + bool bTypeRestricted = false; + try + { + pParent = dynamic_cast< libcmis::Folder* >( getObject( xEnv ).get( ) ); + } + catch ( const libcmis::Exception& ) + { + } + + if ( pParent ) + { + map< string, libcmis::PropertyPtr >& aProperties = pParent->getProperties( ); + map< string, libcmis::PropertyPtr >::iterator it = aProperties.find( "cmis:allowedChildObjectTypeIds" ); + if ( it != aProperties.end( ) ) + { + libcmis::PropertyPtr pProperty = it->second; + if ( pProperty ) + { + vector< string > typesIds = pProperty->getStrings( ); + for ( vector< string >::iterator typeIt = typesIds.begin(); + typeIt != typesIds.end() && !m_pObjectType; ++typeIt ) + { + bTypeRestricted = true; + libcmis::ObjectTypePtr type = getSession( xEnv )->getType( *typeIt ); + + // FIXME Improve performances by adding getBaseTypeId( ) method to libcmis + if ( type->getBaseType( )->getId( ) == typeId ) + m_pObjectType = type; + } + } + } + } + + if ( !bTypeRestricted ) + m_pObjectType = getSession( xEnv )->getType( typeId ); } return m_pObjectType; } @@ -318,7 +357,39 @@ namespace cmis if ( !m_pObject.get() ) { if ( !m_sObjectPath.isEmpty( ) ) - m_pObject = getSession( xEnv )->getObjectByPath( OUSTR_TO_STDSTR( m_sObjectPath ) ); + { + try + { + m_pObject = getSession( xEnv )->getObjectByPath( OUSTR_TO_STDSTR( m_sObjectPath ) ); + } + catch ( const libcmis::Exception& ) + { + // In some cases, getting the object from the path doesn't work, + // but getting the parent from its path and the get the child in the list is OK. + // It's weird, but needed to handle case where the path isn't the folders/files + // names separated by '/' (as in Lotus Live) + INetURLObject aParentUrl( m_sURL ); + string sName = OUSTR_TO_STDSTR( aParentUrl.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DECODE_WITH_CHARSET ) ); + aParentUrl.removeSegment( ); + rtl::OUString sParentUrl = aParentUrl.GetMainURL( INetURLObject::NO_DECODE ); + + Content aParent( m_xContext, m_pProvider, new ucbhelper::ContentIdentifier( sParentUrl ) ); + libcmis::FolderPtr pParentFolder = boost::dynamic_pointer_cast< libcmis::Folder >( aParent.getObject( xEnv ) ); + if ( pParentFolder ) + { + vector< libcmis::ObjectPtr > children = pParentFolder->getChildren( ); + for ( vector< libcmis::ObjectPtr >::iterator it = children.begin( ); + it != children.end() && !m_pObject; ++it ) + { + if ( ( *it )->getName( ) == sName ) + m_pObject = *it; + } + } + + if ( !m_pObject ) + throw libcmis::Exception( "Object not found" ); + } + } else if (!m_sObjectId.isEmpty( ) ) m_pObject = getSession( xEnv )->getObject( OUSTR_TO_STDSTR( m_sObjectId ) ); else @@ -643,25 +714,6 @@ namespace cmis return uno::Reference< sdbc::XRow >( xRow.get() ); } - bool Content::exists( const uno::Reference< ucb::XCommandEnvironment >& xEnv ) - { - bool bExists = true; - try - { - if ( !m_sObjectPath.isEmpty( ) ) - m_pSession->getObjectByPath( OUSTR_TO_STDSTR( m_sObjectPath ) ); - else if ( !m_sObjectId.isEmpty( ) ) - getSession( xEnv )->getObject( OUSTR_TO_STDSTR( m_sObjectId ) ); - // No need to handle the root folder case... how can it not exists? - } - catch ( const libcmis::Exception& ) - { - bExists = false; - } - - return bExists; - } - uno::Any Content::open(const ucb::OpenCommandArgument2 & rOpenCommand, const uno::Reference< ucb::XCommandEnvironment > & xEnv ) throw( uno::Exception ) @@ -669,7 +721,7 @@ namespace cmis bool bIsFolder = isFolder( xEnv ); // Handle the case of the non-existing file - if ( !exists( xEnv ) ) + if ( !getObject( xEnv ) ) { uno::Sequence< uno::Any > aArgs( 1 ); aArgs[ 0 ] <<= m_xIdentifier->getContentIdentifier(); @@ -992,7 +1044,17 @@ namespace cmis boost::shared_ptr< ostream > pOut( new ostringstream ( ios_base::binary | ios_base::in | ios_base::out ) ); uno::Reference < io::XOutputStream > xOutput = new ucbhelper::StdOutputStream( pOut ); copyData( xInputStream, xOutput ); - document->setContentStream( pOut, OUSTR_TO_STDSTR( rMimeType ), string( ), bReplaceExisting ); + try + { + document->setContentStream( pOut, OUSTR_TO_STDSTR( rMimeType ), string( ), bReplaceExisting ); + } + catch ( const libcmis::Exception& ) + { + ucbhelper::cancelCommandExecution( uno::makeAny + ( uno::RuntimeException( "Error when setting document content", + static_cast< cppu::OWeakObject * >( this ) ) ), + xEnv ); + } } } else @@ -1003,16 +1065,36 @@ namespace cmis if ( bIsFolder ) { - libcmis::FolderPtr pNew = pFolder->createFolder( m_pObjectProps ); - sNewPath = STD_TO_OUSTR( newPath ); + try + { + libcmis::FolderPtr pNew = pFolder->createFolder( m_pObjectProps ); + sNewPath = STD_TO_OUSTR( newPath ); + } + catch ( const libcmis::Exception& ) + { + ucbhelper::cancelCommandExecution( uno::makeAny + ( uno::RuntimeException( "Error when creating folder", + static_cast< cppu::OWeakObject * >( this ) ) ), + xEnv ); + } } else { boost::shared_ptr< ostream > pOut( new ostringstream ( ios_base::binary | ios_base::in | ios_base::out ) ); uno::Reference < io::XOutputStream > xOutput = new ucbhelper::StdOutputStream( pOut ); copyData( xInputStream, xOutput ); - pFolder->createDocument( m_pObjectProps, pOut, OUSTR_TO_STDSTR( rMimeType ), string() ); - sNewPath = STD_TO_OUSTR( newPath ); + try + { + pFolder->createDocument( m_pObjectProps, pOut, OUSTR_TO_STDSTR( rMimeType ), string() ); + sNewPath = STD_TO_OUSTR( newPath ); + } + catch ( const libcmis::Exception& ) + { + ucbhelper::cancelCommandExecution( uno::makeAny + ( uno::RuntimeException( "Error when creating document", + static_cast< cppu::OWeakObject * >( this ) ) ), + xEnv ); + } } } @@ -1336,7 +1418,7 @@ namespace cmis { URL aCmisUrl( m_sURL ); aUrl.removeSegment( ); - aCmisUrl.setObjectPath( aUrl.GetURLPath( INetURLObject::NO_DECODE ) ); + aCmisUrl.setObjectPath( aUrl.GetURLPath( INetURLObject::DECODE_WITH_CHARSET ) ); sRet = aCmisUrl.asString( ); } } diff --git a/ucb/source/ucp/cmis/cmis_content.hxx b/ucb/source/ucp/cmis/cmis_content.hxx index b28c301..7260cb3 100644 --- a/ucb/source/ucp/cmis/cmis_content.hxx +++ b/ucb/source/ucp/cmis/cmis_content.hxx @@ -99,8 +99,6 @@ private: libcmis::Session* getSession( const com::sun::star::uno::Reference< com::sun::star::ucb::XCommandEnvironment >& xEnv ); libcmis::ObjectTypePtr getObjectType( const com::sun::star::uno::Reference< com::sun::star::ucb::XCommandEnvironment >& xEnv ); - bool exists( const com::sun::star::uno::Reference< com::sun::star::ucb::XCommandEnvironment >& xEnv ); - private: typedef rtl::Reference< Content > ContentRef; typedef std::list< ContentRef > ContentRefList; diff --git a/ucb/source/ucp/cmis/cmis_url.cxx b/ucb/source/ucp/cmis/cmis_url.cxx index 40d7a25..dc23c9e 100644 --- a/ucb/source/ucp/cmis/cmis_url.cxx +++ b/ucb/source/ucp/cmis/cmis_url.cxx @@ -107,13 +107,34 @@ namespace cmis if ( !m_sPath.isEmpty( ) ) { - if ( m_sPath[0] != '/' ) - sUrl += "/"; - sUrl += m_sPath; + sal_Int32 nPos = -1; + rtl::OUString sEncodedPath; + do + { + sal_Int32 nStartPos = nPos + 1; + nPos = m_sPath.indexOf( '/', nStartPos ); + sal_Int32 nLen = nPos - nStartPos; + if ( nPos == -1 ) + nLen = m_sPath.getLength( ) - nStartPos; + rtl::OUString sSegment = m_sPath.copy( nStartPos, nLen ); + + if ( !sSegment.isEmpty( ) ) + { + sEncodedPath += "/" + rtl::Uri::encode( sSegment, + rtl_UriCharClassRelSegment, + rtl_UriEncodeKeepEscapes, + RTL_TEXTENCODING_UTF8 ); + } + } + while ( nPos != -1 ); + sUrl += sEncodedPath; } else if ( !m_sId.isEmpty( ) ) { - sUrl += "#" + m_sId; + sUrl += "#" + rtl::Uri::encode( m_sId, + rtl_UriCharClassRelSegment, + rtl_UriEncodeKeepEscapes, + RTL_TEXTENCODING_UTF8 ); } return sUrl; commit 6dbc7fdd094b3c29de0932c4ea3afc1a5a10d7fc Author: Michael Meeks <michael.me...@suse.com> Date: Wed Feb 13 11:35:13 2013 +0000 asynchronous word-count. Change-Id: Ie78819590bca52f36406022a3954651c42c52540 Signed-off-by: Caolán McNamara <caol...@redhat.com> diff --git a/sw/inc/IDocumentStatistics.hxx b/sw/inc/IDocumentStatistics.hxx index 4ec926a..2c5a0501 100644 --- a/sw/inc/IDocumentStatistics.hxx +++ b/sw/inc/IDocumentStatistics.hxx @@ -35,13 +35,26 @@ /** Document - Statistics */ + /// Returns a reference to the existing document statistics virtual const SwDocStat &GetDocStat() const = 0; - virtual const SwDocStat &GetUpdatedDocStat() = 0; + /** + * Updates the document statistics if the document has been + * modified and returns a reference to the result. + * \param bCompleteAsync if true will return a partial result, + * and potentially trigger a timeout to complete the work. + */ + virtual const SwDocStat &GetUpdatedDocStat(bool bCompleteAsync) = 0; + /// Set the document statistics virtual void SetDocStat(const SwDocStat& rStat) = 0; - virtual void UpdateDocStat() = 0; + /** + * Updates the internal document's statistics + * \param bCompleteAsync if true it may do part of the + * work and trigger a timeout to complete it. + */ + virtual void UpdateDocStat(bool bCompleteAsync) = 0; protected: virtual ~IDocumentStatistics() {}; diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx index 34ba7df..d71ce52 100644 --- a/sw/inc/doc.hxx +++ b/sw/inc/doc.hxx @@ -282,6 +282,7 @@ class SW_DLLPUBLIC SwDoc : */ Timer aIdleTimer; ///< Own IdleTimer Timer aOLEModifiedTimer; ///< Timer for update modified OLE-Objecs + Timer aStatsUpdateTimer; ///< Timer for asynchronous stats calculation SwDBData aDBData; ///< database descriptor ::com::sun::star::uno::Sequence <sal_Int8 > aRedlinePasswd; String sTOIAutoMarkURL; ///< ::com::sun::star::util::URL of table of index AutoMark file @@ -909,9 +910,9 @@ public: */ virtual void DocInfoChgd(); virtual const SwDocStat &GetDocStat() const; - virtual const SwDocStat &GetUpdatedDocStat(); + virtual const SwDocStat &GetUpdatedDocStat(bool bCompleteAsync = false); virtual void SetDocStat(const SwDocStat& rStat); - virtual void UpdateDocStat(); + virtual void UpdateDocStat(bool bCompleteAsync = false); /** IDocumentState */ @@ -2070,6 +2071,17 @@ private: void CopyMasterHeader(const SwPageDesc &rChged, const SwFmtHeader &rHead, SwPageDesc *pDesc, bool bLeft); /// Copies master footer to left / first one, if necessary - used by ChgPageDesc(). void CopyMasterFooter(const SwPageDesc &rChged, const SwFmtFooter &rFoot, SwPageDesc *pDesc, bool bLeft); + + /** continue computing a chunk of document statistics + * \param nTextNodes number of paragraphs to calculate before + * exiting + * + * returns false when there is no more to calculate + */ + bool IncrementalDocStatCalculate(long nTextNodes = 250); + + /// Our own 'StatsUpdateTimer' calls the following method + DECL_LINK( DoIdleStatsUpdate, Timer * ); }; // This method is called in Dtor of SwDoc and deletes cache of ContourObjects. diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx index c0bf53b..2b0d8cd 100644 --- a/sw/inc/ndtxt.hxx +++ b/sw/inc/ndtxt.hxx @@ -745,8 +745,8 @@ public: xub_StrLen nStart, xub_StrLen nEnd, SwUndoTransliterate* pUndo = 0 ); - /// count words in given range - void CountWords( SwDocStat& rStat, xub_StrLen nStart, xub_StrLen nEnd ) const; + /// count words in given range - returns true if we refreshed out count + bool CountWords( SwDocStat& rStat, xub_StrLen nStart, xub_StrLen nEnd ) const; /** Checks some global conditions like loading or destruction of document to economize notifications */ diff --git a/sw/source/core/doc/doc.cxx b/sw/source/core/doc/doc.cxx index 80cfa84..7a27c23 100644 --- a/sw/source/core/doc/doc.cxx +++ b/sw/source/core/doc/doc.cxx @@ -55,6 +55,7 @@ #include <editeng/rsiditem.hxx> #include <unotools/charclass.hxx> #include <unotools/localedatawrapper.hxx> +#include <vcl/timer.hxx> #include <swatrset.hxx> #include <swmodule.hxx> @@ -112,6 +113,7 @@ #include <shellres.hxx> #include <txtfrm.hxx> #include <attrhint.hxx> +#include <view.hxx> #include <wdocsh.hxx> // SwWebDocShell #include <prtopt.hxx> // SwPrintOptions @@ -1146,10 +1148,6 @@ bool SwDoc::UpdateParRsid( SwTxtNode *pTxtNode, sal_uInt32 nVal ) return pTxtNode->SetAttr( aRsid ); } - -/************************************************************************* - * void SetDocStat( const SwDocStat& rStat ); - *************************************************************************/ void SwDoc::SetDocStat( const SwDocStat& rStat ) { *pDocStat = rStat; @@ -1160,11 +1158,11 @@ const SwDocStat& SwDoc::GetDocStat() const return *pDocStat; } -const SwDocStat& SwDoc::GetUpdatedDocStat() +const SwDocStat& SwDoc::GetUpdatedDocStat( bool bCompleteAsync ) { - if (pDocStat->bModified) + if( pDocStat->bModified ) { - UpdateDocStat(); + UpdateDocStat( bCompleteAsync ); } return *pDocStat; } @@ -1686,92 +1684,120 @@ void SwDoc::CalculatePagePairsForProspectPrinting( // thus we are done here. } -/************************************************************************* - * void UpdateDocStat(); - *************************************************************************/ -void SwDoc::UpdateDocStat() +// returns true while there is more to do +bool SwDoc::IncrementalDocStatCalculate( long nTextNodes ) { - if( pDocStat->bModified ) - { - pDocStat->Reset(); - pDocStat->nPara = 0; // default is 1! - SwNode* pNd; + pDocStat->Reset(); + pDocStat->nPara = 0; // default is 1! + SwNode* pNd; - for( sal_uLong i = GetNodes().Count(); i; ) + // This is the inner loop - at least while the paras are dirty. + for( sal_uLong i = GetNodes().Count(); i > 0 && nTextNodes > 0; ) + { + switch( ( pNd = GetNodes()[ --i ])->GetNodeType() ) { - switch( ( pNd = GetNodes()[ --i ])->GetNodeType() ) - { - case ND_TEXTNODE: - ((SwTxtNode*)pNd)->CountWords( *pDocStat, 0, ((SwTxtNode*)pNd)->GetTxt().Len() ); - break; - case ND_TABLENODE: ++pDocStat->nTbl; break; - case ND_GRFNODE: ++pDocStat->nGrf; break; - case ND_OLENODE: ++pDocStat->nOLE; break; - case ND_SECTIONNODE: break; - } + case ND_TEXTNODE: + { + SwTxtNode *pTxt = static_cast< SwTxtNode * >( pNd ); + if( pTxt->CountWords( *pDocStat, 0, pTxt->GetTxt().Len() ) ) + nTextNodes--; + break; + } + case ND_TABLENODE: ++pDocStat->nTbl; break; + case ND_GRFNODE: ++pDocStat->nGrf; break; + case ND_OLENODE: ++pDocStat->nOLE; break; + case ND_SECTIONNODE: break; } + } - // #i93174#: notes contain paragraphs that are not nodes + // #i93174#: notes contain paragraphs that are not nodes + { + SwFieldType * const pPostits( GetSysFldType(RES_POSTITFLD) ); + SwIterator<SwFmtFld,SwFieldType> aIter( *pPostits ); + for( SwFmtFld* pFmtFld = aIter.First(); pFmtFld; pFmtFld = aIter.Next() ) { - SwFieldType * const pPostits( GetSysFldType(RES_POSTITFLD) ); - SwIterator<SwFmtFld,SwFieldType> aIter( *pPostits ); - for( SwFmtFld* pFmtFld = aIter.First(); pFmtFld; pFmtFld = aIter.Next() ) + if (pFmtFld->IsFldInDoc()) { - if (pFmtFld->IsFldInDoc()) - { - SwPostItField const * const pField( - static_cast<SwPostItField const*>(pFmtFld->GetFld())); - pDocStat->nAllPara += pField->GetNumberOfParagraphs(); - } + SwPostItField const * const pField( + static_cast<SwPostItField const*>(pFmtFld->GetFld())); + pDocStat->nAllPara += pField->GetNumberOfParagraphs(); } } + } - pDocStat->nPage = GetCurrentLayout() ? GetCurrentLayout()->GetPageNum() : 0; //swmod 080218 - pDocStat->bModified = sal_False; - - com::sun::star::uno::Sequence < com::sun::star::beans::NamedValue > aStat( pDocStat->nPage ? 8 : 7); - sal_Int32 n=0; - aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("TableCount")); - aStat[n++].Value <<= (sal_Int32)pDocStat->nTbl; - aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ImageCount")); - aStat[n++].Value <<= (sal_Int32)pDocStat->nGrf; - aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ObjectCount")); - aStat[n++].Value <<= (sal_Int32)pDocStat->nOLE; - if ( pDocStat->nPage ) - { - aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("PageCount")); - aStat[n++].Value <<= (sal_Int32)pDocStat->nPage; - } - aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ParagraphCount")); - aStat[n++].Value <<= (sal_Int32)pDocStat->nPara; - aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("WordCount")); - aStat[n++].Value <<= (sal_Int32)pDocStat->nWord; - aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharacterCount")); - aStat[n++].Value <<= (sal_Int32)pDocStat->nChar; - aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("NonWhitespaceCharacterCount")); - aStat[n++].Value <<= (sal_Int32)pDocStat->nCharExcludingSpaces; - - // For e.g. autotext documents there is no pSwgInfo (#i79945) - SfxObjectShell * const pObjShell( GetDocShell() ); - if (pObjShell) - { - const uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDocStat->nPage = GetCurrentLayout() ? GetCurrentLayout()->GetPageNum() : 0; + pDocStat->bModified = sal_False; + + com::sun::star::uno::Sequence < com::sun::star::beans::NamedValue > aStat( pDocStat->nPage ? 8 : 7); + sal_Int32 n=0; + aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("TableCount")); + aStat[n++].Value <<= (sal_Int32)pDocStat->nTbl; + aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ImageCount")); + aStat[n++].Value <<= (sal_Int32)pDocStat->nGrf; + aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ObjectCount")); + aStat[n++].Value <<= (sal_Int32)pDocStat->nOLE; + if ( pDocStat->nPage ) + { + aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("PageCount")); + aStat[n++].Value <<= (sal_Int32)pDocStat->nPage; + } + aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ParagraphCount")); + aStat[n++].Value <<= (sal_Int32)pDocStat->nPara; + aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("WordCount")); + aStat[n++].Value <<= (sal_Int32)pDocStat->nWord; + aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharacterCount")); + aStat[n++].Value <<= (sal_Int32)pDocStat->nChar; + aStat[n].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("NonWhitespaceCharacterCount")); + aStat[n++].Value <<= (sal_Int32)pDocStat->nCharExcludingSpaces; + + // For e.g. autotext documents there is no pSwgInfo (#i79945) + SfxObjectShell * const pObjShell( GetDocShell() ); + if (pObjShell) + { + const uno::Reference<document::XDocumentPropertiesSupplier> xDPS( pObjShell->GetModel(), uno::UNO_QUERY_THROW); - const uno::Reference<document::XDocumentProperties> xDocProps( + const uno::Reference<document::XDocumentProperties> xDocProps( xDPS->getDocumentProperties()); - // #i96786#: do not set modified flag when updating statistics - const bool bDocWasModified( IsModified() ); - const ModifyBlocker_Impl b(pObjShell); - xDocProps->setDocumentStatistics(aStat); - if (!bDocWasModified) - { - ResetModified(); - } + // #i96786#: do not set modified flag when updating statistics + const bool bDocWasModified( IsModified() ); + const ModifyBlocker_Impl b(pObjShell); + xDocProps->setDocumentStatistics(aStat); + if (!bDocWasModified) + { + ResetModified(); } + } + + // optionally update stat. fields + SwFieldType *pType = GetSysFldType(RES_DOCSTATFLD); + pType->UpdateFlds(); - // optionally update stat. fields - SwFieldType *pType = GetSysFldType(RES_DOCSTATFLD); - pType->UpdateFlds(); + return nTextNodes <= 0; +} + +IMPL_LINK( SwDoc, DoIdleStatsUpdate, Timer *, pTimer ) +{ + (void)pTimer; + if( IncrementalDocStatCalculate( 1000 ) ) + aStatsUpdateTimer.Start(); + + SwView* pView = GetDocShell() ? GetDocShell()->GetView() : NULL; + if( pView ) + pView->UpdateDocStats(); + return 0; +} + +void SwDoc::UpdateDocStat( bool bCompleteAsync ) +{ + if( pDocStat->bModified ) + { + if (!bCompleteAsync) + { + while (IncrementalDocStatCalculate()) {} + aStatsUpdateTimer.Stop(); + } + else if (IncrementalDocStatCalculate()) + aStatsUpdateTimer.Start(); } } diff --git a/sw/source/core/doc/docnew.cxx b/sw/source/core/doc/docnew.cxx index d5f55f7..9eb5983 100644 --- a/sw/source/core/doc/docnew.cxx +++ b/sw/source/core/doc/docnew.cxx @@ -403,6 +403,9 @@ SwDoc::SwDoc() aOLEModifiedTimer.SetTimeout( 1000 ); aOLEModifiedTimer.SetTimeoutHdl( LINK( this, SwDoc, DoUpdateModifiedOLE )); + aStatsUpdateTimer.SetTimeout( 100 ); + aStatsUpdateTimer.SetTimeoutHdl( LINK( this, SwDoc, DoIdleStatsUpdate ) ); + // Create DBMgr pNewDBMgr = new SwNewDBMgr; @@ -508,6 +511,7 @@ SwDoc::~SwDoc() SetDefault(aCharFmt); StopIdling(); // stop idle timer + aStatsUpdateTimer.Stop(); delete pUnoCallBack, pUnoCallBack = 0; delete pURLStateChgd; diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx index e0cc703..a538701 100644 --- a/sw/source/core/txtnode/txtedt.cxx +++ b/sw/source/core/txtnode/txtedt.cxx @@ -1842,22 +1842,24 @@ void SwTxtNode::ReplaceTextOnly( xub_StrLen nPos, xub_StrLen nLen, NotifyClients( 0, &aHint ); } -void SwTxtNode::CountWords( SwDocStat& rStat, +// the return values allows us to see if we did the heavy- +// lifting required to actually break and count the words. +bool SwTxtNode::CountWords( SwDocStat& rStat, xub_StrLen nStt, xub_StrLen nEnd ) const { if( nStt > nEnd ) { // bad call - return; + return false; } if (IsInRedlines()) { //not counting txtnodes used to hold deleted redline content - return; + return false; } bool bCountAll = ( (0 == nStt) && (GetTxt().Len() == nEnd) ); ++rStat.nAllPara; // #i93174#: count _all_ paragraphs if ( IsHidden() ) { // not counting hidden paras - return; + return false; } // count words in numbering string if started at beginning of para: bool bCountNumbering = nStt == 0; @@ -1874,7 +1876,7 @@ void SwTxtNode::CountWords( SwDocStat& rStat, if( nStt == nEnd && !bCountNumbering) { // unnumbered empty node or empty selection - return; + return false; } // count of non-empty paras @@ -1888,7 +1890,7 @@ void SwTxtNode::CountWords( SwDocStat& rStat, rStat.nAsianWord += GetParaNumberOfAsianWords(); rStat.nChar += GetParaNumberOfChars(); rStat.nCharExcludingSpaces += GetParaNumberOfCharsExcludingSpaces(); - return; + return false; } // ConversionMap to expand fields, remove invisible and redline deleted text for scanner @@ -1902,7 +1904,7 @@ void SwTxtNode::CountWords( SwDocStat& rStat, if (aExpandText.isEmpty() && !bCountNumbering) { OSL_ENSURE(aExpandText.getLength() >= 0, "Node text expansion error: length < 0." ); - return; + return false; } //do the count @@ -1990,6 +1992,8 @@ void SwTxtNode::CountWords( SwDocStat& rStat, rStat.nAsianWord += nTmpAsianWords; rStat.nChar += nTmpChars; rStat.nCharExcludingSpaces += nTmpCharsExcludingSpaces; + + return true; } // diff --git a/sw/source/ui/dialog/wordcountdialog.cxx b/sw/source/ui/dialog/wordcountdialog.cxx index 5a43642..e2efe0a 100644 --- a/sw/source/ui/dialog/wordcountdialog.cxx +++ b/sw/source/ui/dialog/wordcountdialog.cxx @@ -32,7 +32,7 @@ #include <vcl/msgbox.hxx> IMPL_LINK_NOARG(SwWordCountFloatDlg, CloseHdl) -{ +{ SfxViewFrame* pVFrame = ::GetActiveView()->GetViewFrame(); if (pVFrame != NULL) { diff --git a/sw/source/ui/inc/view.hxx b/sw/source/ui/inc/view.hxx index ff93acf..c1be2bb 100644 --- a/sw/source/ui/inc/view.hxx +++ b/sw/source/ui/inc/view.hxx @@ -355,7 +355,9 @@ class SW_DLLPUBLIC SwView: public SfxViewShell SW_DLLPRIVATE virtual void Move(); - SW_DLLPRIVATE sal_Bool InsertGraphicDlg( SfxRequest& ); + SW_DLLPRIVATE sal_Bool InsertGraphicDlg( SfxRequest& ); + + SW_DLLPRIVATE void DocumentStatsChanged(); protected: @@ -654,6 +656,8 @@ public: // exhibition hack (MA,MBA) void SelectShellForDrop(); + + void UpdateDocStats(); }; // ----------------- inline Methoden ---------------------- diff --git a/sw/source/ui/uiview/view.cxx b/sw/source/ui/uiview/view.cxx index 44fa043..df77bd1 100644 --- a/sw/source/ui/uiview/view.cxx +++ b/sw/source/ui/uiview/view.cxx @@ -1048,6 +1048,7 @@ SwView::~SwView() bInDtor = sal_True; pEditWin->Hide(); // damit kein Paint Aerger machen kann! // An der SwDocShell den Pointer auf die View ruecksetzen + SwDocShell* pDocSh = GetDocShell(); if( pDocSh && pDocSh->GetView() == this ) pDocSh->SetView( 0 ); diff --git a/sw/source/ui/uiview/view2.cxx b/sw/source/ui/uiview/view2.cxx index a1ada624..61b9440 100644 --- a/sw/source/ui/uiview/view2.cxx +++ b/sw/source/ui/uiview/view2.cxx @@ -1203,6 +1203,13 @@ void SwView::UpdatePageNums(sal_uInt16 nPhyNum, sal_uInt16 nVirtNum, const Strin rBnd.Update( FN_STAT_PAGE ); } +void SwView::UpdateDocStats() +{ + SfxBindings &rBnd = GetViewFrame()->GetBindings(); + rBnd.Invalidate( FN_STAT_WORDCOUNT ); + rBnd.Update( FN_STAT_WORDCOUNT ); +} + /*-------------------------------------------------------------------- Beschreibung: Status der Stauszeile --------------------------------------------------------------------*/ @@ -1241,7 +1248,7 @@ void SwView::StateStatusLine(SfxItemSet &rSet) SwDocStat documentStats; { rShell.CountWords(selectionStats); - documentStats = rShell.GetDoc()->GetUpdatedDocStat(); + documentStats = rShell.GetDoc()->GetUpdatedDocStat( true /* complete-async */ ); } const sal_uInt32 stringId = selectionStats.nWord? STR_STATUSBAR_WORDCOUNT : STR_STATUSBAR_WORDCOUNT_NO_SELECTION;
_______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits