diff options
Diffstat (limited to 'sw/source/uibase/misc')
-rw-r--r-- | sw/source/uibase/misc/glosdoc.cxx | 671 | ||||
-rw-r--r-- | sw/source/uibase/misc/glshell.cxx | 276 | ||||
-rw-r--r-- | sw/source/uibase/misc/numberingtypelistbox.cxx | 164 | ||||
-rw-r--r-- | sw/source/uibase/misc/redlndlg.cxx | 1206 | ||||
-rw-r--r-- | sw/source/uibase/misc/redlndlg.hrc | 28 | ||||
-rw-r--r-- | sw/source/uibase/misc/redlndlg.src | 85 | ||||
-rw-r--r-- | sw/source/uibase/misc/swruler.cxx | 302 |
7 files changed, 2732 insertions, 0 deletions
diff --git a/sw/source/uibase/misc/glosdoc.cxx b/sw/source/uibase/misc/glosdoc.cxx new file mode 100644 index 000000000000..55489082adea --- /dev/null +++ b/sw/source/uibase/misc/glosdoc.cxx @@ -0,0 +1,671 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <algorithm> + +#include <com/sun/star/container/XNamed.hpp> + +#include <unotools/transliterationwrapper.hxx> + +#ifndef __RSC //autogen +#include <tools/errinf.hxx> +#endif +#include <osl/diagnose.h> +#include <svl/urihelper.hxx> +#include <svl/fstathelper.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/tempfile.hxx> +#include <comphelper/string.hxx> +#include <swtypes.hxx> +#include <uitool.hxx> +#include <glosdoc.hxx> +#include <shellio.hxx> +#include <swunohelper.hxx> + +#include <unoatxt.hxx> +#include <swerror.h> +#include <globals.hrc> + +#include <boost/scoped_ptr.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace +{ + +inline OUString lcl_FullPathName(const OUString& sPath, const OUString& sName) +{ + return sPath + "/" + sName + SwGlossaries::GetExtension(); +} + +OUString lcl_CheckFileName( const OUString& rNewFilePath, + const OUString& rNewGroupName ) +{ + const sal_Int32 nLen = rNewGroupName.getLength(); + OUStringBuffer aBuf(nLen); + //group name should contain only A-Z and a-z and spaces + for( sal_Int32 i=0; i < nLen; ++i ) + { + const sal_Unicode cChar = rNewGroupName[i]; + if (comphelper::string::isalnumAscii(cChar) || + cChar == '_' || cChar == 0x20) + { + aBuf.append(cChar); + } + } + + const OUString sRet = aBuf.makeStringAndClear().trim(); + if ( !sRet.isEmpty() ) + { + if (!FStatHelper::IsDocument( lcl_FullPathName(rNewFilePath, sRet) )) + return sRet; + } + + OUString rSG = SwGlossaries::GetExtension(); + //generate generic name + utl::TempFile aTemp("group", true, &rSG, &rNewFilePath); + aTemp.EnableKillingFile(); + + INetURLObject aTempURL( aTemp.GetURL() ); + return aTempURL.GetBase(); +} + +} + +// supplies the default group's name +OUString SwGlossaries::GetDefName() +{ + return OUString("standard"); + +} + +// supplies the number of text block groups +sal_uInt16 SwGlossaries::GetGroupCnt() +{ + return static_cast<sal_uInt16>(GetNameList().size()); +} + +// supplies the group's name +bool SwGlossaries::FindGroupName(OUString& rGroup) +{ + // if the group name doesn't contain a path, a suitable group entry + // can the searched for here; + sal_uInt16 nCount = GetGroupCnt(); + sal_uInt16 i; + for(i= 0; i < nCount; i++) + { + const OUString sTemp(GetGroupName(i)); + if (rGroup==sTemp.getToken(0, GLOS_DELIM)) + { + rGroup = sTemp; + return true; + } + } + // you can search two times because for more directories the case sensitive + // name could occur multiple times + const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); + for(i = 0; i < nCount; i++) + { + const OUString sTemp( GetGroupName( i )); + sal_uInt16 nPath = (sal_uInt16)sTemp.getToken(1, GLOS_DELIM).toInt32(); + + if (!SWUnoHelper::UCB_IsCaseSensitiveFileName( m_PathArr[nPath] ) + && rSCmp.isEqual( rGroup, sTemp.getToken( 0, GLOS_DELIM) ) ) + { + rGroup = sTemp; + return true; + } + } + return false; +} + +OUString SwGlossaries::GetGroupName(sal_uInt16 nGroupId) +{ + OSL_ENSURE(static_cast<size_t>(nGroupId) < m_GlosArr.size(), + "SwGlossaries::GetGroupName: index out of bounds"); + return m_GlosArr[nGroupId]; +} + +OUString SwGlossaries::GetGroupTitle( const OUString& rGroupName ) +{ + OUString sRet; + OUString sGroup(rGroupName); + if (sGroup.indexOf(GLOS_DELIM)<0) + FindGroupName(sGroup); + SwTextBlocks* pGroup = GetGroupDoc(sGroup, false); + if(pGroup) + { + sRet = pGroup->GetName(); + PutGroupDoc( pGroup ); + } + return sRet; +} + +// supplies the group rName's text block document +SwTextBlocks* SwGlossaries::GetGroupDoc(const OUString &rName, + bool bCreate) +{ + // insert to the list of text blocks if applicable + if(bCreate && !m_GlosArr.empty()) + { + std::vector<OUString>::const_iterator it(m_GlosArr.begin()); + for (; it != m_GlosArr.end(); ++it) + { + if (*it == rName) + break; + } + if (it == m_GlosArr.end()) + { // block not in the list + m_GlosArr.push_back(rName); + } + } + return GetGlosDoc( rName, bCreate ); +} + +// delete a text block +void SwGlossaries::PutGroupDoc(SwTextBlocks *pBlock) { + delete pBlock; +} + +// Creates a new document with the group name. temporarly also created as file +// so that groups remain there later (without access). +bool SwGlossaries::NewGroupDoc(OUString& rGroupName, const OUString& rTitle) +{ + const OUString sNewPath(rGroupName.getToken(1, GLOS_DELIM)); + sal_uInt16 nNewPath = (sal_uInt16)sNewPath.toInt32(); + if (static_cast<size_t>(nNewPath) >= m_PathArr.size()) + return false; + const OUString sNewFilePath(m_PathArr[nNewPath]); + const OUString sNewGroup = lcl_CheckFileName(sNewFilePath, rGroupName.getToken(0, GLOS_DELIM)) + + OUString(GLOS_DELIM) + sNewPath; + SwTextBlocks *pBlock = GetGlosDoc( sNewGroup ); + if(pBlock) + { + GetNameList().push_back(sNewGroup); + pBlock->SetName(rTitle); + PutGroupDoc(pBlock); + rGroupName = sNewGroup; + return true; + } + return false; +} + +bool SwGlossaries::RenameGroupDoc( + const OUString& rOldGroup, OUString& rNewGroup, const OUString& rNewTitle ) +{ + sal_uInt16 nOldPath = (sal_uInt16)rOldGroup.getToken(1, GLOS_DELIM).toInt32(); + if (static_cast<size_t>(nOldPath) >= m_PathArr.size()) + return false; + + const OUString sOldFileURL = + lcl_FullPathName(m_PathArr[nOldPath], rOldGroup.getToken(0, GLOS_DELIM)); + + if (!FStatHelper::IsDocument( sOldFileURL )) + { + OSL_FAIL("group doesn't exist!"); + return false; + } + + sal_uInt16 nNewPath = (sal_uInt16)rNewGroup.getToken(1, GLOS_DELIM).toInt32(); + if (static_cast<size_t>(nNewPath) >= m_PathArr.size()) + return false; + + const OUString sNewFileName = lcl_CheckFileName(m_PathArr[nNewPath], + rNewGroup.getToken(0, GLOS_DELIM)); + const OUString sNewFileURL = lcl_FullPathName(m_PathArr[nNewPath], sNewFileName); + + if (FStatHelper::IsDocument( sNewFileURL )) + { + OSL_FAIL("group already exists!"); + return false; + } + + if (!SWUnoHelper::UCB_CopyFile(sOldFileURL, sNewFileURL, true )) + return false; + + RemoveFileFromList( rOldGroup ); + + rNewGroup = sNewFileName + OUString(GLOS_DELIM) + OUString::number(nNewPath); + if (m_GlosArr.empty()) + { + GetNameList(); + } + else + { + m_GlosArr.push_back(rNewGroup); + } + + SwTextBlocks* pNewBlock = new SwTextBlocks( sNewFileURL ); + pNewBlock->SetName(rNewTitle); + delete pNewBlock; + + return true; +} + +// Deletes a text block group +bool SwGlossaries::DelGroupDoc(const OUString &rName) +{ + sal_uInt16 nPath = (sal_uInt16)rName.getToken(1, GLOS_DELIM).toInt32(); + if (static_cast<size_t>(nPath) >= m_PathArr.size()) + return false; + const OUString sBaseName(rName.getToken(0, GLOS_DELIM)); + const OUString sFileURL = lcl_FullPathName(m_PathArr[nPath], sBaseName); + const OUString aName = sBaseName + OUString(GLOS_DELIM) + OUString::number(nPath); + // Even if the file doesn't exist it has to be deleted from + // the list of text block regions + // no && because of CFfront + bool bRemoved = SWUnoHelper::UCB_DeleteFile( sFileURL ); + OSL_ENSURE(bRemoved, "file has not been removed"); + RemoveFileFromList( aName ); + return bRemoved; +} + +SwGlossaries::~SwGlossaries() +{ + InvalidateUNOOjects(); +} + +// read a block document +SwTextBlocks* SwGlossaries::GetGlosDoc( const OUString &rName, bool bCreate ) const +{ + sal_uInt16 nPath = (sal_uInt16)rName.getToken(1, GLOS_DELIM).toInt32(); + SwTextBlocks *pTmp = 0; + if (static_cast<size_t>(nPath) < m_PathArr.size()) + { + const OUString sFileURL = + lcl_FullPathName(m_PathArr[nPath], rName.getToken(0, GLOS_DELIM)); + + bool bExist = false; + if(!bCreate) + bExist = FStatHelper::IsDocument( sFileURL ); + + if (bCreate || bExist) + { + pTmp = new SwTextBlocks( sFileURL ); + bool bOk = true; + if( pTmp->GetError() ) + { + ErrorHandler::HandleError( pTmp->GetError() ); + bOk = !IsError( pTmp->GetError() ); + } + + if( bOk && pTmp->GetName().isEmpty() ) + pTmp->SetName( rName ); + } + } + + return pTmp; +} + +// access to the list of names; read in if applicable +std::vector<OUString> & SwGlossaries::GetNameList() +{ + if (m_GlosArr.empty()) + { + const OUString sExt( SwGlossaries::GetExtension() ); + for (size_t i = 0; i < m_PathArr.size(); ++i) + { + std::vector<OUString> aFiles; + + SWUnoHelper::UCB_GetFileListOfFolder(m_PathArr[i], aFiles, &sExt); + for( std::vector<OUString>::const_iterator filesIt(aFiles.begin()); + filesIt != aFiles.end(); ++filesIt) + { + const OUString aTitle = *filesIt; + const OUString sName( aTitle.copy( 0, aTitle.getLength() - sExt.getLength() ) + + OUString(GLOS_DELIM) + OUString::number( static_cast<sal_Int16>(i) )); + m_GlosArr.push_back(sName); + } + } + if (m_GlosArr.empty()) + { + // the standard block is inside of the path's first part + m_GlosArr.push_back( SwGlossaries::GetDefName() + OUString(GLOS_DELIM) + "0" ); + } + } + return m_GlosArr; +} + +SwGlossaries::SwGlossaries() +{ + UpdateGlosPath(true); +} + +// set new path and recreate internal array +rtl::OUString lcl_makePath(const std::vector<rtl::OUString>& rPaths) +{ + std::vector<rtl::OUString>::const_iterator aIt(rPaths.begin()); + const std::vector<rtl::OUString>::const_iterator aEnd(rPaths.end()); + rtl::OUStringBuffer aPath(*aIt); + for (++aIt; aIt != aEnd; ++aIt) + { + aPath.append(SVT_SEARCHPATH_DELIMITER); + const INetURLObject aTemp(*aIt); + aPath.append(aTemp.GetFull()); + } + return aPath.getStr(); +} + +void SwGlossaries::UpdateGlosPath(bool bFull) +{ + SvtPathOptions aPathOpt; + OUString aNewPath( aPathOpt.GetAutoTextPath() ); + bool bPathChanged = m_aPath != aNewPath; + if (bFull || bPathChanged) + { + m_aPath = aNewPath; + + m_PathArr.clear(); + + std::vector<OUString> aDirArr; + std::vector<rtl::OUString> aInvalidPaths; + if (!m_aPath.isEmpty()) + { + sal_Int32 nIndex = 0; + do + { + const OUString sPth = URIHelper::SmartRel2Abs( + INetURLObject(), + m_aPath.getToken(0, SVT_SEARCHPATH_DELIMITER, nIndex), + URIHelper::GetMaybeFileHdl()); + if (aDirArr.size() && + std::find(aDirArr.begin(), aDirArr.end(), sPth) != aDirArr.end()) + { + continue; + } + aDirArr.push_back(sPth); + if( !FStatHelper::IsFolder( sPth ) ) + aInvalidPaths.push_back(sPth); + else + m_PathArr.push_back(sPth); + } + while (nIndex>=0); + } + + if (m_aPath.isEmpty() || !aInvalidPaths.empty()) + { + std::sort(aInvalidPaths.begin(), aInvalidPaths.end()); + aInvalidPaths.erase(std::unique(aInvalidPaths.begin(), aInvalidPaths.end()), aInvalidPaths.end()); + if (bPathChanged || (m_aInvalidPaths != aInvalidPaths)) + { + m_aInvalidPaths = aInvalidPaths; + // wrong path, that means AutoText directory doesn't exist + + ErrorHandler::HandleError( *new StringErrorInfo( + ERR_AUTOPATH_ERROR, lcl_makePath(m_aInvalidPaths), + ERRCODE_BUTTON_OK | ERRCODE_MSG_ERROR )); + m_bError = true; + } + else + m_bError = false; + } + else + m_bError = false; + + if (!m_GlosArr.empty()) + { + m_GlosArr.clear(); + GetNameList(); + } + } +} + +void SwGlossaries::ShowError() +{ + sal_uInt32 nPathError = *new StringErrorInfo(ERR_AUTOPATH_ERROR, + lcl_makePath(m_aInvalidPaths), ERRCODE_BUTTON_OK ); + ErrorHandler::HandleError( nPathError ); +} + +OUString SwGlossaries::GetExtension() +{ + return OUString(".bau"); +} + +void SwGlossaries::RemoveFileFromList( const OUString& rGroup ) +{ + if (!m_GlosArr.empty()) + { + for (std::vector<OUString>::iterator it(m_GlosArr.begin()); + it != m_GlosArr.end(); ++it) + { + if (*it == rGroup) + { + OUString aUName = rGroup; + { + // tell the UNO AutoTextGroup object that it's not valid anymore + for ( UnoAutoTextGroups::iterator aLoop = m_aGlossaryGroups.begin(); + aLoop != m_aGlossaryGroups.end(); + ++aLoop + ) + { + Reference< container::XNamed > xNamed( aLoop->get(), UNO_QUERY ); + if ( xNamed.is() && ( xNamed->getName() == aUName ) ) + { + static_cast< SwXAutoTextGroup* >( xNamed.get() )->Invalidate(); + // note that this static_cast works because we know that the array only + // contains SwXAutoTextGroup implementation + m_aGlossaryGroups.erase( aLoop ); + break; + } + } + } + + { + // tell all our UNO AutoTextEntry objects that they're not valid anymore + for ( UnoAutoTextEntries::iterator aLoop = m_aGlossaryEntries.begin(); + aLoop != m_aGlossaryEntries.end(); + ) + { + Reference< lang::XUnoTunnel > xEntryTunnel( aLoop->get(), UNO_QUERY ); + + SwXAutoTextEntry* pEntry = NULL; + if ( xEntryTunnel.is() ) + pEntry = reinterpret_cast< SwXAutoTextEntry* >( + xEntryTunnel->getSomething( SwXAutoTextEntry::getUnoTunnelId() ) ); + + if ( pEntry && ( pEntry->GetGroupName() == rGroup ) ) + { + pEntry->Invalidate(); + aLoop = m_aGlossaryEntries.erase( aLoop ); + } + else + ++aLoop; + } + } + + m_GlosArr.erase(it); + break; + } + } + } +} + +OUString SwGlossaries::GetCompleteGroupName( const OUString& rGroupName ) +{ + const sal_uInt16 nCount = GetGroupCnt(); + // when the group name was created internally the path is here as well + sal_Int32 nIndex = 0; + const OUString sGroupName(rGroupName.getToken(0, GLOS_DELIM, nIndex)); + const bool bPathLen = !rGroupName.getToken(0, GLOS_DELIM, nIndex).isEmpty(); + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + const OUString sGrpName = GetGroupName(i); + if (bPathLen) + { + if (rGroupName == sGrpName) + return sGrpName; + } + else + { + if (sGroupName == sGrpName.getToken(0, GLOS_DELIM)) + return sGrpName; + } + } + return OUString(); +} + +void SwGlossaries::InvalidateUNOOjects() +{ + // invalidate all the AutoTextGroup-objects + for ( UnoAutoTextGroups::iterator aGroupLoop = m_aGlossaryGroups.begin(); + aGroupLoop != m_aGlossaryGroups.end(); + ++aGroupLoop + ) + { + Reference< text::XAutoTextGroup > xGroup( aGroupLoop->get(), UNO_QUERY ); + if ( xGroup.is() ) + static_cast< SwXAutoTextGroup* >( xGroup.get() )->Invalidate(); + } + UnoAutoTextGroups aTmpg = UnoAutoTextGroups(); + m_aGlossaryGroups.swap( aTmpg ); + + // invalidate all the AutoTextEntry-objects + for ( UnoAutoTextEntries::const_iterator aEntryLoop = m_aGlossaryEntries.begin(); + aEntryLoop != m_aGlossaryEntries.end(); + ++aEntryLoop + ) + { + Reference< lang::XUnoTunnel > xEntryTunnel( aEntryLoop->get(), UNO_QUERY ); + SwXAutoTextEntry* pEntry = NULL; + if ( xEntryTunnel.is() ) + pEntry = reinterpret_cast< SwXAutoTextEntry* >( + xEntryTunnel->getSomething( SwXAutoTextEntry::getUnoTunnelId() ) ); + + if ( pEntry ) + pEntry->Invalidate(); + } + UnoAutoTextEntries aTmpe = UnoAutoTextEntries(); + m_aGlossaryEntries.swap( aTmpe ); +} + +Reference< text::XAutoTextGroup > SwGlossaries::GetAutoTextGroup( const OUString& _rGroupName, bool _bCreate ) +{ + // first, find the name with path-extension + const OUString sCompleteGroupName = GetCompleteGroupName( _rGroupName ); + + Reference< text::XAutoTextGroup > xGroup; + + // look up the group in the cache + UnoAutoTextGroups::iterator aSearch = m_aGlossaryGroups.begin(); + for ( ; aSearch != m_aGlossaryGroups.end(); ) + { + Reference< lang::XUnoTunnel > xGroupTunnel( aSearch->get(), UNO_QUERY ); + + SwXAutoTextGroup* pSwGroup = 0; + if ( xGroupTunnel.is() ) + pSwGroup = reinterpret_cast< SwXAutoTextGroup* >( xGroupTunnel->getSomething( SwXAutoTextGroup::getUnoTunnelId() ) ); + + if ( !pSwGroup ) + { + // the object is dead in the meantime -> remove from cache + aSearch = m_aGlossaryGroups.erase( aSearch ); + continue; + } + + if ( _rGroupName == pSwGroup->getName() ) + { // the group is already cached + if ( !sCompleteGroupName.isEmpty() ) + { // the group still exists -> return it + xGroup = pSwGroup; + break; + } + else + { + // this group does not exist (anymore) -> release the cached UNO object for it + aSearch = m_aGlossaryGroups.erase( aSearch ); + // so it won't be created below + _bCreate = false; + break; + } + } + + ++aSearch; + } + + if ( !xGroup.is() && _bCreate ) + { + xGroup = new SwXAutoTextGroup( sCompleteGroupName, this ); + // cache it + m_aGlossaryGroups.push_back( AutoTextGroupRef( xGroup ) ); + } + + return xGroup; +} + +Reference< text::XAutoTextEntry > SwGlossaries::GetAutoTextEntry( + const OUString& rCompleteGroupName, + const OUString& rGroupName, + const OUString& rEntryName, + bool _bCreate ) +{ + //standard must be created + bool bCreate = ( rCompleteGroupName == GetDefName() ); + boost::scoped_ptr< SwTextBlocks > pGlosGroup( GetGroupDoc( rCompleteGroupName, bCreate ) ); + + if ( pGlosGroup.get() && !pGlosGroup->GetError() ) + { + sal_uInt16 nIdx = pGlosGroup->GetIndex( rEntryName ); + if ( USHRT_MAX == nIdx ) + throw container::NoSuchElementException(); + } + else + throw lang::WrappedTargetException(); + + Reference< text::XAutoTextEntry > xReturn; + + UnoAutoTextEntries::iterator aSearch( m_aGlossaryEntries.begin() ); + for ( ; aSearch != m_aGlossaryEntries.end(); ) + { + Reference< lang::XUnoTunnel > xEntryTunnel( aSearch->get(), UNO_QUERY ); + + SwXAutoTextEntry* pEntry = NULL; + if ( xEntryTunnel.is() ) + pEntry = reinterpret_cast< SwXAutoTextEntry* >( xEntryTunnel->getSomething( SwXAutoTextEntry::getUnoTunnelId() ) ); + else + { + // the object is dead in the meantime -> remove from cache + aSearch = m_aGlossaryEntries.erase( aSearch ); + continue; + } + + if ( pEntry + && pEntry->GetGroupName() == rGroupName + && pEntry->GetEntryName() == rEntryName + ) + { + xReturn = pEntry; + break; + } + + ++aSearch; + } + + if ( !xReturn.is() && _bCreate ) + { + xReturn = new SwXAutoTextEntry( this, rGroupName, rEntryName ); + // cache it + m_aGlossaryEntries.push_back( AutoTextEntryRef( xReturn ) ); + } + + return xReturn; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/misc/glshell.cxx b/sw/source/uibase/misc/glshell.cxx new file mode 100644 index 000000000000..21fce360171c --- /dev/null +++ b/sw/source/uibase/misc/glshell.cxx @@ -0,0 +1,276 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/frame/XTitle.hpp> + +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxsids.hrc> +#include <svl/srchitem.hxx> +#include <svl/macitem.hxx> +#include <gloshdl.hxx> + +#include <editeng/acorrcfg.hxx> +#include <sfx2/app.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <uitool.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <glshell.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <glosdoc.hxx> +#include <shellio.hxx> +#include <initui.hxx> +#include <cmdid.h> +#include <swerror.h> +#include <misc.hrc> + +#define SwWebGlosDocShell +#define SwGlosDocShell + +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +using namespace ::com::sun::star; + +SFX_IMPL_INTERFACE(SwGlosDocShell, SwDocShell, SW_RES(0) ) + +void SwGlosDocShell::InitInterface_Impl() +{ +} + +SFX_IMPL_INTERFACE(SwWebGlosDocShell, SwWebDocShell, SW_RES(0) ) + +void SwWebGlosDocShell::InitInterface_Impl() +{ +} + +TYPEINIT1( SwGlosDocShell, SwDocShell ); +TYPEINIT1( SwWebGlosDocShell, SwWebDocShell ); + +static void lcl_Execute( SwDocShell& rSh, SfxRequest& rReq ) +{ + if ( rReq.GetSlot() == SID_SAVEDOC ) + { + if( !rSh.HasName() ) + { + rReq.SetReturnValue( SfxBoolItem( 0, rSh.Save() ) ); + } + else + { + const SfxBoolItem* pRes = ( const SfxBoolItem* ) + rSh.ExecuteSlot( rReq, + rSh.SfxObjectShell::GetInterface() ); + if( pRes->GetValue() ) + rSh.GetDoc()->ResetModified(); + } + } +} + +static void lcl_GetState( SwDocShell& rSh, SfxItemSet& rSet ) +{ + if( SFX_ITEM_AVAILABLE >= rSet.GetItemState( SID_SAVEDOC, false )) + { + if( !rSh.GetDoc()->IsModified() ) + rSet.DisableItem( SID_SAVEDOC ); + else + rSet.Put( SfxStringItem( SID_SAVEDOC, SW_RESSTR(STR_SAVE_GLOSSARY))); + } +} + +static bool lcl_Save( SwWrtShell& rSh, const OUString& rGroupName, + const OUString& rShortNm, const OUString& rLongNm ) +{ + const SvxAutoCorrCfg& rCfg = SvxAutoCorrCfg::Get(); + SwTextBlocks * pBlock = ::GetGlossaries()->GetGroupDoc( rGroupName ); + + SvxMacro aStart(aEmptyOUStr, aEmptyOUStr); + SvxMacro aEnd(aEmptyOUStr, aEmptyOUStr); + SwGlossaryHdl* pGlosHdl; + + pGlosHdl = rSh.GetView().GetGlosHdl(); + pGlosHdl->GetMacros( rShortNm, aStart, aEnd, pBlock ); + + sal_uInt16 nRet = rSh.SaveGlossaryDoc( *pBlock, rLongNm, rShortNm, + rCfg.IsSaveRelFile(), + pBlock->IsOnlyTextBlock( rShortNm ) ); + + if(aStart.HasMacro() || aEnd.HasMacro() ) + { + SvxMacro* pStart = aStart.HasMacro() ? &aStart : 0; + SvxMacro* pEnd = aEnd.HasMacro() ? &aEnd : 0; + pGlosHdl->SetMacros( rShortNm, pStart, pEnd, pBlock ); + } + + rSh.EnterStdMode(); + if( USHRT_MAX != nRet ) + rSh.ResetModified(); + delete pBlock; + return nRet != USHRT_MAX; +} + +SwGlosDocShell::SwGlosDocShell(bool bNewShow) + : SwDocShell( (bNewShow) + ? SFX_CREATE_MODE_STANDARD : SFX_CREATE_MODE_INTERNAL ) +{ + SetHelpId(SW_GLOSDOCSHELL); +} + +SwGlosDocShell::~SwGlosDocShell( ) +{ +} + +void SwGlosDocShell::Execute( SfxRequest& rReq ) +{ + ::lcl_Execute( *this, rReq ); +} + +void SwGlosDocShell::GetState( SfxItemSet& rSet ) +{ + ::lcl_GetState( *this, rSet ); +} + +bool SwGlosDocShell::Save() +{ + // In case of an API object which holds this document, it is possible that the WrtShell is already + // dead. For instance, if the doc is modified via this API object, and then, upon office shutdown, + // the document's view is closed (by the SFX framework) _before_ the API object is release and + // tries to save the doc, again. + // 96380 - 2002-03-03 - fs@openoffice.org + if ( GetWrtShell() ) + return ::lcl_Save( *GetWrtShell(), aGroupName, aShortName, aLongName ); + else + { + SetModified( false ); + return false; + } +} + +SwWebGlosDocShell::SwWebGlosDocShell() + : SwWebDocShell( SFX_CREATE_MODE_STANDARD ) +{ + SetHelpId(SW_WEBGLOSDOCSHELL); +} + +SwWebGlosDocShell::~SwWebGlosDocShell( ) +{ +} + +void SwWebGlosDocShell::Execute( SfxRequest& rReq ) +{ + ::lcl_Execute( *this, rReq ); +} + +void SwWebGlosDocShell::GetState( SfxItemSet& rSet ) +{ + ::lcl_GetState( *this, rSet ); +} + +bool SwWebGlosDocShell::Save() +{ + // same comment as in SwGlosDocShell::Save - see there + if ( GetWrtShell() ) + return ::lcl_Save( *GetWrtShell(), aGroupName, aShortName, aLongName ); + else + { + SetModified( false ); + return false; + } +} + +SwDocShellRef SwGlossaries::EditGroupDoc( const OUString& rGroup, const OUString& rShortName, bool bShow ) +{ + SwDocShellRef xDocSh; + + SwTextBlocks* pGroup = GetGroupDoc( rGroup ); + if( pGroup && pGroup->GetCount() ) + { + // query which view is registered. In WebWriter there is no normal view + sal_uInt16 nViewId = 0 != &SwView::Factory() ? 2 : 6; + const OUString sLongName = pGroup->GetLongName(pGroup->GetIndex( rShortName )); + + if( 6 == nViewId ) + { + SwWebGlosDocShell* pDocSh = new SwWebGlosDocShell(); + xDocSh = pDocSh; + pDocSh->DoInitNew( 0 ); + pDocSh->SetLongName( sLongName ); + pDocSh->SetShortName( rShortName); + pDocSh->SetGroupName( rGroup ); + } + else + { + SwGlosDocShell* pDocSh = new SwGlosDocShell(bShow); + xDocSh = pDocSh; + pDocSh->DoInitNew( 0 ); + pDocSh->SetLongName( sLongName ); + pDocSh->SetShortName( rShortName ); + pDocSh->SetGroupName( rGroup ); + } + + // set document title + SfxViewFrame* pFrame = bShow ? SfxViewFrame::LoadDocument( *xDocSh, nViewId ) : SfxViewFrame::LoadHiddenDocument( *xDocSh, nViewId ); + const OUString aDocTitle(SW_RESSTR( STR_GLOSSARY ) + " " + sLongName); + + bool const bDoesUndo = + xDocSh->GetDoc()->GetIDocumentUndoRedo().DoesUndo(); + xDocSh->GetDoc()->GetIDocumentUndoRedo().DoUndo( false ); + + xDocSh->GetWrtShell()->InsertGlossary( *pGroup, rShortName ); + if( !xDocSh->GetDoc()->getPrinter( false ) ) + { + // we create a default SfxPrinter. + // ItemSet is deleted by Sfx! + SfxItemSet *pSet = new SfxItemSet( xDocSh->GetDoc()->GetAttrPool(), + FN_PARAM_ADDPRINTER, FN_PARAM_ADDPRINTER, + SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN, + SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC, + 0 ); + SfxPrinter* pPrinter = new SfxPrinter( pSet ); + + // and append it to the document. + xDocSh->GetDoc()->setPrinter( pPrinter, true, true ); + } + + xDocSh->SetTitle( aDocTitle ); + try + { + // set the UI-title + uno::Reference< frame::XTitle > xTitle( xDocSh->GetModel(), uno::UNO_QUERY_THROW ); + xTitle->setTitle( aDocTitle ); + } + catch (const uno::Exception&) + { + } + + xDocSh->GetDoc()->GetIDocumentUndoRedo().DoUndo( bDoesUndo ); + xDocSh->GetDoc()->ResetModified(); + if ( bShow ) + pFrame->GetFrame().Appear(); + + delete pGroup; + } + return xDocSh; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/misc/numberingtypelistbox.cxx b/sw/source/uibase/misc/numberingtypelistbox.cxx new file mode 100644 index 000000000000..ab21d35764a4 --- /dev/null +++ b/sw/source/uibase/misc/numberingtypelistbox.cxx @@ -0,0 +1,164 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <numberingtypelistbox.hxx> +#include <misc.hrc> +#include <cnttab.hxx> +#include <com/sun/star/style/NumberingType.hpp> +#include <com/sun/star/text/DefaultNumberingProvider.hpp> +#include <com/sun/star/text/XDefaultNumberingProvider.hpp> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/text/XNumberingTypeInfo.hpp> + +#include <unomid.h> + +using namespace com::sun::star; + +struct SwNumberingTypeListBox_Impl +{ + uno::Reference<text::XNumberingTypeInfo> xInfo; +}; + +SwNumberingTypeListBox::SwNumberingTypeListBox( Window* pWin, WinBits nStyle ) : + ListBox(pWin, nStyle), + pImpl(new SwNumberingTypeListBox_Impl) +{ + uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference<text::XDefaultNumberingProvider> xDefNum = text::DefaultNumberingProvider::create(xContext); + + pImpl->xInfo = uno::Reference<text::XNumberingTypeInfo>(xDefNum, uno::UNO_QUERY); +} + +bool SwNumberingTypeListBox::set_property(const OString &rKey, const OString &rValue) +{ + if (rKey == "type") + Reload(rValue.toInt32()); + else + return ListBox::set_property(rKey, rValue); + return true; +} + +extern "C" SAL_DLLPUBLIC_EXPORT Window* SAL_CALL makeSwNumberingTypeListBox(Window *pParent, VclBuilder::stringmap &) +{ + SwNumberingTypeListBox *pListBox = new SwNumberingTypeListBox(pParent, WB_LEFT|WB_DROPDOWN|WB_VCENTER|WB_3DLOOK|WB_TABSTOP); + pListBox->EnableAutoSize(true); + return pListBox; +} + +SwNumberingTypeListBox::~SwNumberingTypeListBox() +{ + delete pImpl; +} + +void SwNumberingTypeListBox::Reload(sal_uInt16 nTypeFlags) +{ + Clear(); + uno::Sequence<sal_Int16> aTypes; + const sal_Int16* pTypes = NULL; + if(0 != (nTypeFlags&INSERT_NUM_EXTENDED_TYPES) ) + { + if(pImpl->xInfo.is()) + { + aTypes = pImpl->xInfo->getSupportedNumberingTypes(); + pTypes = aTypes.getConstArray(); + } + } + SwOLENames aNames(SW_RES(STRRES_NUMTYPES)); + ResStringArray& rNames = aNames.GetNames(); + for(sal_uInt16 i = 0; i < rNames.Count(); i++) + { + sal_IntPtr nValue = rNames.GetValue(i); + bool bInsert = true; + sal_Int32 nPos = LISTBOX_APPEND; + switch(nValue) + { + case style::NumberingType::NUMBER_NONE: + bInsert = 0 != (nTypeFlags&INSERT_NUM_TYPE_NO_NUMBERING); + nPos = 0; + break; + case style::NumberingType::CHAR_SPECIAL: bInsert = 0 != (nTypeFlags&INSERT_NUM_TYPE_BULLET); break; + case style::NumberingType::PAGE_DESCRIPTOR:bInsert = 0 != (nTypeFlags&INSERT_NUM_TYPE_PAGE_STYLE_NUMBERING); break; + case style::NumberingType::BITMAP:bInsert = 0 != (nTypeFlags&INSERT_NUM_TYPE_BITMAP ); break; + default: + if (nValue > style::NumberingType::CHARS_LOWER_LETTER_N) + { + // Insert only if offered by i18n framework per configuration. + bInsert = false; + if (pTypes) + { + for(sal_Int32 nType = 0; nType < aTypes.getLength(); nType++) + { + if (pTypes[nType] == nValue) + { + bInsert = true; + break; // for + } + } + } + } + } + if(bInsert) + { + sal_Int32 nEntry = InsertEntry(rNames.GetString(i), nPos); + SetEntryData( nEntry, (void*)nValue ); + } + } + if(0 != (nTypeFlags&INSERT_NUM_EXTENDED_TYPES) ) + { + if(pTypes) + { + for(sal_Int32 nType = 0; nType < aTypes.getLength(); nType++) + { + sal_Int16 nCurrent = pTypes[nType]; + if(nCurrent > style::NumberingType::CHARS_LOWER_LETTER_N) + { + if(LISTBOX_ENTRY_NOTFOUND == GetEntryPos((void*)(sal_uLong)nCurrent)) + { + OUString aIdent = pImpl->xInfo->getNumberingIdentifier( nCurrent ); + sal_Int32 nPos = InsertEntry(aIdent); + SetEntryData(nPos,(void*)(sal_uLong)nCurrent); + } + } + } + } + SelectEntryPos(0); + } +} + +sal_Int16 SwNumberingTypeListBox::GetSelectedNumberingType() +{ + sal_Int16 nRet = 0; + sal_Int32 nSelPos = GetSelectEntryPos(); + if(LISTBOX_ENTRY_NOTFOUND != nSelPos) + nRet = (sal_Int16)(sal_uLong)GetEntryData(nSelPos); +#if OSL_DEBUG_LEVEL > 0 + else + OSL_FAIL("SwNumberingTypeListBox not selected"); +#endif + return nRet; +} + +bool SwNumberingTypeListBox::SelectNumberingType(sal_Int16 nType) +{ + sal_Int32 nPos = GetEntryPos((void*)(sal_uLong)nType); + SelectEntryPos( nPos ); + return LISTBOX_ENTRY_NOTFOUND != nPos; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/misc/redlndlg.cxx b/sw/source/uibase/misc/redlndlg.cxx new file mode 100644 index 000000000000..53aebdcdcc6d --- /dev/null +++ b/sw/source/uibase/misc/redlndlg.cxx @@ -0,0 +1,1206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <redline.hxx> +#include <tools/datetime.hxx> +#include <vcl/msgbox.hxx> +#include <svl/eitem.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/ctredlin.hxx> +#include <svx/postattr.hxx> +#include <swtypes.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <swmodule.hxx> +#include <redlndlg.hxx> +#include <swwait.hxx> +#include <uitool.hxx> + +#include <helpid.h> +#include <cmdid.h> +#include <misc.hrc> +#include <redlndlg.hrc> +#include <shells.hrc> + +// -> #111827# +#include <comcore.hrc> +#include <swundo.hxx> +#include <SwRewriter.hxx> +// <- #111827# + +#include <vector> +#include <svx/svxdlg.hxx> +#include <svx/dialogs.hrc> + +#include <unomid.h> + +#include <docsh.hxx> + +#include <IDocumentRedlineAccess.hxx> + +SFX_IMPL_MODELESSDIALOG_WITHID( SwRedlineAcceptChild, FN_REDLINE_ACCEPT ) + +static sal_uInt16 nSortMode = 0xffff; +static bool bSortDir = true; + +SwRedlineAcceptChild::SwRedlineAcceptChild( Window* _pParent, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo ) : + SwChildWinWrapper( _pParent, nId ) +{ + pWindow = new SwModelessRedlineAcceptDlg( pBindings, this, _pParent); + + ((SwModelessRedlineAcceptDlg *)pWindow)->Initialize(pInfo); +} + +// newly initialise dialog after document switch +bool SwRedlineAcceptChild::ReInitDlg(SwDocShell *pDocSh) +{ + bool bRet; + + if ((bRet = SwChildWinWrapper::ReInitDlg(pDocSh))) // update immediately, doc switch! + ((SwModelessRedlineAcceptDlg*)GetWindow())->Activate(); + + return bRet; +} + +SwModelessRedlineAcceptDlg::SwModelessRedlineAcceptDlg( + SfxBindings* _pBindings, SwChildWinWrapper* pChild, Window *_pParent) + : SfxModelessDialog(_pBindings, pChild, _pParent, + "AcceptRejectChangesDialog", "svx/ui/acceptrejectchangesdialog.ui") + , pChildWin (pChild) +{ + pImplDlg = new SwRedlineAcceptDlg(this); +} + +void SwModelessRedlineAcceptDlg::Activate() +{ + SwView *pView = ::GetActiveView(); + + if (!pView) // can happen when switching to another app, when a Listbox in dialog + return; // had the focus previously (actually THs Bug) + + SwDocShell *pDocSh = pView->GetDocShell(); + + if (pChildWin->GetOldDocShell() != pDocSh) + { // doc-switch + SwWait aWait( *pDocSh, false ); + SwWrtShell* pSh = pView->GetWrtShellPtr(); + + pChildWin->SetOldDocShell(pDocSh); // avoid recursion (using modified-Hdl) + + bool bMod = pSh->IsModified(); + SfxBoolItem aShow(FN_REDLINE_SHOW, true); + pSh->GetView().GetViewFrame()->GetDispatcher()->Execute( + FN_REDLINE_SHOW, SFX_CALLMODE_SYNCHRON|SFX_CALLMODE_RECORD, &aShow, 0L); + if (!bMod) + pSh->ResetModified(); + pImplDlg->Init(); + + return; + } + + pImplDlg->Activate(); +} + +void SwModelessRedlineAcceptDlg::Initialize(SfxChildWinInfo *pInfo) +{ + if (pInfo != NULL) + pImplDlg->Initialize(pInfo->aExtraString); + + SfxModelessDialog::Initialize(pInfo); +} + +void SwModelessRedlineAcceptDlg::FillInfo(SfxChildWinInfo& rInfo) const +{ + SfxModelessDialog::FillInfo(rInfo); + pImplDlg->FillInfo(rInfo.aExtraString); +} + +SwModelessRedlineAcceptDlg::~SwModelessRedlineAcceptDlg() +{ + delete pImplDlg; +} + +SwRedlineAcceptDlg::SwRedlineAcceptDlg(Dialog *pParent, bool bAutoFmt) : + pParentDlg (pParent), + aTabPagesCTRL (pParent->get_content_area()), + aPopup (SW_RES(MN_REDLINE_POPUP)), + sInserted (SW_RES(STR_REDLINE_INSERTED)), + sDeleted (SW_RES(STR_REDLINE_DELETED)), + sFormated (SW_RES(STR_REDLINE_FORMATED)), + sTableChgd (SW_RES(STR_REDLINE_TABLECHG)), + sFmtCollSet (SW_RES(STR_REDLINE_FMTCOLLSET)), + sAutoFormat (SW_RES(STR_REDLINE_AUTOFMT)), + bOnlyFormatedRedlines( false ), + bHasReadonlySel ( false ), + bRedlnAutoFmt (bAutoFmt), + bInhibitActivate( false ) +{ + aTabPagesCTRL.SetHelpId(HID_REDLINE_CTRL); + pTPView = aTabPagesCTRL.GetViewPage(); + pTable = pTPView->GetTableControl(); + + pTPView->InsertWriterHeader(); + pTPView->SetAcceptClickHdl(LINK(this, SwRedlineAcceptDlg, AcceptHdl)); + pTPView->SetAcceptAllClickHdl(LINK(this, SwRedlineAcceptDlg, AcceptAllHdl)); + pTPView->SetRejectClickHdl(LINK(this, SwRedlineAcceptDlg, RejectHdl)); + pTPView->SetRejectAllClickHdl(LINK(this, SwRedlineAcceptDlg, RejectAllHdl)); + pTPView->SetUndoClickHdl(LINK(this, SwRedlineAcceptDlg, UndoHdl)); + + aTabPagesCTRL.GetFilterPage()->SetReadyHdl(LINK(this, SwRedlineAcceptDlg, FilterChangedHdl)); + + ListBox *pActLB = aTabPagesCTRL.GetFilterPage()->GetLbAction(); + pActLB->InsertEntry(sInserted); + pActLB->InsertEntry(sDeleted); + pActLB->InsertEntry(sFormated); + pActLB->InsertEntry(sTableChgd); + + if (HasRedlineAutoFmt()) + { + pActLB->InsertEntry(sFmtCollSet); + pActLB->InsertEntry(sAutoFormat); + pTPView->ShowUndo(true); + pTPView->DisableUndo(); // no UNDO events yet + } + + pActLB->SelectEntryPos(0); + + pTable->SetStyle(pTable->GetStyle()|WB_HASLINES|WB_CLIPCHILDREN|WB_HASBUTTONS|WB_HASBUTTONSATROOT|WB_HSCROLL); + pTable->SetNodeDefaultImages(); + pTable->SetSelectionMode(MULTIPLE_SELECTION); + pTable->SetHighlightRange(1); + + static long aStaticTabs[]= + { + 4,10,70,120,170 + }; + + pTable->SetTabs(aStaticTabs); + + pTable->SortByCol(nSortMode, bSortDir); + + aOldSelectHdl = pTable->GetSelectHdl(); + aOldDeselectHdl = pTable->GetDeselectHdl(); + pTable->SetSelectHdl(LINK(this, SwRedlineAcceptDlg, SelectHdl)); + pTable->SetDeselectHdl(LINK(this, SwRedlineAcceptDlg, DeselectHdl)); + pTable->SetCommandHdl(LINK(this, SwRedlineAcceptDlg, CommandHdl)); + + // avoid flickering of buttons: + aDeselectTimer.SetTimeout(100); + aDeselectTimer.SetTimeoutHdl(LINK(this, SwRedlineAcceptDlg, SelectHdl)); + + // avoid multiple selection of the same texts: + aSelectTimer.SetTimeout(100); + aSelectTimer.SetTimeoutHdl(LINK(this, SwRedlineAcceptDlg, GotoHdl)); +} + +SwRedlineAcceptDlg::~SwRedlineAcceptDlg() +{ +} + +void SwRedlineAcceptDlg::Init(sal_uInt16 nStart) +{ + SwWait aWait( *::GetActiveView()->GetDocShell(), false ); + pTable->SetUpdateMode(false); + aUsedSeqNo.clear(); + + if (nStart) + RemoveParents(nStart, aRedlineParents.size() - 1); + else + { + pTable->Clear(); + aRedlineChildren.clear(); + aRedlineParents.erase(aRedlineParents.begin() + nStart, aRedlineParents.end()); + } + + // insert parents + InsertParents(nStart); + InitAuthors(); + + pTable->SetUpdateMode(true); + // #i69618# this moves the list box to the right position, visually + SvTreeListEntry* pSelEntry = pTable->FirstSelected(); + if( pSelEntry ) + pTable->MakeVisible( pSelEntry, true ); //#i70937#, force the scroll +} + +void SwRedlineAcceptDlg::InitAuthors() +{ + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + + SvxTPFilter *pFilterPage = aTabPagesCTRL.GetFilterPage(); + + std::vector<OUString> aStrings; + OUString sOldAuthor(pFilterPage->GetSelectedAuthor()); + pFilterPage->ClearAuthors(); + + sal_uInt16 nCount = pSh->GetRedlineCount(); + + bOnlyFormatedRedlines = true; + bHasReadonlySel = false; + bool bIsNotFormated = false; + sal_uInt16 i; + + // determine authors + for ( i = 0; i < nCount; i++) + { + const SwRangeRedline& rRedln = pSh->GetRedline(i); + + if( bOnlyFormatedRedlines && nsRedlineType_t::REDLINE_FORMAT != rRedln.GetType() ) + bOnlyFormatedRedlines = false; + + aStrings.push_back(rRedln.GetAuthorString()); + + for (sal_uInt16 nStack = 1; nStack < rRedln.GetStackCount(); nStack++) + { + aStrings.push_back(rRedln.GetAuthorString(nStack)); + } + } + + std::sort(aStrings.begin(), aStrings.end()); + aStrings.erase(std::unique(aStrings.begin(), aStrings.end()), aStrings.end()); + + for (i = 0; i < aStrings.size(); i++) + pFilterPage->InsertAuthor(aStrings[i]); + + if (pFilterPage->SelectAuthor(sOldAuthor) == LISTBOX_ENTRY_NOTFOUND && !aStrings.empty()) + pFilterPage->SelectAuthor(aStrings[0]); + + bool bEnable = pTable->GetEntryCount() != 0 && !pSh->getIDocumentRedlineAccess()->GetRedlinePassword().getLength(); + bool bSel = pTable->FirstSelected() != 0; + + SvTreeListEntry* pSelEntry = pTable->FirstSelected(); + while (pSelEntry) + { + // find the selected redline + // (fdo#57874: ignore, if the redline is already gone) + sal_uInt16 nPos = GetRedlinePos(*pSelEntry); + if( nPos != USHRT_MAX ) + { + const SwRangeRedline& rRedln = pSh->GetRedline( nPos ); + + bIsNotFormated |= nsRedlineType_t::REDLINE_FORMAT != rRedln.GetType(); + } + pSelEntry = pTable->NextSelected(pSelEntry); + } + + pTPView->EnableAccept( bEnable && bSel ); + pTPView->EnableReject( bEnable && bIsNotFormated && bSel ); + pTPView->EnableAcceptAll( bEnable && !bHasReadonlySel ); + pTPView->EnableRejectAll( bEnable && !bHasReadonlySel && + !bOnlyFormatedRedlines ); +} + +OUString SwRedlineAcceptDlg::GetRedlineText( const SwRangeRedline& rRedln, + DateTime &rDateTime, sal_uInt16 nStack) +{ + OUString sEntry(GetActionText(rRedln, nStack)); + sEntry += "\t"; + sEntry += rRedln.GetAuthorString(nStack); + sEntry += "\t"; + + const DateTime &rDT = rRedln.GetTimeStamp(nStack); + rDateTime = rDT; + + sEntry += GetAppLangDateTimeString( rDT ); + sEntry += "\t"; + + sEntry += rRedln.GetComment(nStack); + + return sEntry; +} + +OUString SwRedlineAcceptDlg::GetActionText(const SwRangeRedline& rRedln, sal_uInt16 nStack) +{ + switch( rRedln.GetType(nStack) ) + { + case nsRedlineType_t::REDLINE_INSERT: return sInserted; + case nsRedlineType_t::REDLINE_DELETE: return sDeleted; + case nsRedlineType_t::REDLINE_FORMAT: return sFormated; + case nsRedlineType_t::REDLINE_TABLE: return sTableChgd; + case nsRedlineType_t::REDLINE_FMTCOLL: return sFmtCollSet; + default:;//prevent warning + } + + return OUString(); +} + +// newly initialise after activation +void SwRedlineAcceptDlg::Activate() +{ + // prevent update if flag is set (#102547#) + if( bInhibitActivate ) + return; + + SwView *pView = ::GetActiveView(); + + if (!pView) // can happen when switching to another app, when a Listbox in the dialog + return; // had the focus previously (actually THs Bug) + + SwWait aWait( *pView->GetDocShell(), false ); + + aUsedSeqNo.clear(); + + // did something change? + SwWrtShell* pSh = pView->GetWrtShellPtr(); + sal_uInt16 nCount = pSh->GetRedlineCount(); + + // check the number of pointers + SwRedlineDataParent *pParent = 0; + sal_uInt16 i; + + for ( i = 0; i < nCount; i++) + { + const SwRangeRedline& rRedln = pSh->GetRedline(i); + + if (i >= aRedlineParents.size()) + { + // new entries have been appended + Init(i); + return; + } + + pParent = &aRedlineParents[i]; + if (&rRedln.GetRedlineData() != pParent->pData) + { + // Redline-Parents were inserted, changed or deleted + if ((i = CalcDiff(i, false)) == USHRT_MAX) + return; + continue; + } + + const SwRedlineData *pRedlineData = rRedln.GetRedlineData().Next(); + const SwRedlineDataChild *pBackupData = pParent->pNext; + + if (!pRedlineData && pBackupData) + { + // Redline-Children were deleted + if ((i = CalcDiff(i, true)) == USHRT_MAX) + return; + continue; + } + else + { + while (pRedlineData) + { + if (pRedlineData != pBackupData->pChild) + { + // Redline-Children were inserted, changed or deleted + if ((i = CalcDiff(i, true)) == USHRT_MAX) + return; + continue; + } + pBackupData = pBackupData->pNext; + pRedlineData = pRedlineData->Next(); + } + } + } + + if (nCount != aRedlineParents.size()) + { + // Redlines were deleted at the end + Init(nCount); + return; + } + + // check comment + for (i = 0; i < nCount; i++) + { + const SwRangeRedline& rRedln = pSh->GetRedline(i); + pParent = &aRedlineParents[i]; + + if(rRedln.GetComment() != pParent->sComment) + { + if (pParent->pTLBParent) + { + // update only comment + OUString sComment(rRedln.GetComment()); + pTable->SetEntryText(sComment.replace('\n', ' '), pParent->pTLBParent, 3); + } + pParent->sComment = rRedln.GetComment(); + } + } + + InitAuthors(); +} + +sal_uInt16 SwRedlineAcceptDlg::CalcDiff(sal_uInt16 nStart, bool bChild) +{ + if (!nStart) + { + Init(); + return USHRT_MAX; + } + + pTable->SetUpdateMode(false); + SwView *pView = ::GetActiveView(); + SwWrtShell* pSh = pView->GetWrtShellPtr(); + sal_uInt16 nAutoFmt = HasRedlineAutoFmt() ? nsRedlineType_t::REDLINE_FORM_AUTOFMT : 0; + SwRedlineDataParent *pParent = &aRedlineParents[nStart]; + const SwRangeRedline& rRedln = pSh->GetRedline(nStart); + + if (bChild) // should actually never happen, but just in case... + { + // throw away all entry's children and initialise newly + SwRedlineDataChild* pBackupData = (SwRedlineDataChild*)pParent->pNext; + SwRedlineDataChild* pNext; + + while (pBackupData) + { + pNext = (SwRedlineDataChild*)pBackupData->pNext; + if (pBackupData->pTLBChild) + pTable->RemoveEntry(pBackupData->pTLBChild); + + for( SwRedlineDataChildArr::iterator it = aRedlineChildren.begin(); + it != aRedlineChildren.end(); ++it) + if (&*it == pBackupData) + { + aRedlineChildren.erase(it); + break; + } + pBackupData = pNext; + } + pParent->pNext = 0; + + // insert new children + InsertChildren(pParent, rRedln, nAutoFmt); + + pTable->SetUpdateMode(true); + return nStart; + } + + // have entries been deleted? + const SwRedlineData *pRedlineData = &rRedln.GetRedlineData(); + sal_uInt16 i; + for ( i = nStart + 1; i < aRedlineParents.size(); i++) + { + if (aRedlineParents[i].pData == pRedlineData) + { + // remove entries from nStart to i-1 + RemoveParents(nStart, i - 1); + pTable->SetUpdateMode(true); + return nStart - 1; + } + } + + // entries been inserted? + sal_uInt16 nCount = pSh->GetRedlineCount(); + pRedlineData = aRedlineParents[nStart].pData; + + for (i = nStart + 1; i < nCount; i++) + { + if (&pSh->GetRedline(i).GetRedlineData() == pRedlineData) + { + // insert entries from nStart to i-1 + InsertParents(nStart, i - 1); + pTable->SetUpdateMode(true); + return nStart - 1; + } + } + + pTable->SetUpdateMode(true); + Init(nStart); // adjust all entries until the end + return USHRT_MAX; +} + +void SwRedlineAcceptDlg::InsertChildren(SwRedlineDataParent *pParent, const SwRangeRedline& rRedln, const sal_uInt16 nAutoFmt) +{ + OUString sChild; + SwRedlineDataChild *pLastRedlineChild = 0; + const SwRedlineData *pRedlineData = &rRedln.GetRedlineData(); + bool bAutoFmt = (rRedln.GetRealType() & nAutoFmt) != 0; + + OUString sAction = GetActionText(rRedln); + bool bValidParent = sFilterAction.isEmpty() || sFilterAction == sAction; + bValidParent = bValidParent && pTable->IsValidEntry(rRedln.GetAuthorString(), rRedln.GetTimeStamp(), rRedln.GetComment()); + if (nAutoFmt) + { + + if (pParent->pData->GetSeqNo()) + { + std::pair<SwRedlineDataParentSortArr::const_iterator,bool> const ret + = aUsedSeqNo.insert(pParent); + if (ret.second) // already there + { + if (pParent->pTLBParent) + { + pTable->SetEntryText( + sAutoFormat, (*ret.first)->pTLBParent, 0); + pTable->RemoveEntry(pParent->pTLBParent); + pParent->pTLBParent = 0; + } + return; + } + } + bValidParent = bValidParent && bAutoFmt; + } + bool bValidTree = bValidParent; + + for (sal_uInt16 nStack = 1; nStack < rRedln.GetStackCount(); nStack++) + { + pRedlineData = pRedlineData->Next(); + + SwRedlineDataChildPtr pRedlineChild = new SwRedlineDataChild; + pRedlineChild->pChild = pRedlineData; + aRedlineChildren.push_back(pRedlineChild); + + if ( pLastRedlineChild ) + pLastRedlineChild->pNext = pRedlineChild; + else + pParent->pNext = pRedlineChild; + + sAction = GetActionText(rRedln, nStack); + bool bValidChild = sFilterAction.isEmpty() || sFilterAction == sAction; + bValidChild = bValidChild && pTable->IsValidEntry(rRedln.GetAuthorString(nStack), rRedln.GetTimeStamp(nStack), rRedln.GetComment()); + if (nAutoFmt) + bValidChild = bValidChild && bAutoFmt; + bValidTree |= bValidChild; + + if (bValidChild) + { + RedlinData *pData = new RedlinData; + pData->pData = pRedlineChild; + pData->bDisabled = true; + sChild = GetRedlineText(rRedln, pData->aDateTime, nStack); + + SvTreeListEntry* pChild = pTable->InsertEntry(sChild, pData, pParent->pTLBParent); + + pRedlineChild->pTLBChild = pChild; + if (!bValidParent) + pTable->Expand(pParent->pTLBParent); + } + else + pRedlineChild->pTLBChild = 0; + + pLastRedlineChild = pRedlineChild; + } + + if (pLastRedlineChild) + pLastRedlineChild->pNext = 0; + + if (!bValidTree && pParent->pTLBParent) + { + pTable->RemoveEntry(pParent->pTLBParent); + pParent->pTLBParent = 0; + if (nAutoFmt) + aUsedSeqNo.erase(pParent); + } +} + +void SwRedlineAcceptDlg::RemoveParents(sal_uInt16 nStart, sal_uInt16 nEnd) +{ + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + sal_uInt16 nCount = pSh->GetRedlineCount(); + + SvLBoxEntryArr aLBoxArr; + + // because of Bug of TLB that ALWAYS calls the SelectHandler at Remove: + pTable->SetSelectHdl(aOldSelectHdl); + pTable->SetDeselectHdl(aOldDeselectHdl); + bool bChildrenRemoved = false; + pTable->SelectAll(false); + + // set the cursor after the last entry because otherwise performance problem in TLB. + // TLB would otherwise reset the cursor at every Remove (expensive) + sal_uInt16 nPos = std::min((sal_uInt16)nCount, (sal_uInt16)aRedlineParents.size()); + SvTreeListEntry *pCurEntry = NULL; + while( ( pCurEntry == NULL ) && ( nPos > 0 ) ) + { + --nPos; + pCurEntry = aRedlineParents[nPos].pTLBParent; + } + + if (pCurEntry) + pTable->SetCurEntry(pCurEntry); + + SvTreeList* pModel = pTable->GetModel(); + + for (sal_uInt16 i = nStart; i <= nEnd; i++) + { + if (!bChildrenRemoved && aRedlineParents[i].pNext) + { + SwRedlineDataChildPtr pChildPtr = (SwRedlineDataChildPtr)aRedlineParents[i].pNext; + for( SwRedlineDataChildArr::iterator it = aRedlineChildren.begin(); + it != aRedlineChildren.end(); ++it) + if (&*it == pChildPtr) + { + sal_uInt16 nChildren = 0; + while (pChildPtr) + { + pChildPtr = (SwRedlineDataChildPtr)pChildPtr->pNext; + nChildren++; + } + + aRedlineChildren.erase(it, it + nChildren); + bChildrenRemoved = true; + break; + } + } + SvTreeListEntry *pEntry = aRedlineParents[i].pTLBParent; + if (pEntry) + { + long nIdx = aLBoxArr.size() - 1L; + sal_uLong nAbsPos = pModel->GetAbsPos(pEntry); + while (nIdx >= 0 && + pModel->GetAbsPos(aLBoxArr[ static_cast< sal_uInt16 >(nIdx) ]) > nAbsPos) + nIdx--; + aLBoxArr.insert( aLBoxArr.begin() + static_cast< sal_uInt16 >(++nIdx) , pEntry); + } + } + + // clear TLB from behind + long nIdx = (long)aLBoxArr.size() - 1L; + while (nIdx >= 0) + pTable->RemoveEntry(aLBoxArr[ static_cast< sal_uInt16 >(nIdx--) ]); + + pTable->SetSelectHdl(LINK(this, SwRedlineAcceptDlg, SelectHdl)); + pTable->SetDeselectHdl(LINK(this, SwRedlineAcceptDlg, DeselectHdl)); + // unfortunately by Remove it was selected from the TLB always again ... + pTable->SelectAll(false); + + aRedlineParents.erase( aRedlineParents.begin() + nStart, aRedlineParents.begin() + nEnd + 1); +} + +void SwRedlineAcceptDlg::InsertParents(sal_uInt16 nStart, sal_uInt16 nEnd) +{ + SwView *pView = ::GetActiveView(); + SwWrtShell* pSh = pView->GetWrtShellPtr(); + sal_uInt16 nAutoFmt = HasRedlineAutoFmt() ? nsRedlineType_t::REDLINE_FORM_AUTOFMT : 0; + + OUString sParent; + sal_uInt16 nCount = pSh->GetRedlineCount(); + nEnd = std::min((sal_uInt16)nEnd, (sal_uInt16)(nCount - 1)); // also treats nEnd=USHRT_MAX (until the end) + + if (nEnd == USHRT_MAX) + return; // no redlines in the document + + RedlinData *pData; + SvTreeListEntry *pParent; + SwRedlineDataParent* pRedlineParent; + const SwRangeRedline* pCurrRedline; + if( !nStart && !pTable->FirstSelected() ) + { + pCurrRedline = pSh->GetCurrRedline(); + if( !pCurrRedline ) + { + pSh->SwCrsrShell::Push(); + if( 0 == (pCurrRedline = pSh->SelNextRedline())) + pCurrRedline = pSh->SelPrevRedline(); + pSh->SwCrsrShell::Pop( false ); + } + } + else + pCurrRedline = 0; + + for (sal_uInt16 i = nStart; i <= nEnd; i++) + { + const SwRangeRedline& rRedln = pSh->GetRedline(i); + const SwRedlineData *pRedlineData = &rRedln.GetRedlineData(); + + pRedlineParent = new SwRedlineDataParent; + pRedlineParent->pData = pRedlineData; + pRedlineParent->pNext = 0; + OUString sComment(rRedln.GetComment()); + pRedlineParent->sComment = sComment.replace('\n', ' '); + aRedlineParents.insert(aRedlineParents.begin() + i, pRedlineParent); + + pData = new RedlinData; + pData->pData = pRedlineParent; + pData->bDisabled = false; + + sParent = GetRedlineText(rRedln, pData->aDateTime); + pParent = pTable->InsertEntry(sParent, pData, 0, i); + if( pCurrRedline == &rRedln ) + { + pTable->SetCurEntry( pParent ); + pTable->Select( pParent ); + pTable->MakeVisible( pParent ); + } + + pRedlineParent->pTLBParent = pParent; + + InsertChildren(pRedlineParent, rRedln, nAutoFmt); + } +} + +void SwRedlineAcceptDlg::CallAcceptReject( bool bSelect, bool bAccept ) +{ + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + SvTreeListEntry* pEntry = bSelect ? pTable->FirstSelected() : pTable->First(); + sal_uLong nPos = LONG_MAX; + + typedef std::vector<SvTreeListEntry*> ListBoxEntries_t; + ListBoxEntries_t aRedlines; + + // don't activate + OSL_ENSURE( bInhibitActivate == false, + "recursive call of CallAcceptReject?"); + bInhibitActivate = true; + + // collect redlines-to-be-accepted/rejected in aRedlines vector + while( pEntry ) + { + if( !pTable->GetParent( pEntry ) ) + { + if( bSelect && LONG_MAX == nPos ) + nPos = pTable->GetModel()->GetAbsPos( pEntry ); + + RedlinData *pData = (RedlinData *)pEntry->GetUserData(); + + if( !pData->bDisabled ) + aRedlines.push_back( pEntry ); + } + + pEntry = bSelect ? pTable->NextSelected(pEntry) : pTable->Next(pEntry); + } + + bool (SwEditShell:: *FnAccRej)( sal_uInt16 ) = &SwEditShell::AcceptRedline; + if( !bAccept ) + FnAccRej = &SwEditShell::RejectRedline; + + SwWait aWait( *pSh->GetView().GetDocShell(), true ); + pSh->StartAction(); + + // #111827# + if (aRedlines.size() > 1) + { + OUString aTmpStr; + { + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, + OUString::number(aRedlines.size())); + aTmpStr = aRewriter.Apply(OUString(SW_RES(STR_N_REDLINES))); + } + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, aTmpStr); + + pSh->StartUndo(bAccept? UNDO_ACCEPT_REDLINE : UNDO_REJECT_REDLINE, + &aRewriter); + } + + // accept/reject the redlines in aRedlines. The absolute + // position may change during the process (e.g. when two redlines + // are merged in result of another one being deleted), so the + // position must be resolved late and checked before using it. + // (cf #102547#) + ListBoxEntries_t::iterator aEnd = aRedlines.end(); + for( ListBoxEntries_t::iterator aIter = aRedlines.begin(); + aIter != aEnd; + ++aIter ) + { + sal_uInt16 nPosition = GetRedlinePos( **aIter ); + if( nPosition != USHRT_MAX ) + (pSh->*FnAccRej)( nPosition ); + } + + // #111827# + if (aRedlines.size() > 1) + { + pSh->EndUndo(); + } + + pSh->EndAction(); + + bInhibitActivate = false; + Activate(); + + if( ULONG_MAX != nPos && pTable->GetEntryCount() ) + { + if( nPos >= pTable->GetEntryCount() ) + nPos = pTable->GetEntryCount() - 1; + pEntry = pTable->GetEntryAtAbsPos( nPos ); + if( !pEntry && nPos-- ) + pEntry = pTable->GetEntryAtAbsPos( nPos ); + if( pEntry ) + { + pTable->Select( pEntry ); + pTable->MakeVisible( pEntry ); + pTable->SetCurEntry(pEntry); + } + } + pTPView->EnableUndo(); +} + +sal_uInt16 SwRedlineAcceptDlg::GetRedlinePos( const SvTreeListEntry& rEntry ) const +{ + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + return pSh->FindRedlineOfData( *((SwRedlineDataParent*)((RedlinData *) + rEntry.GetUserData())->pData)->pData ); +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, AcceptHdl) +{ + CallAcceptReject( true, true ); + return 0; +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, AcceptAllHdl) +{ + CallAcceptReject( false, true ); + return 0; +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, RejectHdl) +{ + CallAcceptReject( true, false ); + return 0; +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, RejectAllHdl) +{ + CallAcceptReject( false, false ); + return 0; +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, UndoHdl) +{ + SwView * pView = ::GetActiveView(); + pView->GetViewFrame()->GetDispatcher()-> + Execute(SID_UNDO, SFX_CALLMODE_SYNCHRON); + pTPView->EnableUndo(pView->GetSlotState(SID_UNDO) != 0); + + Activate(); + + return 0; +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, FilterChangedHdl) +{ + SvxTPFilter *pFilterTP = aTabPagesCTRL.GetFilterPage(); + + if (pFilterTP->IsAction()) + sFilterAction = pFilterTP->GetLbAction()->GetSelectEntry(); + else + sFilterAction = aEmptyOUStr; + + Init(); + + return 0; +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, DeselectHdl) +{ + // avoid flickering of buttons: + aDeselectTimer.Start(); + + return 0; +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, SelectHdl) +{ + aDeselectTimer.Stop(); + aSelectTimer.Start(); + + return 0; +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, GotoHdl) +{ + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + aSelectTimer.Stop(); + + bool bIsNotFormated = false; + bool bSel = false; + + //#98883# don't select redlines while the dialog is not focussed + //#107938# But not only ask pTable if it has the focus. To move + // the selection to the selected redline any child of pParentDlg + // may the focus. + SvTreeListEntry* pSelEntry = 0; + + if (pParentDlg->HasChildPathFocus()) + pSelEntry = pTable->FirstSelected(); + + if( pSelEntry ) + { + SvTreeListEntry* pActEntry = pSelEntry; + pSh->StartAction(); + pSh->EnterStdMode(); + pSh->SetCareWin(pParentDlg); + + while (pSelEntry) + { + if (pTable->GetParent(pSelEntry)) + { + pActEntry = pTable->GetParent(pSelEntry); + + if (pTable->IsSelected(pActEntry)) + { + pSelEntry = pActEntry = pTable->NextSelected(pSelEntry); + continue; // don't select twice + } + } + else + bSel = true; + + // #98864# find the selected redline (ignore, if the redline is already gone) + sal_uInt16 nPos = GetRedlinePos(*pActEntry); + if( nPos != USHRT_MAX ) + { + + const SwRangeRedline& rRedln = pSh->GetRedline( nPos ); + bIsNotFormated |= nsRedlineType_t::REDLINE_FORMAT != rRedln.GetType(); + + if (pSh->GotoRedline(nPos, true)) + { + pSh->SetInSelect(); + pSh->EnterAddMode(); + } + } + + pSelEntry = pActEntry = pTable->NextSelected(pSelEntry); + } + + pSh->LeaveAddMode(); + pSh->EndAction(); + pSh->SetCareWin(NULL); + } + bool bEnable = !pSh->getIDocumentRedlineAccess()->GetRedlinePassword().getLength(); + pTPView->EnableAccept( bEnable && bSel /*&& !bReadonlySel*/ ); + pTPView->EnableReject( bEnable && bSel && bIsNotFormated /*&& !bReadonlySel*/ ); + pTPView->EnableRejectAll( bEnable && !bOnlyFormatedRedlines && !bHasReadonlySel ); + + return 0; +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, CommandHdl) +{ + const CommandEvent aCEvt(pTable->GetCommandEvent()); + + switch ( aCEvt.GetCommand() ) + { + case COMMAND_CONTEXTMENU: + { + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + SvTreeListEntry* pEntry = pTable->FirstSelected(); + const SwRangeRedline *pRed = 0; + + if (pEntry) + { + SvTreeListEntry* pTopEntry = pEntry; + + if (pTable->GetParent(pEntry)) + pTopEntry = pTable->GetParent(pEntry); + + sal_uInt16 nPos = GetRedlinePos(*pTopEntry); + + // disable commenting for protected areas + if (nPos != USHRT_MAX && (pRed = pSh->GotoRedline(nPos, true)) != 0) + { + if( pSh->IsCrsrPtAtEnd() ) + pSh->SwapPam(); + pSh->SetInSelect(); + } + } + + aPopup.EnableItem( MN_EDIT_COMMENT, pEntry && pRed && + !pTable->GetParent(pEntry) && + !pTable->NextSelected(pEntry) +//JP 27.9.2001: make no sense if we handle readonly sections +// && pRed->HasReadonlySel() + ); + + aPopup.EnableItem( MN_SUB_SORT, pTable->First() != 0 ); + sal_uInt16 nColumn = pTable->GetSortedCol(); + if (nColumn == 0xffff) + nColumn = 4; + + PopupMenu *pSubMenu = aPopup.GetPopupMenu(MN_SUB_SORT); + if (pSubMenu) + { + for (sal_uInt16 i = MN_SORT_ACTION; i < MN_SORT_ACTION + 5; i++) + pSubMenu->CheckItem(i, false); + + pSubMenu->CheckItem(nColumn + MN_SORT_ACTION); + } + + sal_uInt16 nRet = aPopup.Execute(pTable, aCEvt.GetMousePosPixel()); + + switch( nRet ) + { + case MN_EDIT_COMMENT: + { + if (pEntry) + { + if (pTable->GetParent(pEntry)) + pEntry = pTable->GetParent(pEntry); + + sal_uInt16 nPos = GetRedlinePos(*pEntry); + + if (nPos == USHRT_MAX) + break; + + const SwRangeRedline &rRedline = pSh->GetRedline(nPos); + + /* enable again once we have redline comments in the margin + sComment = rRedline.GetComment(); + if ( !sComment.Len() ) + GetActiveView()->GetDocShell()->Broadcast(SwRedlineHint(&rRedline,SWREDLINE_INSERTED)); + const_cast<SwRangeRedline&>(rRedline).Broadcast(SwRedlineHint(&rRedline,SWREDLINE_FOCUS)); + */ + + OUString sComment = convertLineEnd(rRedline.GetComment(), GetSystemLineEnd()); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + OSL_ENSURE(pFact, "Dialogdiet fail!"); + ::DialogGetRanges fnGetRange = pFact->GetDialogGetRangesFunc(); + OSL_ENSURE(fnGetRange, "Dialogdiet fail! GetRanges()"); + SfxItemSet aSet( pSh->GetAttrPool(), fnGetRange() ); + + aSet.Put(SvxPostItTextItem(sComment, SID_ATTR_POSTIT_TEXT)); + aSet.Put(SvxPostItAuthorItem(rRedline.GetAuthorString(), SID_ATTR_POSTIT_AUTHOR)); + + aSet.Put(SvxPostItDateItem( GetAppLangDateTimeString( + rRedline.GetRedlineData().GetTimeStamp() ), + SID_ATTR_POSTIT_DATE )); + + AbstractSvxPostItDialog* pDlg = pFact->CreateSvxPostItDialog( pParentDlg, aSet, false ); + OSL_ENSURE(pDlg, "Dialogdiet fail!"); + + pDlg->HideAuthor(); + + sal_uInt16 nResId = 0; + switch( rRedline.GetType() ) + { + case nsRedlineType_t::REDLINE_INSERT: + nResId = STR_REDLINE_INSERTED; + break; + case nsRedlineType_t::REDLINE_DELETE: + nResId = STR_REDLINE_DELETED; + break; + case nsRedlineType_t::REDLINE_FORMAT: + nResId = STR_REDLINE_FORMATED; + break; + case nsRedlineType_t::REDLINE_TABLE: + nResId = STR_REDLINE_TABLECHG; + break; + default:;//prevent warning + } + OUString sTitle(SW_RES(STR_REDLINE_COMMENT)); + if( nResId ) + sTitle += SW_RESSTR( nResId ); + pDlg->SetText(sTitle); + + pSh->SetCareWin(pDlg->GetWindow()); + + if ( pDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + OUString sMsg(((const SvxPostItTextItem&)pOutSet->Get(SID_ATTR_POSTIT_TEXT)).GetValue()); + + // insert / change comment + pSh->SetRedlineComment(sMsg); + pTable->SetEntryText(sMsg.replace('\n', ' '), pEntry, 3); + } + + delete pDlg; + pSh->SetCareWin(NULL); + } + + } + break; + + case MN_SORT_ACTION: + case MN_SORT_AUTHOR: + case MN_SORT_DATE: + case MN_SORT_COMMENT: + case MN_SORT_POSITION: + { + bSortDir = true; + if (nRet - MN_SORT_ACTION == 4 && pTable->GetSortedCol() == 0xffff) + break; // we already have it + + nSortMode = nRet - MN_SORT_ACTION; + if (nSortMode == 4) + nSortMode = 0xffff; // unsorted / sorted by position + + if (pTable->GetSortedCol() == nSortMode) + bSortDir = !pTable->GetSortDirection(); + + SwWait aWait( *::GetActiveView()->GetDocShell(), false ); + pTable->SortByCol(nSortMode, bSortDir); + if (nSortMode == 0xffff) + Init(); // newly fill everything + } + break; + } + } + break; + } + + return 0; +} + +void SwRedlineAcceptDlg::Initialize(const OUString& rExtraData) +{ + if (!rExtraData.isEmpty()) + { + sal_Int32 nPos = rExtraData.indexOf("AcceptChgDat:"); + + // try to read the alignment string "ALIGN:(...)"; if none existing, + // it's an old version + if (nPos != -1) + { + sal_Int32 n1 = rExtraData.indexOf('(', nPos); + if (n1 != -1) + { + sal_Int32 n2 = rExtraData.indexOf(')', n1); + if (n2 != -1) + { + // cut out the alignment string + OUString aStr = rExtraData.copy(nPos, n2 - nPos + 1); + aStr = aStr.copy(n1 - nPos + 1); + + if (!aStr.isEmpty()) + { + sal_uInt16 nCount = static_cast< sal_uInt16 >(aStr.toInt32()); + + for (sal_uInt16 i = 0; i < nCount; i++) + { + sal_Int32 n3 = aStr.indexOf(';'); + aStr = aStr.copy(n3 + 1); + pTable->SetTab(i, aStr.toInt32(), MAP_PIXEL); + } + } + } + } + } + } +} + +void SwRedlineAcceptDlg::FillInfo(OUString &rExtraData) const +{ + rExtraData += "AcceptChgDat:("; + + sal_uInt16 nCount = pTable->TabCount(); + + rExtraData += OUString::number(nCount); + rExtraData += ";"; + for(sal_uInt16 i = 0; i < nCount; i++) + { + rExtraData += OUString::number( pTable->GetTab(i) ); + rExtraData += ";"; + } + rExtraData += ")"; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/misc/redlndlg.hrc b/sw/source/uibase/misc/redlndlg.hrc new file mode 100644 index 000000000000..e3d0464faca6 --- /dev/null +++ b/sw/source/uibase/misc/redlndlg.hrc @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#define MN_EDIT_COMMENT 1 +#define MN_SUB_SORT 2 +#define MN_SORT_ACTION 3 +#define MN_SORT_AUTHOR 4 +#define MN_SORT_DATE 5 +#define MN_SORT_COMMENT 6 +#define MN_SORT_POSITION 7 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/misc/redlndlg.src b/sw/source/uibase/misc/redlndlg.src new file mode 100644 index 000000000000..e3b9aadb3a0f --- /dev/null +++ b/sw/source/uibase/misc/redlndlg.src @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +/* StarView resource file */ + +#include "redlndlg.hrc" +#include "misc.hrc" +#include "helpid.h" + +Menu MN_REDLINE_POPUP +{ + ItemList = + { + MenuItem + { + Identifier = MN_EDIT_COMMENT ; + HelpID = HID_EDIT_COMMENT ; + Text [ en-US ] = "Edit Comment..." ; + }; + MenuItem + { + Identifier = MN_SUB_SORT ; + RadioCheck = TRUE ; + SubMenu = Menu + { + ItemList = + { + MenuItem + { + Identifier = MN_SORT_ACTION ; + HelpID = HID_SORT_ACTION ; + RadioCheck = TRUE ; + Text [ en-US ] = "Action" ; + }; + MenuItem + { + Identifier = MN_SORT_AUTHOR ; + HelpID = HID_SORT_AUTHOR ; + RadioCheck = TRUE ; + Text [ en-US ] = "Author" ; + }; + MenuItem + { + Identifier = MN_SORT_DATE ; + HelpID = HID_SORT_DATE ; + RadioCheck = TRUE ; + Text [ en-US ] = "Date" ; + }; + MenuItem + { + Identifier = MN_SORT_COMMENT ; + HelpID = HID_SORT_COMMENT ; + RadioCheck = TRUE ; + Text [ en-US ] = "Comment" ; + }; + MenuItem + { + Identifier = MN_SORT_POSITION ; + HelpID = HID_SW_SORT_POSITION ; + RadioCheck = TRUE ; + Text [ en-US ] = "Document position" ; + }; + }; + }; + Text [ en-US ] = "Sort By" ; + }; + }; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/misc/swruler.cxx b/sw/source/uibase/misc/swruler.cxx new file mode 100644 index 000000000000..1ba770fd6a42 --- /dev/null +++ b/sw/source/uibase/misc/swruler.cxx @@ -0,0 +1,302 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +// FIX fdo#38246 https://bugs.libreoffice.org/show_bug.cgi?id=38246 +// Design proposal: https://wiki.documentfoundation.org/Design/Whiteboards/Comments_Ruler_Control +// TODO Alpha blend border when it doesn't fit in window + +#include "swruler.hxx" + +#include "viewsh.hxx" +#include "edtwin.hxx" +#include "PostItMgr.hxx" +#include "viewopt.hxx" +#include <view.hxx> +#include "cmdid.h" +#include <sfx2/request.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <vcl/settings.hxx> +#include "misc.hrc" + +#define CONTROL_BORDER_WIDTH 1 + +#define CONTROL_LEFT_OFFSET 6 +#define CONTROL_RIGHT_OFFSET 3 +#define CONTROL_TOP_OFFSET 4 + +#define CONTROL_TRIANGLE_WIDTH 4 +#define CONTROL_TRIANGLE_PAD 3 + +// Constructor +SwCommentRuler::SwCommentRuler( SwViewShell* pViewSh, Window* pParent, SwEditWin* pWin, sal_uInt16 nRulerFlags, SfxBindings& rBindings, WinBits nWinStyle) +: SvxRuler(pParent, pWin, nRulerFlags, rBindings, nWinStyle | WB_HSCROLL) +, mpViewShell(pViewSh) +, mpSwWin(pWin) +, mbIsHighlighted(false) +, mnFadeRate(0) +, maVirDev( *this ) +{ + // Set fading timeout: 5 x 40ms = 200ms + maFadeTimer.SetTimeout(40); + maFadeTimer.SetTimeoutHdl( LINK( this, SwCommentRuler, FadeHandler ) ); +} + +// Destructor +SwCommentRuler::~SwCommentRuler() +{ +} + +void SwCommentRuler::Paint( const Rectangle& rRect ) +{ + SvxRuler::Paint( rRect ); + // Don't draw if there is not any note + if ( mpViewShell->GetPostItMgr() + && mpViewShell->GetPostItMgr()->HasNotes() ) + DrawCommentControl(); +} + +void SwCommentRuler::DrawCommentControl() +{ + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + bool bIsCollapsed = ! mpViewShell->GetPostItMgr()->ShowNotes(); + + Rectangle aControlRect = GetCommentControlRegion(); + maVirDev.SetOutputSizePixel( aControlRect.GetSize() ); + + // Paint comment control background + // TODO Check if these are best colors to be used + Color aBgColor = GetFadedColor( rStyleSettings.GetDarkShadowColor(), rStyleSettings.GetWorkspaceColor() ); + maVirDev.SetFillColor( aBgColor ); + + if ( mbIsHighlighted || !bIsCollapsed ) + { + // Draw borders + maVirDev.SetLineColor( rStyleSettings.GetShadowColor() ); + } + else + { + // No borders + maVirDev.SetLineColor(); + } + + maVirDev.DrawRect( Rectangle( Point(), aControlRect.GetSize() ) ); + + // Label and arrow tip + OUString aLabel( SW_RESSTR ( STR_COMMENTS_LABEL ) ); + // Get label and arrow coordinates + Point aLabelPos; + Point aArrowPos; + bool bArrowToRight; + // TODO Discover why it should be 0 instead of CONTROL_BORDER_WIDTH + CONTROL_TOP_OFFSET + aLabelPos.Y() = 0; + aArrowPos.Y() = CONTROL_BORDER_WIDTH + CONTROL_TOP_OFFSET; + if ( !Application::GetSettings().GetLayoutRTL() ) + { + // LTR + if ( bIsCollapsed ) + { + // It should draw something like | > Comments | + aLabelPos.X() = CONTROL_LEFT_OFFSET + CONTROL_TRIANGLE_WIDTH + CONTROL_TRIANGLE_PAD; + aArrowPos.X() = CONTROL_LEFT_OFFSET; + } + else + { + // It should draw something like | Comments < | + aLabelPos.X() = CONTROL_LEFT_OFFSET; + aArrowPos.X() = aControlRect.GetSize().Width() - 1 - CONTROL_RIGHT_OFFSET - CONTROL_BORDER_WIDTH - CONTROL_TRIANGLE_WIDTH; + } + bArrowToRight = bIsCollapsed; + } + else + { + // RTL + long nLabelWidth = GetTextWidth( aLabel ); + if ( bIsCollapsed ) + { + // It should draw something like | Comments < | + aArrowPos.X() = aControlRect.GetSize().Width() - 1 - CONTROL_RIGHT_OFFSET - CONTROL_BORDER_WIDTH - CONTROL_TRIANGLE_WIDTH; + aLabelPos.X() = aArrowPos.X() - CONTROL_TRIANGLE_PAD - nLabelWidth; + } + else + { + // It should draw something like | > Comments | + aLabelPos.X() = aControlRect.GetSize().Width() - 1 - CONTROL_RIGHT_OFFSET - CONTROL_BORDER_WIDTH - nLabelWidth; + aArrowPos.X() = CONTROL_LEFT_OFFSET; + } + bArrowToRight = !bIsCollapsed; + } + + // Draw label + Color aTextColor = GetFadedColor( rStyleSettings.GetButtonTextColor(), rStyleSettings.GetDarkShadowColor() ); + maVirDev.SetTextColor( aTextColor ); + // FIXME Expected font size? + maVirDev.DrawText( aLabelPos, aLabel ); + + // Draw arrow + // FIXME consistence of button colors. http://opengrok.libreoffice.org/xref/core/vcl/source/control/button.cxx#785 + Color aArrowColor = GetFadedColor( Color( COL_BLACK ), rStyleSettings.GetShadowColor() ); + ImplDrawArrow ( aArrowPos.X(), aArrowPos.Y(), aArrowColor, bArrowToRight ); + + // Blit comment control + DrawOutDev( aControlRect.TopLeft(), aControlRect.GetSize(), Point(), aControlRect.GetSize(), maVirDev ); +} + +void SwCommentRuler::ImplDrawArrow(long nX, long nY, const Color& rColor, bool bPointRight) +{ + maVirDev.SetLineColor(); + maVirDev.SetFillColor( rColor ); + if ( bPointRight ) + { + maVirDev.DrawRect( Rectangle( nX+0, nY+0, nX+0, nY+6 ) ); + maVirDev.DrawRect( Rectangle( nX+1, nY+1, nX+1, nY+5 ) ); + maVirDev.DrawRect( Rectangle( nX+2, nY+2, nX+2, nY+4 ) ); + maVirDev.DrawRect( Rectangle( nX+3, nY+3, nX+3, nY+3 ) ); + } + else + { + maVirDev.DrawRect( Rectangle( nX+0, nY+3, nX+0, nY+3 ) ); + maVirDev.DrawRect( Rectangle( nX+1, nY+2, nX+1, nY+4 ) ); + maVirDev.DrawRect( Rectangle( nX+2, nY+1, nX+2, nY+5 ) ); + maVirDev.DrawRect( Rectangle( nX+3, nY+0, nX+3, nY+6 ) ); + } +} + +// Just accept double-click outside comment control +void SwCommentRuler::Command( const CommandEvent& rCEvt ) +{ + Point aMousePos = rCEvt.GetMousePosPixel(); + // Ignore command request if it is inside Comment Control + if ( !mpViewShell->GetPostItMgr() + || !mpViewShell->GetPostItMgr()->HasNotes() + || !GetCommentControlRegion().IsInside( aMousePos ) ) + SvxRuler::Command( rCEvt ); +} + +void SwCommentRuler::MouseMove(const MouseEvent& rMEvt) +{ + SvxRuler::MouseMove(rMEvt); + if ( ! mpViewShell->GetPostItMgr() || ! mpViewShell->GetPostItMgr()->HasNotes() ) + return; + + Point aMousePos = rMEvt.GetPosPixel(); + bool bWasHighlighted = mbIsHighlighted; + mbIsHighlighted = GetCommentControlRegion().IsInside( aMousePos ); + if ( mbIsHighlighted != bWasHighlighted ) + { + // Set proper help text + if ( mbIsHighlighted ) + { + // Mouse over comment control + UpdateCommentHelpText(); + } + else + { + // Mouse out of comment control + // FIXME Should remember previous tooltip text? + SetQuickHelpText( OUString() ); + } + // Do start fading + maFadeTimer.Start(); + } +} + +void SwCommentRuler::MouseButtonDown( const MouseEvent& rMEvt ) +{ + Point aMousePos = rMEvt.GetPosPixel(); + if ( !rMEvt.IsLeft() || IsTracking() || !GetCommentControlRegion().IsInside( aMousePos ) ) + { + SvxRuler::MouseButtonDown(rMEvt); + return; + } + + // Toggle notes visibility + SwView &rView = mpSwWin->GetView(); + SfxRequest aRequest( rView.GetViewFrame(), FN_VIEW_NOTES ); + rView.ExecViewOptions( aRequest ); + + // It is inside comment control, so update help text + UpdateCommentHelpText(); + + Invalidate(); +} + +void SwCommentRuler::Update() +{ + Rectangle aPreviousControlRect = GetCommentControlRegion(); + SvxRuler::Update(); + if (aPreviousControlRect != GetCommentControlRegion()) + Invalidate(); +} + +void SwCommentRuler::UpdateCommentHelpText() +{ + int nTooltipResId; + if ( mpViewShell->GetPostItMgr()->ShowNotes() ) + nTooltipResId = STR_HIDE_COMMENTS; + else + nTooltipResId = STR_SHOW_COMMENTS; + SetQuickHelpText( OUString( SW_RESSTR( nTooltipResId ) ) ); +} + +// TODO Make Ruler return its central rectangle instead of margins. +Rectangle SwCommentRuler::GetCommentControlRegion() +{ + long nLeft = 0; + SwPostItMgr *pPostItMgr = mpViewShell->GetPostItMgr(); + + //rhbz#1006850 When the SwPostItMgr ctor is called from SwView::SwView it + //triggers an update of the uiview, but the result of the ctor hasn't been + //set into the mpViewShell yet, so GetPostItMgr is temporarily still NULL + if (!pPostItMgr) + return Rectangle(); + + unsigned long nSidebarWidth = pPostItMgr->GetSidebarWidth(true); + //FIXME When the page width is larger then screen, the ruler is misplaced by one pixel + if (GetTextRTL()) + nLeft = GetPageOffset() - nSidebarWidth + GetBorderOffset(); + else + nLeft = GetWinOffset() + GetPageOffset() + mpSwWin->LogicToPixel(Size(GetPageWidth(), 0)).Width(); + long nTop = 0 + 4; // Ruler::ImplDraw uses RULER_OFF (value: 3px) as offset, and Ruler::ImplFormat adds one extra pixel + // Somehow pPostItMgr->GetSidebarBorderWidth() returns border width already doubled + long nRight = nLeft + nSidebarWidth + pPostItMgr->GetSidebarBorderWidth(true); + long nBottom = nTop + GetRulerVirHeight() - 3; + + Rectangle aRect(nLeft, nTop, nRight, nBottom); + return aRect; +} + +Color SwCommentRuler::GetFadedColor(const Color &rHighColor, const Color &rLowColor) +{ + if ( ! maFadeTimer.IsActive() ) + return mbIsHighlighted ? rHighColor : rLowColor; + + Color aColor = rHighColor; + aColor.Merge( rLowColor, mnFadeRate * 255/100.f ); + return aColor; +} + +IMPL_LINK_NOARG(SwCommentRuler, FadeHandler) +{ + const int nStep = 25; + if ( mbIsHighlighted && mnFadeRate < 100 ) + mnFadeRate += nStep; + else if ( !mbIsHighlighted && mnFadeRate > 0 ) + mnFadeRate -= nStep; + else + return 0; + + Invalidate(); + + if ( mnFadeRate != 0 && mnFadeRate != 100) + maFadeTimer.Start(); + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |