Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / framework / source / fwe / helper / titlehelper.cxx
blobbe779736e0f7b29270f9a66265b99a67f9ad12aa
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
39 #include <utility>
40 #include <vcl/svapp.hxx>
43 using namespace css;
44 using namespace css::uno;
45 using namespace css::frame;
47 namespace framework{
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)
58 // SYNCHRONIZED ->
60 osl::MutexGuard aLock(m_aMutex);
62 m_xOwner = xOwner;
63 m_xUntitledNumbers = xNumbers;
65 // <- SYNCHRONIZED
67 css::uno::Reference< css::frame::XModel > xModel(xOwner, css::uno::UNO_QUERY);
68 if (xModel.is ())
70 impl_startListeningForModel (xModel);
71 return;
74 css::uno::Reference< css::frame::XController > xController(xOwner, css::uno::UNO_QUERY);
75 if (xController.is ())
77 impl_startListeningForController (xController);
78 return;
81 css::uno::Reference< css::frame::XFrame > xFrame(xOwner, css::uno::UNO_QUERY);
82 if (xFrame.is ())
84 impl_startListeningForFrame (xFrame);
85 return;
89 TitleHelper::~TitleHelper()
93 OUString SAL_CALL TitleHelper::getTitle()
95 // SYNCHRONIZED ->
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)
102 return m_sTitle;
104 // Title seems to be up-to-date. Return it directly.
105 if (!m_sTitle.isEmpty())
106 return m_sTitle;
108 // Title seems to be unused till now ... do bootstrapping
109 impl_updateTitle (true);
111 return m_sTitle;
112 // <- SYNCHRONIZED
115 void SAL_CALL TitleHelper::setTitle(const OUString& sTitle)
117 // SYNCHRONIZED ->
119 osl::MutexGuard aLock(m_aMutex);
121 m_bExternalTitle = true;
122 m_sTitle = sTitle;
124 // <- SYNCHRONIZED
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;
144 // SYNCHRONIZED ->
146 osl::MutexGuard aLock(m_aMutex);
148 xSubTitle.set(m_xSubTitle.get (), css::uno::UNO_QUERY);
150 // <- SYNCHRONIZED
152 if (aEvent.Source != xSubTitle)
153 return;
155 impl_updateTitle ();
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"))
163 return;
165 css::uno::Reference< css::frame::XModel > xOwner;
166 // SYNCHRONIZED ->
168 osl::MutexGuard aLock(m_aMutex);
170 xOwner.set(m_xOwner.get (), css::uno::UNO_QUERY);
172 // <- SYNCHRONIZED
174 if (aEvent.Source != xOwner
175 || ((aEvent.EventName.equalsIgnoreAsciiCase("OnModeChanged")
176 || aEvent.EventName.equalsIgnoreAsciiCase("OnTitleChanged"))
177 && !xOwner.is()))
179 return;
182 impl_updateTitle ();
185 void SAL_CALL TitleHelper::frameAction(const css::frame::FrameActionEvent& aEvent)
187 css::uno::Reference< css::frame::XFrame > xOwner;
188 // SYNCHRONIZED ->
190 osl::MutexGuard aLock(m_aMutex);
192 xOwner.set(m_xOwner.get (), css::uno::UNO_QUERY);
194 // <- SYNCHRONIZED
196 if (aEvent.Source != xOwner)
197 return;
199 // we are interested on events only, which must trigger a title bar update
200 // because component was changed.
201 if (
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);
208 impl_updateTitle ();
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;
217 // SYNCHRONIZED ->
219 osl::MutexGuard aLock(m_aMutex);
221 xOwner = m_xOwner;
222 xNumbers.set(m_xUntitledNumbers.get(), css::uno::UNO_QUERY);
223 nLeasedNumber = m_nLeasedNumber;
225 // <- SYNCHRONIZED
227 if ( ! xOwner.is ())
228 return;
230 css::uno::Reference< css::frame::XFrame > xFrame(xOwner, css::uno::UNO_QUERY);
231 if (xFrame.is())
232 xFrame->removeFrameActionListener(this);
234 if (xOwner != aEvent.Source)
235 return;
237 if (
238 (xNumbers.is () ) &&
239 (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER)
241 xNumbers->releaseNumber (nLeasedNumber);
243 // SYNCHRONIZED ->
245 osl::MutexGuard aLock(m_aMutex);
247 m_xOwner.clear();
248 m_sTitle = OUString ();
249 m_nLeasedNumber = css::frame::UntitledNumbersConst::INVALID_NUMBER;
251 // <- SYNCHRONIZED
254 void TitleHelper::impl_sendTitleChangedEvent ()
256 css::uno::Reference<css::uno::XInterface> xOwner;
257 // SYNCHRONIZED ->
259 osl::MutexGuard aLock(m_aMutex);
261 xOwner = m_xOwner;
263 // <- SYNCHRONIZED
265 css::frame::TitleChangedEvent aEvent(xOwner, m_sTitle);
267 if( ! aEvent.Source.is() )
268 return;
270 comphelper::OInterfaceContainerHelper2* pContainer = m_aListener.getContainer( cppu::UnoType<css::frame::XTitleChangeListener>::get());
271 if ( ! pContainer)
272 return;
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&)
283 pIt.remove();
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;
293 // SYNCHRONIZED ->
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);
301 // <- SYNCHRONIZED
303 if (xModel.is ())
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;
322 // SYNCHRONIZED ->
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)
329 return;
331 xOwner = m_xOwner;
332 xNumbers.set (m_xUntitledNumbers.get(), css::uno::UNO_QUERY);
333 nLeasedNumber = m_nLeasedNumber;
335 // <- SYNCHRONIZED
337 if (
338 ( ! xOwner.is ()) ||
339 ( ! xNumbers.is ()) ||
340 ( ! xModel.is ())
342 return;
344 OUString sTitle;
345 OUString sURL;
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());
355 if (!sURL.isEmpty())
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;
367 else
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);
374 else
375 sTitle = xNumbers->getUntitledPrefix() + "?";
378 bool bChanged;
379 // SYNCHRONIZED ->
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
387 m_sTitle = sTitle;
388 m_nLeasedNumber = nLeasedNumber;
390 // <- SYNCHRONIZED
392 if (bChanged)
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;
401 // SYNCHRONIZED ->
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)
408 return;
410 xOwner = m_xOwner;
411 xNumbers.set (m_xUntitledNumbers.get(), css::uno::UNO_QUERY);
412 nLeasedNumber = m_nLeasedNumber;
414 // <- SYNCHRONIZED
416 if (
417 ( ! xOwner.is ()) ||
418 ( ! xNumbers.is ()) ||
419 ( ! xController.is ())
421 return;
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));
439 if (xModel.is ())
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);
450 else
452 sTitle.append (xNumbers->getUntitledPrefix ());
453 if ( nLeasedNumber > 1 )
455 sTitle.append(nLeasedNumber );
459 bool bChanged;
460 // SYNCHRONIZED ->
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;
469 // <- SYNCHRONIZED
471 if (bChanged)
472 impl_sendTitleChangedEvent ();
475 void TitleHelper::impl_updateTitleForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame, bool init)
477 if ( ! xFrame.is ())
478 return;
480 // SYNCHRONIZED ->
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)
487 return;
489 // <- SYNCHRONIZED
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);
498 #ifndef MACOSX
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);
507 #endif
508 impl_appendSafeMode (sTitle);
510 bool bChanged;
511 // SYNCHRONIZED ->
513 osl::MutexGuard aLock(m_aMutex);
515 OUString sNewTitle = sTitle.makeStringAndClear ();
516 bChanged = !init && m_sTitle != sNewTitle;
517 m_sTitle = sNewTitle;
519 // <- SYNCHRONIZED
521 if (bChanged)
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.
531 if (xTitle.is ())
532 sTitle.append (xTitle->getTitle ());
535 void TitleHelper::impl_appendProductName (OUStringBuffer& sTitle)
537 OUString name(utl::ConfigManager::getProductName());
538 if (!name.isEmpty())
540 if (!sTitle.isEmpty())
542 OUString separator (FwkResId (STR_EMDASH_SEPARATOR));
543 sTitle.append(separator);
545 sTitle.append(name);
549 void TitleHelper::impl_appendModuleName (OUStringBuffer& sTitle)
551 css::uno::Reference< css::uno::XInterface > xOwner;
552 css::uno::Reference< css::uno::XComponentContext > xContext;
553 // SYNCHRONIZED ->
555 osl::MutexGuard aLock(m_aMutex);
557 xOwner = m_xOwner.get();
558 xContext = m_xContext;
560 // <- SYNCHRONIZED
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&)
583 #ifdef DBG_UTIL
584 void TitleHelper::impl_appendDebugVersion (OUStringBuffer& sTitle)
586 OUString version(utl::ConfigManager::getProductVersion());
587 sTitle.append(' ');
588 sTitle.append(version);
589 OUString sVersion = ::utl::Bootstrap::getBuildIdData("development");
590 sTitle.append(" [");
591 sTitle.append(sVersion);
592 sTitle.append("]");
594 #else
595 void TitleHelper::impl_appendDebugVersion (OUStringBuffer&)
598 #endif
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 ())
610 return;
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;
637 // SYNCHRONIZED ->
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)
644 return;
646 m_xSubTitle = xSubTitle;
648 // <- SYNCHRONIZED
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);
664 OUString sTitle;
666 if (aURL.GetProtocol() == INetProtocol::File)
668 if (aURL.HasMark())
669 aURL = INetURLObject(aURL.GetURLNoMark());
671 sTitle = aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset);
673 else
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);
685 return sTitle;
688 } // namespace framework
690 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */