1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <framework/titlehelper.hxx>
21 #include <classes/fwkresid.hxx>
22 #include <strings.hrc>
23 #include <properties.h>
25 #include <com/sun/star/frame/UntitledNumbersConst.hpp>
26 #include <com/sun/star/frame/XStorable.hpp>
27 #include <com/sun/star/frame/ModuleManager.hpp>
28 #include <com/sun/star/frame/XUntitledNumbers.hpp>
29 #include <com/sun/star/frame/XModel3.hpp>
30 #include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
32 #include <unotools/configmgr.hxx>
33 #include <unotools/bootstrap.hxx>
34 #include <unotools/mediadescriptor.hxx>
35 #include <comphelper/sequenceashashmap.hxx>
36 #include <rtl/ustrbuf.hxx>
37 #include <osl/mutex.hxx>
38 #include <tools/urlobj.hxx>
40 #include <vcl/svapp.hxx>
44 using namespace css::uno
;
45 using namespace css::frame
;
49 TitleHelper::TitleHelper(css::uno::Reference
< css::uno::XComponentContext
> xContext
,
50 const css::uno::Reference
< css::uno::XInterface
>& xOwner
,
51 const css::uno::Reference
< css::frame::XUntitledNumbers
>& xNumbers
)
52 : ::cppu::BaseMutex ()
53 , m_xContext (std::move(xContext
))
54 , m_bExternalTitle (false)
55 , m_nLeasedNumber (css::frame::UntitledNumbersConst::INVALID_NUMBER
)
56 , m_aListener (m_aMutex
)
60 osl::MutexGuard
aLock(m_aMutex
);
63 m_xUntitledNumbers
= xNumbers
;
67 css::uno::Reference
< css::frame::XModel
> xModel(xOwner
, css::uno::UNO_QUERY
);
70 impl_startListeningForModel (xModel
);
74 css::uno::Reference
< css::frame::XController
> xController(xOwner
, css::uno::UNO_QUERY
);
75 if (xController
.is ())
77 impl_startListeningForController (xController
);
81 css::uno::Reference
< css::frame::XFrame
> xFrame(xOwner
, css::uno::UNO_QUERY
);
84 impl_startListeningForFrame (xFrame
);
89 TitleHelper::~TitleHelper()
93 OUString SAL_CALL
TitleHelper::getTitle()
96 osl::MutexGuard
aLock(m_aMutex
);
98 // An external title will win always and disable all internal logic about
99 // creating/using a title value.
100 // Even an empty string will be accepted as valid title !
101 if (m_bExternalTitle
)
104 // Title seems to be up-to-date. Return it directly.
105 if (!m_sTitle
.isEmpty())
108 // Title seems to be unused till now ... do bootstrapping
109 impl_updateTitle (true);
115 void SAL_CALL
TitleHelper::setTitle(const OUString
& sTitle
)
119 osl::MutexGuard
aLock(m_aMutex
);
121 m_bExternalTitle
= true;
126 impl_sendTitleChangedEvent ();
129 void SAL_CALL
TitleHelper::addTitleChangeListener(const css::uno::Reference
< css::frame::XTitleChangeListener
>& xListener
)
131 // container is threadsafe by himself
132 m_aListener
.addInterface( cppu::UnoType
<css::frame::XTitleChangeListener
>::get(), xListener
);
135 void SAL_CALL
TitleHelper::removeTitleChangeListener(const css::uno::Reference
< css::frame::XTitleChangeListener
>& xListener
)
137 // container is threadsafe by himself
138 m_aListener
.removeInterface( cppu::UnoType
<css::frame::XTitleChangeListener
>::get(), xListener
);
141 void SAL_CALL
TitleHelper::titleChanged(const css::frame::TitleChangedEvent
& aEvent
)
143 css::uno::Reference
< css::frame::XTitle
> xSubTitle
;
146 osl::MutexGuard
aLock(m_aMutex
);
148 xSubTitle
.set(m_xSubTitle
.get (), css::uno::UNO_QUERY
);
152 if (aEvent
.Source
!= xSubTitle
)
158 void SAL_CALL
TitleHelper::documentEventOccured(const css::document::DocumentEvent
& aEvent
)
160 if ( ! aEvent
.EventName
.equalsIgnoreAsciiCase("OnSaveAsDone")
161 && ! aEvent
.EventName
.equalsIgnoreAsciiCase("OnModeChanged")
162 && ! aEvent
.EventName
.equalsIgnoreAsciiCase("OnTitleChanged"))
165 css::uno::Reference
< css::frame::XModel
> xOwner
;
168 osl::MutexGuard
aLock(m_aMutex
);
170 xOwner
.set(m_xOwner
.get (), css::uno::UNO_QUERY
);
174 if (aEvent
.Source
!= xOwner
175 || ((aEvent
.EventName
.equalsIgnoreAsciiCase("OnModeChanged")
176 || aEvent
.EventName
.equalsIgnoreAsciiCase("OnTitleChanged"))
185 void SAL_CALL
TitleHelper::frameAction(const css::frame::FrameActionEvent
& aEvent
)
187 css::uno::Reference
< css::frame::XFrame
> xOwner
;
190 osl::MutexGuard
aLock(m_aMutex
);
192 xOwner
.set(m_xOwner
.get (), css::uno::UNO_QUERY
);
196 if (aEvent
.Source
!= xOwner
)
199 // we are interested on events only, which must trigger a title bar update
200 // because component was changed.
202 (aEvent
.Action
== css::frame::FrameAction_COMPONENT_ATTACHED
) ||
203 (aEvent
.Action
== css::frame::FrameAction_COMPONENT_REATTACHED
) ||
204 (aEvent
.Action
== css::frame::FrameAction_COMPONENT_DETACHING
)
207 impl_updateListeningForFrame (xOwner
);
212 void SAL_CALL
TitleHelper::disposing(const css::lang::EventObject
& aEvent
)
214 css::uno::Reference
< css::uno::XInterface
> xOwner
;
215 css::uno::Reference
< css::frame::XUntitledNumbers
> xNumbers
;
216 ::sal_Int32 nLeasedNumber
;
219 osl::MutexGuard
aLock(m_aMutex
);
222 xNumbers
.set(m_xUntitledNumbers
.get(), css::uno::UNO_QUERY
);
223 nLeasedNumber
= m_nLeasedNumber
;
230 css::uno::Reference
< css::frame::XFrame
> xFrame(xOwner
, css::uno::UNO_QUERY
);
232 xFrame
->removeFrameActionListener(this);
234 if (xOwner
!= aEvent
.Source
)
239 (nLeasedNumber
!= css::frame::UntitledNumbersConst::INVALID_NUMBER
)
241 xNumbers
->releaseNumber (nLeasedNumber
);
245 osl::MutexGuard
aLock(m_aMutex
);
248 m_sTitle
= OUString ();
249 m_nLeasedNumber
= css::frame::UntitledNumbersConst::INVALID_NUMBER
;
254 void TitleHelper::impl_sendTitleChangedEvent ()
256 css::uno::Reference
<css::uno::XInterface
> xOwner
;
259 osl::MutexGuard
aLock(m_aMutex
);
265 css::frame::TitleChangedEvent
aEvent(xOwner
, m_sTitle
);
267 if( ! aEvent
.Source
.is() )
270 comphelper::OInterfaceContainerHelper2
* pContainer
= m_aListener
.getContainer( cppu::UnoType
<css::frame::XTitleChangeListener
>::get());
274 comphelper::OInterfaceIteratorHelper2
pIt( *pContainer
);
275 while ( pIt
.hasMoreElements() )
279 static_cast<css::frame::XTitleChangeListener
*>(pIt
.next())->titleChanged( aEvent
);
281 catch(const css::uno::Exception
&)
288 void TitleHelper::impl_updateTitle (bool init
)
290 css::uno::Reference
< css::frame::XModel3
> xModel
;
291 css::uno::Reference
< css::frame::XController
> xController
;
292 css::uno::Reference
< css::frame::XFrame
> xFrame
;
295 osl::MutexGuard
aLock(m_aMutex
);
297 xModel
.set (m_xOwner
.get(), css::uno::UNO_QUERY
);
298 xController
.set(m_xOwner
.get(), css::uno::UNO_QUERY
);
299 xFrame
.set (m_xOwner
.get(), css::uno::UNO_QUERY
);
305 impl_updateTitleForModel (xModel
, init
);
307 else if (xController
.is ())
309 impl_updateTitleForController (xController
, init
);
311 else if (xFrame
.is ())
313 impl_updateTitleForFrame (xFrame
, init
);
317 void TitleHelper::impl_updateTitleForModel (const css::uno::Reference
< css::frame::XModel3
>& xModel
, bool init
)
319 css::uno::Reference
< css::uno::XInterface
> xOwner
;
320 css::uno::Reference
< css::frame::XUntitledNumbers
> xNumbers
;
321 ::sal_Int32 nLeasedNumber
;
324 osl::MutexGuard
aLock(m_aMutex
);
326 // external title won't be updated internally!
327 // It has to be set from outside new.
328 if (m_bExternalTitle
)
332 xNumbers
.set (m_xUntitledNumbers
.get(), css::uno::UNO_QUERY
);
333 nLeasedNumber
= m_nLeasedNumber
;
339 ( ! xNumbers
.is ()) ||
347 css::uno::Reference
< css::frame::XStorable
> xURLProvider(xModel
, css::uno::UNO_QUERY
);
348 if (xURLProvider
.is())
349 sURL
= xURLProvider
->getLocation ();
351 utl::MediaDescriptor
aDescriptor(xModel
->getArgs2( { utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME
} ));
352 const OUString sSuggestedSaveAsName
= aDescriptor
.getUnpackedValueOrDefault(
353 utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME
, OUString());
357 sTitle
= impl_convertURL2Title(sURL
);
358 if (nLeasedNumber
!= css::frame::UntitledNumbersConst::INVALID_NUMBER
)
359 xNumbers
->releaseNumber (nLeasedNumber
);
360 nLeasedNumber
= css::frame::UntitledNumbersConst::INVALID_NUMBER
;
362 else if (!sSuggestedSaveAsName
.isEmpty())
364 // tdf#121537 Use suggested save as name for title if file has not yet been saved
365 sTitle
= sSuggestedSaveAsName
;
369 if (nLeasedNumber
== css::frame::UntitledNumbersConst::INVALID_NUMBER
)
370 nLeasedNumber
= xNumbers
->leaseNumber (xOwner
);
372 if (nLeasedNumber
!= css::frame::UntitledNumbersConst::INVALID_NUMBER
)
373 sTitle
= xNumbers
->getUntitledPrefix() + OUString::number(nLeasedNumber
);
375 sTitle
= xNumbers
->getUntitledPrefix() + "?";
381 osl::MutexGuard
aLock(m_aMutex
);
383 // WORKAROUND: the notification is currently sent always,
384 // can be changed after shared mode is supported per UNO API
385 bChanged
= !init
; // && m_sTitle != sTitle
388 m_nLeasedNumber
= nLeasedNumber
;
393 impl_sendTitleChangedEvent ();
396 void TitleHelper::impl_updateTitleForController (const css::uno::Reference
< css::frame::XController
>& xController
, bool init
)
398 css::uno::Reference
< css::uno::XInterface
> xOwner
;
399 css::uno::Reference
< css::frame::XUntitledNumbers
> xNumbers
;
400 ::sal_Int32 nLeasedNumber
;
403 osl::MutexGuard
aLock(m_aMutex
);
405 // external title won't be updated internally!
406 // It has to be set from outside new.
407 if (m_bExternalTitle
)
411 xNumbers
.set (m_xUntitledNumbers
.get(), css::uno::UNO_QUERY
);
412 nLeasedNumber
= m_nLeasedNumber
;
418 ( ! xNumbers
.is ()) ||
419 ( ! xController
.is ())
423 OUStringBuffer
sTitle(256);
425 if (nLeasedNumber
== css::frame::UntitledNumbersConst::INVALID_NUMBER
)
426 nLeasedNumber
= xNumbers
->leaseNumber (xOwner
);
428 css::uno::Reference
< css::frame::XTitle
> xModelTitle(xController
->getModel (), css::uno::UNO_QUERY
);
429 css::uno::Reference
< css::frame::XModel
> xModel
= xController
->getModel ();
430 if (!xModelTitle
.is ())
431 xModelTitle
.set(xController
, css::uno::UNO_QUERY
);
432 if (xModelTitle
.is ())
434 sTitle
.append (xModelTitle
->getTitle ());
435 if ( nLeasedNumber
> 1 )
437 sTitle
.append(" : " + OUString::number(nLeasedNumber
));
441 INetURLObject
aURL (xModel
->getURL ());
442 if (aURL
.GetProtocol () != INetProtocol::File
443 && aURL
.GetProtocol () != INetProtocol::NotValid
)
445 OUString
sRemoteText (FwkResId (STR_REMOTE_TITLE
));
446 sTitle
.append (sRemoteText
);
452 sTitle
.append (xNumbers
->getUntitledPrefix ());
453 if ( nLeasedNumber
> 1 )
455 sTitle
.append(nLeasedNumber
);
462 osl::MutexGuard
aLock(m_aMutex
);
464 OUString sNewTitle
= sTitle
.makeStringAndClear ();
465 bChanged
= !init
&& m_sTitle
!= sNewTitle
;
466 m_sTitle
= sNewTitle
;
467 m_nLeasedNumber
= nLeasedNumber
;
472 impl_sendTitleChangedEvent ();
475 void TitleHelper::impl_updateTitleForFrame (const css::uno::Reference
< css::frame::XFrame
>& xFrame
, bool init
)
482 osl::MutexGuard
aLock(m_aMutex
);
484 // external title won't be updated internally!
485 // It has to be set from outside new.
486 if (m_bExternalTitle
)
491 css::uno::Reference
< css::uno::XInterface
> xComponent
= xFrame
->getController ();
492 if ( ! xComponent
.is ())
493 xComponent
= xFrame
->getComponentWindow ();
495 OUStringBuffer
sTitle (256);
497 impl_appendComponentTitle (sTitle
, xComponent
);
499 if (!utl::ConfigManager::IsFuzzing())
501 // fdo#70376: We want the window title to contain just the
502 // document name (from the above "component title").
503 impl_appendProductName (sTitle
);
504 impl_appendModuleName (sTitle
);
505 impl_appendDebugVersion (sTitle
);
508 impl_appendSafeMode (sTitle
);
513 osl::MutexGuard
aLock(m_aMutex
);
515 OUString sNewTitle
= sTitle
.makeStringAndClear ();
516 bChanged
= !init
&& m_sTitle
!= sNewTitle
;
517 m_sTitle
= sNewTitle
;
522 impl_sendTitleChangedEvent ();
525 void TitleHelper::impl_appendComponentTitle ( OUStringBuffer
& sTitle
,
526 const css::uno::Reference
< css::uno::XInterface
>& xComponent
)
528 css::uno::Reference
< css::frame::XTitle
> xTitle(xComponent
, css::uno::UNO_QUERY
);
530 // Note: Title has to be used (even if it's empty) if the right interface is supported.
532 sTitle
.append (xTitle
->getTitle ());
535 void TitleHelper::impl_appendProductName (OUStringBuffer
& sTitle
)
537 OUString
name(utl::ConfigManager::getProductName());
540 if (!sTitle
.isEmpty())
542 OUString
separator (FwkResId (STR_EMDASH_SEPARATOR
));
543 sTitle
.append(separator
);
549 void TitleHelper::impl_appendModuleName (OUStringBuffer
& sTitle
)
551 css::uno::Reference
< css::uno::XInterface
> xOwner
;
552 css::uno::Reference
< css::uno::XComponentContext
> xContext
;
555 osl::MutexGuard
aLock(m_aMutex
);
557 xOwner
= m_xOwner
.get();
558 xContext
= m_xContext
;
564 css::uno::Reference
< css::frame::XModuleManager2
> xModuleManager
=
565 css::frame::ModuleManager::create(xContext
);
567 const OUString sID
= xModuleManager
->identify(xOwner
);
568 ::comphelper::SequenceAsHashMap lProps
= xModuleManager
->getByName (sID
);
569 const OUString sUIName
= lProps
.getUnpackedValueOrDefault (OFFICEFACTORY_PROPNAME_ASCII_UINAME
, OUString());
571 // An UIname property is an optional value !
572 // So please add it to the title in case it does really exists only.
573 if (!sUIName
.isEmpty())
575 sTitle
.append (" " );
576 sTitle
.append (sUIName
);
579 catch(const css::uno::Exception
&)
584 void TitleHelper::impl_appendDebugVersion (OUStringBuffer
& sTitle
)
586 OUString
version(utl::ConfigManager::getProductVersion());
588 sTitle
.append(version
);
589 OUString sVersion
= ::utl::Bootstrap::getBuildIdData("development");
591 sTitle
.append(sVersion
);
595 void TitleHelper::impl_appendDebugVersion (OUStringBuffer
&)
600 void TitleHelper::impl_appendSafeMode (OUStringBuffer
& sTitle
)
602 if (Application::IsSafeModeEnabled())
603 sTitle
.append(FwkResId (STR_SAFEMODE_TITLE
));
606 void TitleHelper::impl_startListeningForModel (const css::uno::Reference
< css::frame::XModel
>& xModel
)
608 css::uno::Reference
< css::document::XDocumentEventBroadcaster
> xBroadcaster(xModel
, css::uno::UNO_QUERY
);
609 if ( ! xBroadcaster
.is ())
612 xBroadcaster
->addDocumentEventListener (static_cast< css::document::XDocumentEventListener
* >(this));
615 void TitleHelper::impl_startListeningForController (const css::uno::Reference
< css::frame::XController
>& xController
)
617 xController
->addEventListener (static_cast< css::lang::XEventListener
* > (static_cast< css::frame::XFrameActionListener
* > (this) ) );
618 css::uno::Reference
< css::frame::XTitle
> xSubTitle(xController
->getModel (), css::uno::UNO_QUERY
);
619 impl_setSubTitle (xSubTitle
);
622 void TitleHelper::impl_startListeningForFrame (const css::uno::Reference
< css::frame::XFrame
>& xFrame
)
624 xFrame
->addFrameActionListener(this );
625 impl_updateListeningForFrame (xFrame
);
628 void TitleHelper::impl_updateListeningForFrame (const css::uno::Reference
< css::frame::XFrame
>& xFrame
)
630 css::uno::Reference
< css::frame::XTitle
> xSubTitle(xFrame
->getController (), css::uno::UNO_QUERY
);
631 impl_setSubTitle (xSubTitle
);
634 void TitleHelper::impl_setSubTitle (const css::uno::Reference
< css::frame::XTitle
>& xSubTitle
)
636 css::uno::Reference
< css::frame::XTitle
> xOldSubTitle
;
639 osl::MutexGuard
aLock(m_aMutex
);
641 // ignore duplicate calls. Makes outside using of this helper more easy :-)
642 xOldSubTitle
.set(m_xSubTitle
.get(), css::uno::UNO_QUERY
);
643 if (xOldSubTitle
== xSubTitle
)
646 m_xSubTitle
= xSubTitle
;
650 css::uno::Reference
< css::frame::XTitleChangeBroadcaster
> xOldBroadcaster(xOldSubTitle
, css::uno::UNO_QUERY
);
651 css::uno::Reference
< css::frame::XTitleChangeBroadcaster
> xNewBroadcaster(xSubTitle
, css::uno::UNO_QUERY
);
652 css::uno::Reference
< css::frame::XTitleChangeListener
> xThis(this);
654 if (xOldBroadcaster
.is())
655 xOldBroadcaster
->removeTitleChangeListener (xThis
);
657 if (xNewBroadcaster
.is())
658 xNewBroadcaster
->addTitleChangeListener (xThis
);
661 OUString
TitleHelper::impl_convertURL2Title(std::u16string_view sURL
)
663 INetURLObject
aURL (sURL
);
666 if (aURL
.GetProtocol() == INetProtocol::File
)
669 aURL
= INetURLObject(aURL
.GetURLNoMark());
671 sTitle
= aURL
.getName(INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
675 if (aURL
.hasExtension())
676 sTitle
= aURL
.getName(INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
678 if ( sTitle
.isEmpty() )
679 sTitle
= aURL
.GetHostPort(INetURLObject::DecodeMechanism::WithCharset
);
681 if ( sTitle
.isEmpty() )
682 sTitle
= aURL
.GetURLNoPass(INetURLObject::DecodeMechanism::WithCharset
);
688 } // namespace framework
690 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */