Avoid potential negative array index access to cached text.
[LibreOffice.git] / framework / source / fwe / helper / titlehelper.cxx
blob5ab03ab10e8f53d5c72c07aa6030997f308ecbb6
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_xOwner (xOwner)
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()
81 // SYNCHRONIZED ->
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 !
87 if (m_bExternalTitle)
88 return m_sTitle;
90 // Title seems to be up-to-date. Return it directly.
91 if (!m_sTitle.isEmpty())
92 return m_sTitle;
94 // Title seems to be unused till now ... do bootstrapping
95 impl_updateTitle (true);
97 return m_sTitle;
98 // <- SYNCHRONIZED
101 void SAL_CALL TitleHelper::setTitle(const OUString& sTitle)
103 // SYNCHRONIZED ->
105 osl::MutexGuard aLock(m_aMutex);
107 m_bExternalTitle = true;
108 m_sTitle = sTitle;
110 // <- SYNCHRONIZED
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;
130 // SYNCHRONIZED ->
132 osl::MutexGuard aLock(m_aMutex);
134 xSubTitle = m_xSubTitle;
136 // <- SYNCHRONIZED
138 if (aEvent.Source != xSubTitle)
139 return;
141 impl_updateTitle ();
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"))
149 return;
151 css::uno::Reference< css::frame::XModel > xOwner;
152 // SYNCHRONIZED ->
154 osl::MutexGuard aLock(m_aMutex);
156 xOwner.set(m_xOwner, css::uno::UNO_QUERY);
158 // <- SYNCHRONIZED
160 if (aEvent.Source != xOwner
161 || ((aEvent.EventName.equalsIgnoreAsciiCase("OnModeChanged")
162 || aEvent.EventName.equalsIgnoreAsciiCase("OnTitleChanged"))
163 && !xOwner.is()))
165 return;
168 impl_updateTitle ();
171 void SAL_CALL TitleHelper::frameAction(const css::frame::FrameActionEvent& aEvent)
173 css::uno::Reference< css::frame::XFrame > xOwner;
174 // SYNCHRONIZED ->
176 osl::MutexGuard aLock(m_aMutex);
178 xOwner.set(m_xOwner, css::uno::UNO_QUERY);
180 // <- SYNCHRONIZED
182 if (aEvent.Source != xOwner)
183 return;
185 // we are interested on events only, which must trigger a title bar update
186 // because component was changed.
187 if (
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);
194 impl_updateTitle ();
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;
203 // SYNCHRONIZED ->
205 osl::MutexGuard aLock(m_aMutex);
207 xOwner = m_xOwner;
208 xNumbers = m_xUntitledNumbers;
209 nLeasedNumber = m_nLeasedNumber;
211 // <- SYNCHRONIZED
213 if ( ! xOwner.is ())
214 return;
216 css::uno::Reference< css::frame::XFrame > xFrame(xOwner, css::uno::UNO_QUERY);
217 if (xFrame.is())
218 xFrame->removeFrameActionListener(this);
220 if (xOwner != aEvent.Source)
221 return;
223 if (
224 (xNumbers.is () ) &&
225 (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER)
227 xNumbers->releaseNumber (nLeasedNumber);
229 // SYNCHRONIZED ->
231 osl::MutexGuard aLock(m_aMutex);
233 m_xOwner.clear();
234 m_sTitle.clear();
235 m_nLeasedNumber = css::frame::UntitledNumbersConst::INVALID_NUMBER;
237 // <- SYNCHRONIZED
240 void TitleHelper::impl_sendTitleChangedEvent ()
242 css::uno::Reference<css::uno::XInterface> xOwner;
243 // SYNCHRONIZED ->
245 osl::MutexGuard aLock(m_aMutex);
247 xOwner = m_xOwner;
249 // <- SYNCHRONIZED
251 css::frame::TitleChangedEvent aEvent(xOwner, m_sTitle);
253 if( ! aEvent.Source.is() )
254 return;
256 comphelper::OInterfaceContainerHelper2* pContainer = m_aListener.getContainer( cppu::UnoType<css::frame::XTitleChangeListener>::get());
257 if ( ! pContainer)
258 return;
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&)
269 pIt.remove();
274 void TitleHelper::impl_updateTitle (bool init)
276 css::uno::Reference<css::uno::XInterface> xOwner;
278 // SYNCHRONIZED ->
280 osl::MutexGuard aLock(m_aMutex);
282 xOwner = m_xOwner;
284 // <- SYNCHRONIZED
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();
305 return {};
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;
313 // SYNCHRONIZED ->
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)
320 return;
322 xOwner = m_xOwner;
323 xNumbers = m_xUntitledNumbers;
324 nLeasedNumber = m_nLeasedNumber;
326 // <- SYNCHRONIZED
328 if (
329 ( ! xOwner.is ()) ||
330 ( ! xNumbers.is ()) ||
331 ( ! xModel.is ())
333 return;
335 OUString sTitle;
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;
361 else
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);
368 else
369 sTitle = xNumbers->getUntitledPrefix() + "?";
372 bool bChanged;
373 // SYNCHRONIZED ->
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
381 m_sTitle = sTitle;
382 m_nLeasedNumber = nLeasedNumber;
384 // <- SYNCHRONIZED
386 if (bChanged)
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;
395 // SYNCHRONIZED ->
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)
402 return;
404 xOwner = m_xOwner;
405 xNumbers = m_xUntitledNumbers;
406 nLeasedNumber = m_nLeasedNumber;
408 // <- SYNCHRONIZED
410 if (
411 ( ! xOwner.is ()) ||
412 ( ! xNumbers.is ()) ||
413 ( ! xController.is ())
415 return;
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));
433 if (xModel.is ())
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);
444 else
446 sTitle.append (xNumbers->getUntitledPrefix ());
447 if ( nLeasedNumber > 1 )
449 sTitle.append(nLeasedNumber );
453 bool bChanged;
454 // SYNCHRONIZED ->
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;
463 // <- SYNCHRONIZED
465 if (bChanged)
466 impl_sendTitleChangedEvent ();
469 void TitleHelper::impl_updateTitleForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame, bool init)
471 if ( ! xFrame.is ())
472 return;
474 // SYNCHRONIZED ->
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)
481 return;
483 // <- SYNCHRONIZED
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);
492 #ifndef MACOSX
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);
501 #endif
502 impl_appendSafeMode (sTitle);
504 bool bChanged;
505 // SYNCHRONIZED ->
507 osl::MutexGuard aLock(m_aMutex);
509 OUString sNewTitle = sTitle.makeStringAndClear ();
510 bChanged = !init && m_sTitle != sNewTitle;
511 m_sTitle = sNewTitle;
513 // <- SYNCHRONIZED
515 if (bChanged)
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.
525 if (xTitle.is ())
526 sTitle.append (xTitle->getTitle ());
529 void TitleHelper::impl_appendProductName (OUStringBuffer& sTitle)
531 OUString name(utl::ConfigManager::getProductName());
532 if (!name.isEmpty())
534 if (!sTitle.isEmpty())
536 OUString separator (FwkResId (STR_EMDASH_SEPARATOR));
537 sTitle.append(separator);
539 sTitle.append(name);
543 void TitleHelper::impl_appendModuleName (OUStringBuffer& sTitle)
545 css::uno::Reference< css::uno::XInterface > xOwner;
546 css::uno::Reference< css::uno::XComponentContext > xContext;
547 // SYNCHRONIZED ->
549 osl::MutexGuard aLock(m_aMutex);
551 xOwner = m_xOwner;
552 xContext = m_xContext;
554 // <- SYNCHRONIZED
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&)
577 #ifdef DBG_UTIL
578 void TitleHelper::impl_appendDebugVersion (OUStringBuffer& sTitle)
580 OUString version(utl::ConfigManager::getProductVersion());
581 sTitle.append(' ');
582 sTitle.append(version);
583 OUString sVersion = ::utl::Bootstrap::getBuildIdData("development");
584 sTitle.append(" [");
585 sTitle.append(sVersion);
586 sTitle.append("]");
588 #else
589 void TitleHelper::impl_appendDebugVersion (OUStringBuffer&)
592 #endif
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 ())
604 return;
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;
631 // SYNCHRONIZED ->
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)
638 return;
640 m_xSubTitle = xSubTitle;
642 // <- SYNCHRONIZED
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);
658 OUString sTitle;
660 if (aURL.GetProtocol() == INetProtocol::File)
662 if (aURL.HasMark())
663 aURL = INetURLObject(aURL.GetURLNoMark());
665 sTitle = aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset);
667 else
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);
679 return sTitle;
682 } // namespace framework
684 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */