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
))
55 , m_xUntitledNumbers(xNumbers
)
56 , m_bExternalTitle (false)
57 , m_nLeasedNumber (css::frame::UntitledNumbersConst::INVALID_NUMBER
)
58 , m_aListener (m_aMutex
)
60 if (css::uno::Reference
<css::frame::XModel
> xModel
{ xOwner
, css::uno::UNO_QUERY
})
62 impl_startListeningForModel (xModel
);
64 else if (css::uno::Reference
<css::frame::XController
> xController
{ xOwner
,
65 css::uno::UNO_QUERY
})
67 impl_startListeningForController (xController
);
69 else if (css::uno::Reference
<css::frame::XFrame
> xFrame
{ xOwner
, css::uno::UNO_QUERY
})
71 impl_startListeningForFrame (xFrame
);
75 TitleHelper::~TitleHelper()
79 OUString SAL_CALL
TitleHelper::getTitle()
82 osl::MutexGuard
aLock(m_aMutex
);
84 // An external title will win always and disable all internal logic about
85 // creating/using a title value.
86 // Even an empty string will be accepted as valid title !
90 // Title seems to be up-to-date. Return it directly.
91 if (!m_sTitle
.isEmpty())
94 // Title seems to be unused till now ... do bootstrapping
95 impl_updateTitle (true);
101 void SAL_CALL
TitleHelper::setTitle(const OUString
& sTitle
)
105 osl::MutexGuard
aLock(m_aMutex
);
107 m_bExternalTitle
= true;
112 impl_sendTitleChangedEvent ();
115 void SAL_CALL
TitleHelper::addTitleChangeListener(const css::uno::Reference
< css::frame::XTitleChangeListener
>& xListener
)
117 // container is threadsafe by himself
118 m_aListener
.addInterface( cppu::UnoType
<css::frame::XTitleChangeListener
>::get(), xListener
);
121 void SAL_CALL
TitleHelper::removeTitleChangeListener(const css::uno::Reference
< css::frame::XTitleChangeListener
>& xListener
)
123 // container is threadsafe by himself
124 m_aListener
.removeInterface( cppu::UnoType
<css::frame::XTitleChangeListener
>::get(), xListener
);
127 void SAL_CALL
TitleHelper::titleChanged(const css::frame::TitleChangedEvent
& aEvent
)
129 css::uno::Reference
< css::frame::XTitle
> xSubTitle
;
132 osl::MutexGuard
aLock(m_aMutex
);
134 xSubTitle
= m_xSubTitle
;
138 if (aEvent
.Source
!= xSubTitle
)
144 void SAL_CALL
TitleHelper::documentEventOccured(const css::document::DocumentEvent
& aEvent
)
146 if ( ! aEvent
.EventName
.equalsIgnoreAsciiCase("OnSaveAsDone")
147 && ! aEvent
.EventName
.equalsIgnoreAsciiCase("OnModeChanged")
148 && ! aEvent
.EventName
.equalsIgnoreAsciiCase("OnTitleChanged"))
151 css::uno::Reference
< css::frame::XModel
> xOwner
;
154 osl::MutexGuard
aLock(m_aMutex
);
156 xOwner
.set(m_xOwner
, css::uno::UNO_QUERY
);
160 if (aEvent
.Source
!= xOwner
161 || ((aEvent
.EventName
.equalsIgnoreAsciiCase("OnModeChanged")
162 || aEvent
.EventName
.equalsIgnoreAsciiCase("OnTitleChanged"))
171 void SAL_CALL
TitleHelper::frameAction(const css::frame::FrameActionEvent
& aEvent
)
173 css::uno::Reference
< css::frame::XFrame
> xOwner
;
176 osl::MutexGuard
aLock(m_aMutex
);
178 xOwner
.set(m_xOwner
, css::uno::UNO_QUERY
);
182 if (aEvent
.Source
!= xOwner
)
185 // we are interested on events only, which must trigger a title bar update
186 // because component was changed.
188 (aEvent
.Action
== css::frame::FrameAction_COMPONENT_ATTACHED
) ||
189 (aEvent
.Action
== css::frame::FrameAction_COMPONENT_REATTACHED
) ||
190 (aEvent
.Action
== css::frame::FrameAction_COMPONENT_DETACHING
)
193 impl_updateListeningForFrame (xOwner
);
198 void SAL_CALL
TitleHelper::disposing(const css::lang::EventObject
& aEvent
)
200 css::uno::Reference
< css::uno::XInterface
> xOwner
;
201 css::uno::Reference
< css::frame::XUntitledNumbers
> xNumbers
;
202 ::sal_Int32 nLeasedNumber
;
205 osl::MutexGuard
aLock(m_aMutex
);
208 xNumbers
= m_xUntitledNumbers
;
209 nLeasedNumber
= m_nLeasedNumber
;
216 css::uno::Reference
< css::frame::XFrame
> xFrame(xOwner
, css::uno::UNO_QUERY
);
218 xFrame
->removeFrameActionListener(this);
220 if (xOwner
!= aEvent
.Source
)
225 (nLeasedNumber
!= css::frame::UntitledNumbersConst::INVALID_NUMBER
)
227 xNumbers
->releaseNumber (nLeasedNumber
);
231 osl::MutexGuard
aLock(m_aMutex
);
235 m_nLeasedNumber
= css::frame::UntitledNumbersConst::INVALID_NUMBER
;
240 void TitleHelper::impl_sendTitleChangedEvent ()
242 css::uno::Reference
<css::uno::XInterface
> xOwner
;
245 osl::MutexGuard
aLock(m_aMutex
);
251 css::frame::TitleChangedEvent
aEvent(xOwner
, m_sTitle
);
253 if( ! aEvent
.Source
.is() )
256 comphelper::OInterfaceContainerHelper2
* pContainer
= m_aListener
.getContainer( cppu::UnoType
<css::frame::XTitleChangeListener
>::get());
260 comphelper::OInterfaceIteratorHelper2
pIt( *pContainer
);
261 while ( pIt
.hasMoreElements() )
265 static_cast<css::frame::XTitleChangeListener
*>(pIt
.next())->titleChanged( aEvent
);
267 catch(const css::uno::Exception
&)
274 void TitleHelper::impl_updateTitle (bool init
)
276 css::uno::Reference
<css::uno::XInterface
> xOwner
;
280 osl::MutexGuard
aLock(m_aMutex
);
286 if (css::uno::Reference
<css::frame::XModel3
> xModel
{ xOwner
, css::uno::UNO_QUERY
})
288 impl_updateTitleForModel (xModel
, init
);
290 else if (css::uno::Reference
<css::frame::XController
> xController
{ xOwner
,
291 css::uno::UNO_QUERY
})
293 impl_updateTitleForController (xController
, init
);
295 else if (css::uno::Reference
<css::frame::XFrame
> xFrame
{ xOwner
, css::uno::UNO_QUERY
})
297 impl_updateTitleForFrame (xFrame
, init
);
301 static OUString
getURLFromModel(const css::uno::Reference
< css::frame::XModel3
>& xModel
)
303 if (css::uno::Reference
<css::frame::XStorable
> xURLProvider
{ xModel
, css::uno::UNO_QUERY
})
304 return xURLProvider
->getLocation();
308 void TitleHelper::impl_updateTitleForModel (const css::uno::Reference
< css::frame::XModel3
>& xModel
, bool init
)
310 css::uno::Reference
< css::uno::XInterface
> xOwner
;
311 css::uno::Reference
< css::frame::XUntitledNumbers
> xNumbers
;
312 ::sal_Int32 nLeasedNumber
;
315 osl::MutexGuard
aLock(m_aMutex
);
317 // external title won't be updated internally!
318 // It has to be set from outside new.
319 if (m_bExternalTitle
)
323 xNumbers
= m_xUntitledNumbers
;
324 nLeasedNumber
= m_nLeasedNumber
;
330 ( ! xNumbers
.is ()) ||
337 utl::MediaDescriptor
aDescriptor(
338 xModel
->getArgs2({ utl::MediaDescriptor::PROP_DOCUMENTTITLE
,
339 utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME
}));
341 if (const OUString sMediaTitle
= aDescriptor
.getUnpackedValueOrDefault(
342 utl::MediaDescriptor::PROP_DOCUMENTTITLE
, OUString());
343 !sMediaTitle
.isEmpty())
345 sTitle
= sMediaTitle
;
347 else if (const OUString sURL
= getURLFromModel(xModel
); !sURL
.isEmpty())
349 sTitle
= impl_convertURL2Title(sURL
);
350 if (nLeasedNumber
!= css::frame::UntitledNumbersConst::INVALID_NUMBER
)
351 xNumbers
->releaseNumber (nLeasedNumber
);
352 nLeasedNumber
= css::frame::UntitledNumbersConst::INVALID_NUMBER
;
354 else if (const OUString sSuggestedSaveAsName
= aDescriptor
.getUnpackedValueOrDefault(
355 utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME
, OUString());
356 !sSuggestedSaveAsName
.isEmpty())
358 // tdf#121537 Use suggested save as name for title if file has not yet been saved
359 sTitle
= sSuggestedSaveAsName
;
363 if (nLeasedNumber
== css::frame::UntitledNumbersConst::INVALID_NUMBER
)
364 nLeasedNumber
= xNumbers
->leaseNumber (xOwner
);
366 if (nLeasedNumber
!= css::frame::UntitledNumbersConst::INVALID_NUMBER
)
367 sTitle
= xNumbers
->getUntitledPrefix() + OUString::number(nLeasedNumber
);
369 sTitle
= xNumbers
->getUntitledPrefix() + "?";
375 osl::MutexGuard
aLock(m_aMutex
);
377 // WORKAROUND: the notification is currently sent always,
378 // can be changed after shared mode is supported per UNO API
379 bChanged
= !init
; // && m_sTitle != sTitle
382 m_nLeasedNumber
= nLeasedNumber
;
387 impl_sendTitleChangedEvent ();
390 void TitleHelper::impl_updateTitleForController (const css::uno::Reference
< css::frame::XController
>& xController
, bool init
)
392 css::uno::Reference
< css::uno::XInterface
> xOwner
;
393 css::uno::Reference
< css::frame::XUntitledNumbers
> xNumbers
;
394 ::sal_Int32 nLeasedNumber
;
397 osl::MutexGuard
aLock(m_aMutex
);
399 // external title won't be updated internally!
400 // It has to be set from outside new.
401 if (m_bExternalTitle
)
405 xNumbers
= m_xUntitledNumbers
;
406 nLeasedNumber
= m_nLeasedNumber
;
412 ( ! xNumbers
.is ()) ||
413 ( ! xController
.is ())
417 OUStringBuffer
sTitle(256);
419 if (nLeasedNumber
== css::frame::UntitledNumbersConst::INVALID_NUMBER
)
420 nLeasedNumber
= xNumbers
->leaseNumber (xOwner
);
422 css::uno::Reference
< css::frame::XTitle
> xModelTitle(xController
->getModel (), css::uno::UNO_QUERY
);
423 css::uno::Reference
< css::frame::XModel
> xModel
= xController
->getModel ();
424 if (!xModelTitle
.is ())
425 xModelTitle
.set(xController
, css::uno::UNO_QUERY
);
426 if (xModelTitle
.is ())
428 sTitle
.append (xModelTitle
->getTitle ());
429 if ( nLeasedNumber
> 1 )
431 sTitle
.append(" : " + OUString::number(nLeasedNumber
));
435 INetURLObject
aURL (xModel
->getURL ());
436 if (aURL
.GetProtocol () != INetProtocol::File
437 && aURL
.GetProtocol () != INetProtocol::NotValid
)
439 OUString
sRemoteText (FwkResId (STR_REMOTE_TITLE
));
440 sTitle
.append (sRemoteText
);
446 sTitle
.append (xNumbers
->getUntitledPrefix ());
447 if ( nLeasedNumber
> 1 )
449 sTitle
.append(nLeasedNumber
);
456 osl::MutexGuard
aLock(m_aMutex
);
458 OUString sNewTitle
= sTitle
.makeStringAndClear ();
459 bChanged
= !init
&& m_sTitle
!= sNewTitle
;
460 m_sTitle
= sNewTitle
;
461 m_nLeasedNumber
= nLeasedNumber
;
466 impl_sendTitleChangedEvent ();
469 void TitleHelper::impl_updateTitleForFrame (const css::uno::Reference
< css::frame::XFrame
>& xFrame
, bool init
)
476 osl::MutexGuard
aLock(m_aMutex
);
478 // external title won't be updated internally!
479 // It has to be set from outside new.
480 if (m_bExternalTitle
)
485 css::uno::Reference
< css::uno::XInterface
> xComponent
= xFrame
->getController ();
486 if ( ! xComponent
.is ())
487 xComponent
= xFrame
->getComponentWindow ();
489 OUStringBuffer
sTitle (256);
491 impl_appendComponentTitle (sTitle
, xComponent
);
493 if (!utl::ConfigManager::IsFuzzing())
495 // fdo#70376: We want the window title to contain just the
496 // document name (from the above "component title").
497 impl_appendProductName (sTitle
);
498 impl_appendModuleName (sTitle
);
499 impl_appendDebugVersion (sTitle
);
502 impl_appendSafeMode (sTitle
);
507 osl::MutexGuard
aLock(m_aMutex
);
509 OUString sNewTitle
= sTitle
.makeStringAndClear ();
510 bChanged
= !init
&& m_sTitle
!= sNewTitle
;
511 m_sTitle
= sNewTitle
;
516 impl_sendTitleChangedEvent ();
519 void TitleHelper::impl_appendComponentTitle ( OUStringBuffer
& sTitle
,
520 const css::uno::Reference
< css::uno::XInterface
>& xComponent
)
522 css::uno::Reference
< css::frame::XTitle
> xTitle(xComponent
, css::uno::UNO_QUERY
);
524 // Note: Title has to be used (even if it's empty) if the right interface is supported.
526 sTitle
.append (xTitle
->getTitle ());
529 void TitleHelper::impl_appendProductName (OUStringBuffer
& sTitle
)
531 OUString
name(utl::ConfigManager::getProductName());
534 if (!sTitle
.isEmpty())
536 OUString
separator (FwkResId (STR_EMDASH_SEPARATOR
));
537 sTitle
.append(separator
);
543 void TitleHelper::impl_appendModuleName (OUStringBuffer
& sTitle
)
545 css::uno::Reference
< css::uno::XInterface
> xOwner
;
546 css::uno::Reference
< css::uno::XComponentContext
> xContext
;
549 osl::MutexGuard
aLock(m_aMutex
);
552 xContext
= m_xContext
;
558 css::uno::Reference
< css::frame::XModuleManager2
> xModuleManager
=
559 css::frame::ModuleManager::create(xContext
);
561 const OUString sID
= xModuleManager
->identify(xOwner
);
562 ::comphelper::SequenceAsHashMap lProps
= xModuleManager
->getByName (sID
);
563 const OUString sUIName
= lProps
.getUnpackedValueOrDefault (OFFICEFACTORY_PROPNAME_ASCII_UINAME
, OUString());
565 // An UIname property is an optional value !
566 // So please add it to the title in case it does really exists only.
567 if (!sUIName
.isEmpty())
569 sTitle
.append (" " );
570 sTitle
.append (sUIName
);
573 catch(const css::uno::Exception
&)
578 void TitleHelper::impl_appendDebugVersion (OUStringBuffer
& sTitle
)
580 OUString
version(utl::ConfigManager::getProductVersion());
582 sTitle
.append(version
);
583 OUString sVersion
= ::utl::Bootstrap::getBuildIdData("development");
585 sTitle
.append(sVersion
);
589 void TitleHelper::impl_appendDebugVersion (OUStringBuffer
&)
594 void TitleHelper::impl_appendSafeMode (OUStringBuffer
& sTitle
)
596 if (Application::IsSafeModeEnabled())
597 sTitle
.append(FwkResId (STR_SAFEMODE_TITLE
));
600 void TitleHelper::impl_startListeningForModel (const css::uno::Reference
< css::frame::XModel
>& xModel
)
602 css::uno::Reference
< css::document::XDocumentEventBroadcaster
> xBroadcaster(xModel
, css::uno::UNO_QUERY
);
603 if ( ! xBroadcaster
.is ())
606 xBroadcaster
->addDocumentEventListener (static_cast< css::document::XDocumentEventListener
* >(this));
609 void TitleHelper::impl_startListeningForController (const css::uno::Reference
< css::frame::XController
>& xController
)
611 xController
->addEventListener (static_cast< css::lang::XEventListener
* > (static_cast< css::frame::XFrameActionListener
* > (this) ) );
612 css::uno::Reference
< css::frame::XTitle
> xSubTitle(xController
->getModel (), css::uno::UNO_QUERY
);
613 impl_setSubTitle (xSubTitle
);
616 void TitleHelper::impl_startListeningForFrame (const css::uno::Reference
< css::frame::XFrame
>& xFrame
)
618 xFrame
->addFrameActionListener(this );
619 impl_updateListeningForFrame (xFrame
);
622 void TitleHelper::impl_updateListeningForFrame (const css::uno::Reference
< css::frame::XFrame
>& xFrame
)
624 css::uno::Reference
< css::frame::XTitle
> xSubTitle(xFrame
->getController (), css::uno::UNO_QUERY
);
625 impl_setSubTitle (xSubTitle
);
628 void TitleHelper::impl_setSubTitle (const css::uno::Reference
< css::frame::XTitle
>& xSubTitle
)
630 css::uno::Reference
< css::frame::XTitle
> xOldSubTitle
;
633 osl::MutexGuard
aLock(m_aMutex
);
635 // ignore duplicate calls. Makes outside using of this helper more easy :-)
636 xOldSubTitle
= m_xSubTitle
;
637 if (xOldSubTitle
== xSubTitle
)
640 m_xSubTitle
= xSubTitle
;
644 css::uno::Reference
< css::frame::XTitleChangeBroadcaster
> xOldBroadcaster(xOldSubTitle
, css::uno::UNO_QUERY
);
645 css::uno::Reference
< css::frame::XTitleChangeBroadcaster
> xNewBroadcaster(xSubTitle
, css::uno::UNO_QUERY
);
646 css::uno::Reference
< css::frame::XTitleChangeListener
> xThis(this);
648 if (xOldBroadcaster
.is())
649 xOldBroadcaster
->removeTitleChangeListener (xThis
);
651 if (xNewBroadcaster
.is())
652 xNewBroadcaster
->addTitleChangeListener (xThis
);
655 OUString
TitleHelper::impl_convertURL2Title(std::u16string_view sURL
)
657 INetURLObject
aURL (sURL
);
660 if (aURL
.GetProtocol() == INetProtocol::File
)
663 aURL
= INetURLObject(aURL
.GetURLNoMark());
665 sTitle
= aURL
.getName(INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
669 if (aURL
.hasExtension())
670 sTitle
= aURL
.getName(INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
672 if ( sTitle
.isEmpty() )
673 sTitle
= aURL
.GetHostPort(INetURLObject::DecodeMechanism::WithCharset
);
675 if ( sTitle
.isEmpty() )
676 sTitle
= aURL
.GetURLNoPass(INetURLObject::DecodeMechanism::WithCharset
);
682 } // namespace framework
684 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */