tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / framework / source / fwe / helper / titlehelper.cxx
blob422982ea629e65006cba2d8b4ab88c708418bd87
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 <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>
40 #include <utility>
41 #include <vcl/svapp.hxx>
44 using namespace css;
45 using namespace css::uno;
46 using namespace css::frame;
48 namespace framework{
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))
55 , m_xOwner (xOwner)
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()
81 // SYNCHRONIZED ->
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 !
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 aLock.unlock();
96 impl_updateTitle (true);
97 aLock.lock();
99 return m_sTitle;
100 // <- SYNCHRONIZED
103 void SAL_CALL TitleHelper::setTitle(const OUString& sTitle)
105 // SYNCHRONIZED ->
107 std::unique_lock aLock(m_aMutex);
109 m_bExternalTitle = true;
110 m_sTitle = sTitle;
112 // <- SYNCHRONIZED
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;
132 // SYNCHRONIZED ->
134 std::unique_lock aLock(m_aMutex);
136 xSubTitle = m_xSubTitle;
138 // <- SYNCHRONIZED
140 if (aEvent.Source != xSubTitle)
141 return;
143 impl_updateTitle ();
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"))
151 return;
153 css::uno::Reference< css::frame::XModel > xOwner;
154 // SYNCHRONIZED ->
156 std::unique_lock aLock(m_aMutex);
158 xOwner.set(m_xOwner, css::uno::UNO_QUERY);
160 // <- SYNCHRONIZED
162 if (aEvent.Source != xOwner
163 || ((aEvent.EventName.equalsIgnoreAsciiCase("OnModeChanged")
164 || aEvent.EventName.equalsIgnoreAsciiCase("OnTitleChanged"))
165 && !xOwner.is()))
167 return;
170 impl_updateTitle ();
173 void SAL_CALL TitleHelper::frameAction(const css::frame::FrameActionEvent& aEvent)
175 css::uno::Reference< css::frame::XFrame > xOwner;
176 // SYNCHRONIZED ->
178 std::unique_lock aLock(m_aMutex);
180 xOwner.set(m_xOwner, css::uno::UNO_QUERY);
182 // <- SYNCHRONIZED
184 if (aEvent.Source != xOwner)
185 return;
187 // we are interested on events only, which must trigger a title bar update
188 // because component was changed.
189 if (
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);
196 impl_updateTitle ();
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;
205 // SYNCHRONIZED ->
207 std::unique_lock aLock(m_aMutex);
209 xOwner = m_xOwner;
210 xNumbers = m_xUntitledNumbers;
211 nLeasedNumber = m_nLeasedNumber;
213 // <- SYNCHRONIZED
215 if ( ! xOwner.is ())
216 return;
218 css::uno::Reference< css::frame::XFrame > xFrame(xOwner, css::uno::UNO_QUERY);
219 if (xFrame.is())
220 xFrame->removeFrameActionListener(this);
222 if (xOwner != aEvent.Source)
223 return;
225 if (
226 (xNumbers.is () ) &&
227 (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER)
229 xNumbers->releaseNumber (nLeasedNumber);
231 // SYNCHRONIZED ->
233 std::unique_lock aLock(m_aMutex);
235 m_xOwner.clear();
236 m_sTitle.clear();
237 m_nLeasedNumber = css::frame::UntitledNumbersConst::INVALID_NUMBER;
239 // <- SYNCHRONIZED
242 void TitleHelper::impl_sendTitleChangedEvent ()
244 css::uno::Reference<css::uno::XInterface> xOwner;
245 OUString sTitle;
246 // SYNCHRONIZED ->
248 std::unique_lock aLock(m_aMutex);
250 xOwner = m_xOwner;
252 sTitle = m_sTitle;
254 // <- SYNCHRONIZED
256 css::frame::TitleChangedEvent aEvent(xOwner, sTitle);
258 if( ! aEvent.Source.is() )
259 return;
261 std::unique_lock aLock(m_aMutex);
262 comphelper::OInterfaceIteratorHelper4 pIt( aLock, m_aTitleChangeListeners );
263 while ( pIt.hasMoreElements() )
265 aLock.unlock();
268 uno::Reference<css::frame::XTitleChangeListener> i = pIt.next();
269 i->titleChanged( aEvent );
271 catch(const css::uno::Exception&)
273 aLock.lock();
274 pIt.remove(aLock);
275 aLock.unlock();
277 aLock.lock();
281 void TitleHelper::impl_updateTitle (bool init)
283 css::uno::Reference<css::uno::XInterface> xOwner;
285 // SYNCHRONIZED ->
287 std::unique_lock aLock(m_aMutex);
289 xOwner = m_xOwner;
291 // <- SYNCHRONIZED
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();
312 return {};
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;
320 // SYNCHRONIZED ->
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)
327 return;
329 xOwner = m_xOwner;
330 xNumbers = m_xUntitledNumbers;
331 nLeasedNumber = m_nLeasedNumber;
333 // <- SYNCHRONIZED
335 if (
336 ( ! xOwner.is ()) ||
337 ( ! xNumbers.is ()) ||
338 ( ! xModel.is ())
340 return;
342 OUString sTitle;
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;
368 else
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);
375 else
376 sTitle = xNumbers->getUntitledPrefix() + "?";
379 bool bChanged;
380 // SYNCHRONIZED ->
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
388 m_sTitle = sTitle;
389 m_nLeasedNumber = nLeasedNumber;
391 // <- SYNCHRONIZED
393 if (bChanged)
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;
402 // SYNCHRONIZED ->
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)
409 return;
411 xOwner = m_xOwner;
412 xNumbers = m_xUntitledNumbers;
413 nLeasedNumber = m_nLeasedNumber;
415 // <- SYNCHRONIZED
417 if (
418 ( ! xOwner.is ()) ||
419 ( ! xNumbers.is ()) ||
420 ( ! xController.is ())
422 return;
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));
440 if (xModel.is ())
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);
452 else
454 sTitle.append (xNumbers->getUntitledPrefix ());
455 if ( nLeasedNumber > 1 )
457 sTitle.append(nLeasedNumber );
461 bool bChanged;
462 // SYNCHRONIZED ->
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;
471 // <- SYNCHRONIZED
473 if (bChanged)
474 impl_sendTitleChangedEvent ();
477 void TitleHelper::impl_updateTitleForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame, bool init)
479 if ( ! xFrame.is ())
480 return;
482 // SYNCHRONIZED ->
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)
489 return;
491 // <- SYNCHRONIZED
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);
500 #ifndef MACOSX
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);
509 #endif
510 impl_appendSafeMode (sTitle);
512 bool bChanged;
513 // SYNCHRONIZED ->
515 std::unique_lock aLock(m_aMutex);
517 OUString sNewTitle = sTitle.makeStringAndClear ();
518 bChanged = !init && m_sTitle != sNewTitle;
519 m_sTitle = sNewTitle;
521 // <- SYNCHRONIZED
523 if (bChanged)
524 impl_sendTitleChangedEvent ();
527 // static
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.
534 if (xTitle.is ())
535 sTitle.append (xTitle->getTitle ());
538 // static
539 void TitleHelper::impl_appendProductName (OUStringBuffer& sTitle)
541 OUString name(utl::ConfigManager::getProductName());
542 if (!name.isEmpty())
544 if (!sTitle.isEmpty())
546 OUString separator (FwkResId (STR_EMDASH_SEPARATOR));
547 sTitle.append(separator);
549 sTitle.append(name);
553 void TitleHelper::impl_appendModuleName (OUStringBuffer& sTitle)
555 css::uno::Reference< css::uno::XInterface > xOwner;
556 css::uno::Reference< css::uno::XComponentContext > xContext;
557 // SYNCHRONIZED ->
559 std::unique_lock aLock(m_aMutex);
561 xOwner = m_xOwner;
562 xContext = m_xContext;
564 // <- SYNCHRONIZED
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&)
587 #ifdef DBG_UTIL
588 // static
589 void TitleHelper::impl_appendDebugVersion (OUStringBuffer& sTitle)
591 OUString version(utl::ConfigManager::getProductVersion());
592 sTitle.append(' ');
593 sTitle.append(version);
594 OUString sVersion = ::utl::Bootstrap::getBuildIdData(u"development"_ustr);
595 sTitle.append(" [");
596 sTitle.append(sVersion);
597 sTitle.append("]");
599 #else
600 void TitleHelper::impl_appendDebugVersion (OUStringBuffer&)
603 #endif
605 // static
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 ())
616 return;
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;
643 // SYNCHRONIZED ->
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)
650 return;
652 m_xSubTitle = xSubTitle;
654 // <- SYNCHRONIZED
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);
667 // static
668 OUString TitleHelper::impl_convertURL2Title(std::u16string_view sURL)
670 INetURLObject aURL (sURL);
671 OUString sTitle;
673 if (aURL.GetProtocol() == INetProtocol::File)
675 if (aURL.HasMark())
676 aURL = INetURLObject(aURL.GetURLNoMark());
678 sTitle = aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset);
680 else
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);
692 return sTitle;
695 } // namespace framework
697 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */