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 <comphelper/configuration.hxx>
33 #include <unotools/configmgr.hxx>
34 #include <unotools/bootstrap.hxx>
35 #include <unotools/mediadescriptor.hxx>
36 #include <comphelper/sequenceashashmap.hxx>
37 #include <rtl/ustrbuf.hxx>
38 #include <osl/mutex.hxx>
39 #include <tools/urlobj.hxx>
41 #include <vcl/svapp.hxx>
45 using namespace css::uno
;
46 using namespace css::frame
;
50 TitleHelper::TitleHelper(css::uno::Reference
< css::uno::XComponentContext
> xContext
,
51 const css::uno::Reference
< css::uno::XInterface
>& xOwner
,
52 const css::uno::Reference
< css::frame::XUntitledNumbers
>& xNumbers
)
54 m_xContext (std::move(xContext
))
56 , m_xUntitledNumbers(xNumbers
)
57 , m_bExternalTitle (false)
58 , m_nLeasedNumber (css::frame::UntitledNumbersConst::INVALID_NUMBER
)
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 std::unique_lock
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
96 impl_updateTitle (true);
103 void SAL_CALL
TitleHelper::setTitle(const OUString
& sTitle
)
107 std::unique_lock
aLock(m_aMutex
);
109 m_bExternalTitle
= true;
114 impl_sendTitleChangedEvent ();
117 void SAL_CALL
TitleHelper::addTitleChangeListener(const css::uno::Reference
< css::frame::XTitleChangeListener
>& xListener
)
119 std::unique_lock
aLock(m_aMutex
);
120 m_aTitleChangeListeners
.addInterface( aLock
, xListener
);
123 void SAL_CALL
TitleHelper::removeTitleChangeListener(const css::uno::Reference
< css::frame::XTitleChangeListener
>& xListener
)
125 std::unique_lock
aLock(m_aMutex
);
126 m_aTitleChangeListeners
.removeInterface( aLock
, xListener
);
129 void SAL_CALL
TitleHelper::titleChanged(const css::frame::TitleChangedEvent
& aEvent
)
131 css::uno::Reference
< css::frame::XTitle
> xSubTitle
;
134 std::unique_lock
aLock(m_aMutex
);
136 xSubTitle
= m_xSubTitle
;
140 if (aEvent
.Source
!= xSubTitle
)
146 void SAL_CALL
TitleHelper::documentEventOccured(const css::document::DocumentEvent
& aEvent
)
148 if ( ! aEvent
.EventName
.equalsIgnoreAsciiCase("OnSaveAsDone")
149 && ! aEvent
.EventName
.equalsIgnoreAsciiCase("OnModeChanged")
150 && ! aEvent
.EventName
.equalsIgnoreAsciiCase("OnTitleChanged"))
153 css::uno::Reference
< css::frame::XModel
> xOwner
;
156 std::unique_lock
aLock(m_aMutex
);
158 xOwner
.set(m_xOwner
, css::uno::UNO_QUERY
);
162 if (aEvent
.Source
!= xOwner
163 || ((aEvent
.EventName
.equalsIgnoreAsciiCase("OnModeChanged")
164 || aEvent
.EventName
.equalsIgnoreAsciiCase("OnTitleChanged"))
173 void SAL_CALL
TitleHelper::frameAction(const css::frame::FrameActionEvent
& aEvent
)
175 css::uno::Reference
< css::frame::XFrame
> xOwner
;
178 std::unique_lock
aLock(m_aMutex
);
180 xOwner
.set(m_xOwner
, css::uno::UNO_QUERY
);
184 if (aEvent
.Source
!= xOwner
)
187 // we are interested on events only, which must trigger a title bar update
188 // because component was changed.
190 (aEvent
.Action
== css::frame::FrameAction_COMPONENT_ATTACHED
) ||
191 (aEvent
.Action
== css::frame::FrameAction_COMPONENT_REATTACHED
) ||
192 (aEvent
.Action
== css::frame::FrameAction_COMPONENT_DETACHING
)
195 impl_updateListeningForFrame (xOwner
);
200 void SAL_CALL
TitleHelper::disposing(const css::lang::EventObject
& aEvent
)
202 css::uno::Reference
< css::uno::XInterface
> xOwner
;
203 css::uno::Reference
< css::frame::XUntitledNumbers
> xNumbers
;
204 ::sal_Int32 nLeasedNumber
;
207 std::unique_lock
aLock(m_aMutex
);
210 xNumbers
= m_xUntitledNumbers
;
211 nLeasedNumber
= m_nLeasedNumber
;
218 css::uno::Reference
< css::frame::XFrame
> xFrame(xOwner
, css::uno::UNO_QUERY
);
220 xFrame
->removeFrameActionListener(this);
222 if (xOwner
!= aEvent
.Source
)
227 (nLeasedNumber
!= css::frame::UntitledNumbersConst::INVALID_NUMBER
)
229 xNumbers
->releaseNumber (nLeasedNumber
);
233 std::unique_lock
aLock(m_aMutex
);
237 m_nLeasedNumber
= css::frame::UntitledNumbersConst::INVALID_NUMBER
;
242 void TitleHelper::impl_sendTitleChangedEvent ()
244 css::uno::Reference
<css::uno::XInterface
> xOwner
;
248 std::unique_lock
aLock(m_aMutex
);
256 css::frame::TitleChangedEvent
aEvent(xOwner
, sTitle
);
258 if( ! aEvent
.Source
.is() )
261 std::unique_lock
aLock(m_aMutex
);
262 comphelper::OInterfaceIteratorHelper4
pIt( aLock
, m_aTitleChangeListeners
);
263 while ( pIt
.hasMoreElements() )
268 uno::Reference
<css::frame::XTitleChangeListener
> i
= pIt
.next();
269 i
->titleChanged( aEvent
);
271 catch(const css::uno::Exception
&)
281 void TitleHelper::impl_updateTitle (bool init
)
283 css::uno::Reference
<css::uno::XInterface
> xOwner
;
287 std::unique_lock
aLock(m_aMutex
);
293 if (css::uno::Reference
<css::frame::XModel3
> xModel
{ xOwner
, css::uno::UNO_QUERY
})
295 impl_updateTitleForModel (xModel
, init
);
297 else if (css::uno::Reference
<css::frame::XController
> xController
{ xOwner
,
298 css::uno::UNO_QUERY
})
300 impl_updateTitleForController (xController
, init
);
302 else if (css::uno::Reference
<css::frame::XFrame
> xFrame
{ xOwner
, css::uno::UNO_QUERY
})
304 impl_updateTitleForFrame (xFrame
, init
);
308 static OUString
getURLFromModel(const css::uno::Reference
< css::frame::XModel3
>& xModel
)
310 if (css::uno::Reference
<css::frame::XStorable
> xURLProvider
{ xModel
, css::uno::UNO_QUERY
})
311 return xURLProvider
->getLocation();
315 void TitleHelper::impl_updateTitleForModel (const css::uno::Reference
< css::frame::XModel3
>& xModel
, bool init
)
317 css::uno::Reference
< css::uno::XInterface
> xOwner
;
318 css::uno::Reference
< css::frame::XUntitledNumbers
> xNumbers
;
319 ::sal_Int32 nLeasedNumber
;
322 std::unique_lock
aLock(m_aMutex
);
324 // external title won't be updated internally!
325 // It has to be set from outside new.
326 if (m_bExternalTitle
)
330 xNumbers
= m_xUntitledNumbers
;
331 nLeasedNumber
= m_nLeasedNumber
;
337 ( ! xNumbers
.is ()) ||
344 utl::MediaDescriptor
aDescriptor(
345 xModel
->getArgs2({ utl::MediaDescriptor::PROP_DOCUMENTTITLE
,
346 utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME
}));
348 if (const OUString sMediaTitle
= aDescriptor
.getUnpackedValueOrDefault(
349 utl::MediaDescriptor::PROP_DOCUMENTTITLE
, OUString());
350 !sMediaTitle
.isEmpty())
352 sTitle
= sMediaTitle
;
354 else if (const OUString sURL
= getURLFromModel(xModel
); !sURL
.isEmpty())
356 sTitle
= impl_convertURL2Title(sURL
);
357 if (nLeasedNumber
!= css::frame::UntitledNumbersConst::INVALID_NUMBER
)
358 xNumbers
->releaseNumber (nLeasedNumber
);
359 nLeasedNumber
= css::frame::UntitledNumbersConst::INVALID_NUMBER
;
361 else if (const OUString sSuggestedSaveAsName
= aDescriptor
.getUnpackedValueOrDefault(
362 utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME
, OUString());
363 !sSuggestedSaveAsName
.isEmpty())
365 // tdf#121537 Use suggested save as name for title if file has not yet been saved
366 sTitle
= sSuggestedSaveAsName
;
370 if (nLeasedNumber
== css::frame::UntitledNumbersConst::INVALID_NUMBER
)
371 nLeasedNumber
= xNumbers
->leaseNumber (xOwner
);
373 if (nLeasedNumber
!= css::frame::UntitledNumbersConst::INVALID_NUMBER
)
374 sTitle
= xNumbers
->getUntitledPrefix() + OUString::number(nLeasedNumber
);
376 sTitle
= xNumbers
->getUntitledPrefix() + "?";
382 std::unique_lock
aLock(m_aMutex
);
384 // WORKAROUND: the notification is currently sent always,
385 // can be changed after shared mode is supported per UNO API
386 bChanged
= !init
; // && m_sTitle != sTitle
389 m_nLeasedNumber
= nLeasedNumber
;
394 impl_sendTitleChangedEvent ();
397 void TitleHelper::impl_updateTitleForController (const css::uno::Reference
< css::frame::XController
>& xController
, bool init
)
399 css::uno::Reference
< css::uno::XInterface
> xOwner
;
400 css::uno::Reference
< css::frame::XUntitledNumbers
> xNumbers
;
401 ::sal_Int32 nLeasedNumber
;
404 std::unique_lock
aLock(m_aMutex
);
406 // external title won't be updated internally!
407 // It has to be set from outside new.
408 if (m_bExternalTitle
)
412 xNumbers
= m_xUntitledNumbers
;
413 nLeasedNumber
= m_nLeasedNumber
;
419 ( ! xNumbers
.is ()) ||
420 ( ! xController
.is ())
424 OUStringBuffer
sTitle(256);
426 if (nLeasedNumber
== css::frame::UntitledNumbersConst::INVALID_NUMBER
)
427 nLeasedNumber
= xNumbers
->leaseNumber (xOwner
);
429 css::uno::Reference
< css::frame::XTitle
> xModelTitle(xController
->getModel (), css::uno::UNO_QUERY
);
430 css::uno::Reference
< css::frame::XModel
> xModel
= xController
->getModel ();
431 if (!xModelTitle
.is ())
432 xModelTitle
.set(xController
, css::uno::UNO_QUERY
);
433 if (xModelTitle
.is ())
435 sTitle
.append (xModelTitle
->getTitle ());
436 if ( nLeasedNumber
> 1 )
438 sTitle
.append(" : " + OUString::number(nLeasedNumber
));
442 INetURLObject
aURL (xModel
->getURL ());
443 if (aURL
.GetProtocol () != INetProtocol::File
444 && aURL
.GetProtocol() != INetProtocol::PrivSoffice
445 && aURL
.GetProtocol () != INetProtocol::NotValid
)
447 OUString
sRemoteText (FwkResId (STR_REMOTE_TITLE
));
448 sTitle
.append (sRemoteText
);
454 sTitle
.append (xNumbers
->getUntitledPrefix ());
455 if ( nLeasedNumber
> 1 )
457 sTitle
.append(nLeasedNumber
);
464 std::unique_lock
aLock(m_aMutex
);
466 OUString sNewTitle
= sTitle
.makeStringAndClear ();
467 bChanged
= !init
&& m_sTitle
!= sNewTitle
;
468 m_sTitle
= sNewTitle
;
469 m_nLeasedNumber
= nLeasedNumber
;
474 impl_sendTitleChangedEvent ();
477 void TitleHelper::impl_updateTitleForFrame (const css::uno::Reference
< css::frame::XFrame
>& xFrame
, bool init
)
484 std::unique_lock
aLock(m_aMutex
);
486 // external title won't be updated internally!
487 // It has to be set from outside new.
488 if (m_bExternalTitle
)
493 css::uno::Reference
< css::uno::XInterface
> xComponent
= xFrame
->getController ();
494 if ( ! xComponent
.is ())
495 xComponent
= xFrame
->getComponentWindow ();
497 OUStringBuffer
sTitle (256);
499 impl_appendComponentTitle (sTitle
, xComponent
);
501 if (!comphelper::IsFuzzing())
503 // fdo#70376: We want the window title to contain just the
504 // document name (from the above "component title").
505 impl_appendProductName (sTitle
);
506 impl_appendModuleName (sTitle
);
507 impl_appendDebugVersion (sTitle
);
510 impl_appendSafeMode (sTitle
);
515 std::unique_lock
aLock(m_aMutex
);
517 OUString sNewTitle
= sTitle
.makeStringAndClear ();
518 bChanged
= !init
&& m_sTitle
!= sNewTitle
;
519 m_sTitle
= sNewTitle
;
524 impl_sendTitleChangedEvent ();
528 void TitleHelper::impl_appendComponentTitle ( OUStringBuffer
& sTitle
,
529 const css::uno::Reference
< css::uno::XInterface
>& xComponent
)
531 css::uno::Reference
< css::frame::XTitle
> xTitle(xComponent
, css::uno::UNO_QUERY
);
533 // Note: Title has to be used (even if it's empty) if the right interface is supported.
535 sTitle
.append (xTitle
->getTitle ());
539 void TitleHelper::impl_appendProductName (OUStringBuffer
& sTitle
)
541 OUString
name(utl::ConfigManager::getProductName());
544 if (!sTitle
.isEmpty())
546 OUString
separator (FwkResId (STR_EMDASH_SEPARATOR
));
547 sTitle
.append(separator
);
553 void TitleHelper::impl_appendModuleName (OUStringBuffer
& sTitle
)
555 css::uno::Reference
< css::uno::XInterface
> xOwner
;
556 css::uno::Reference
< css::uno::XComponentContext
> xContext
;
559 std::unique_lock
aLock(m_aMutex
);
562 xContext
= m_xContext
;
568 css::uno::Reference
< css::frame::XModuleManager2
> xModuleManager
=
569 css::frame::ModuleManager::create(xContext
);
571 const OUString sID
= xModuleManager
->identify(xOwner
);
572 ::comphelper::SequenceAsHashMap lProps
= xModuleManager
->getByName (sID
);
573 const OUString sUIName
= lProps
.getUnpackedValueOrDefault (OFFICEFACTORY_PROPNAME_ASCII_UINAME
, OUString());
575 // An UIname property is an optional value !
576 // So please add it to the title in case it does really exists only.
577 if (!sUIName
.isEmpty())
579 sTitle
.append (" " );
580 sTitle
.append (sUIName
);
583 catch(const css::uno::Exception
&)
589 void TitleHelper::impl_appendDebugVersion (OUStringBuffer
& sTitle
)
591 OUString
version(utl::ConfigManager::getProductVersion());
593 sTitle
.append(version
);
594 OUString sVersion
= ::utl::Bootstrap::getBuildIdData(u
"development"_ustr
);
596 sTitle
.append(sVersion
);
600 void TitleHelper::impl_appendDebugVersion (OUStringBuffer
&)
606 void TitleHelper::impl_appendSafeMode (OUStringBuffer
& sTitle
)
608 if (Application::IsSafeModeEnabled())
609 sTitle
.append(FwkResId (STR_SAFEMODE_TITLE
));
612 void TitleHelper::impl_startListeningForModel (const css::uno::Reference
< css::frame::XModel
>& xModel
)
614 css::uno::Reference
< css::document::XDocumentEventBroadcaster
> xBroadcaster(xModel
, css::uno::UNO_QUERY
);
615 if ( ! xBroadcaster
.is ())
618 xBroadcaster
->addDocumentEventListener (static_cast< css::document::XDocumentEventListener
* >(this));
621 void TitleHelper::impl_startListeningForController (const css::uno::Reference
< css::frame::XController
>& xController
)
623 xController
->addEventListener (static_cast< css::lang::XEventListener
* > (static_cast< css::frame::XFrameActionListener
* > (this) ) );
624 css::uno::Reference
< css::frame::XTitle
> xSubTitle(xController
->getModel (), css::uno::UNO_QUERY
);
625 impl_setSubTitle (xSubTitle
);
628 void TitleHelper::impl_startListeningForFrame (const css::uno::Reference
< css::frame::XFrame
>& xFrame
)
630 xFrame
->addFrameActionListener(this );
631 impl_updateListeningForFrame (xFrame
);
634 void TitleHelper::impl_updateListeningForFrame (const css::uno::Reference
< css::frame::XFrame
>& xFrame
)
636 css::uno::Reference
< css::frame::XTitle
> xSubTitle(xFrame
->getController (), css::uno::UNO_QUERY
);
637 impl_setSubTitle (xSubTitle
);
640 void TitleHelper::impl_setSubTitle (const css::uno::Reference
< css::frame::XTitle
>& xSubTitle
)
642 css::uno::Reference
< css::frame::XTitle
> xOldSubTitle
;
645 std::unique_lock
aLock(m_aMutex
);
647 // ignore duplicate calls. Makes outside using of this helper more easy :-)
648 xOldSubTitle
= m_xSubTitle
;
649 if (xOldSubTitle
== xSubTitle
)
652 m_xSubTitle
= xSubTitle
;
656 css::uno::Reference
< css::frame::XTitleChangeBroadcaster
> xOldBroadcaster(xOldSubTitle
, css::uno::UNO_QUERY
);
657 css::uno::Reference
< css::frame::XTitleChangeBroadcaster
> xNewBroadcaster(xSubTitle
, css::uno::UNO_QUERY
);
658 css::uno::Reference
< css::frame::XTitleChangeListener
> xThis(this);
660 if (xOldBroadcaster
.is())
661 xOldBroadcaster
->removeTitleChangeListener (xThis
);
663 if (xNewBroadcaster
.is())
664 xNewBroadcaster
->addTitleChangeListener (xThis
);
668 OUString
TitleHelper::impl_convertURL2Title(std::u16string_view sURL
)
670 INetURLObject
aURL (sURL
);
673 if (aURL
.GetProtocol() == INetProtocol::File
)
676 aURL
= INetURLObject(aURL
.GetURLNoMark());
678 sTitle
= aURL
.getName(INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
682 if (aURL
.hasExtension())
683 sTitle
= aURL
.getName(INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
685 if ( sTitle
.isEmpty() )
686 sTitle
= aURL
.GetHostPort(INetURLObject::DecodeMechanism::WithCharset
);
688 if ( sTitle
.isEmpty() )
689 sTitle
= aURL
.GetURLNoPass(INetURLObject::DecodeMechanism::WithCharset
);
695 } // namespace framework
697 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */