tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / dbaccess / source / ui / dlg / sqlmessage.cxx
blob60b533f90c19691b3001d89b24b2c2c0b0afd666
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 <core_resource.hxx>
21 #include <sqlmessage.hxx>
22 #include <strings.hrc>
23 #include <com/sun/star/sdbc/SQLException.hpp>
24 #include <com/sun/star/sdb/SQLContext.hpp>
25 #include <utility>
26 #include <vcl/stdtext.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/weld.hxx>
29 #include <osl/diagnose.h>
30 #include <connectivity/dbexception.hxx>
31 #include <connectivity/sqlerror.hxx>
32 #include <unotools/configmgr.hxx>
34 #include <tools/urlobj.hxx>
36 #define RET_MORE RET_RETRY + 1
38 using namespace dbtools;
39 using namespace com::sun::star::uno;
40 using namespace com::sun::star::sdb;
41 using namespace com::sun::star::sdbc;
43 namespace dbaui
46 namespace
48 class ImageProvider
50 private:
51 OUString m_defaultImageID;
53 public:
54 explicit ImageProvider(OUString defaultImageID)
55 : m_defaultImageID(std::move(defaultImageID))
59 const OUString& getImage() const
61 return m_defaultImageID;
65 class LabelProvider
67 private:
68 OUString m_label;
69 public:
70 explicit LabelProvider(TranslateId labelResourceID)
71 : m_label(DBA_RES(labelResourceID))
75 const OUString& getLabel() const
77 return m_label;
81 class ProviderFactory
83 private:
84 mutable std::shared_ptr< ImageProvider > m_pErrorImage;
85 mutable std::shared_ptr< ImageProvider > m_pWarningsImage;
86 mutable std::shared_ptr< ImageProvider > m_pInfoImage;
87 mutable std::shared_ptr< LabelProvider > m_pErrorLabel;
88 mutable std::shared_ptr< LabelProvider > m_pWarningsLabel;
89 mutable std::shared_ptr< LabelProvider > m_pInfoLabel;
91 public:
92 ProviderFactory()
96 std::shared_ptr< ImageProvider > const & getImageProvider( SQLExceptionInfo::TYPE _eType ) const
98 std::shared_ptr< ImageProvider >* ppProvider( &m_pErrorImage );
99 OUString sNormalImageID(u"dialog-error"_ustr);
101 switch ( _eType )
103 case SQLExceptionInfo::TYPE::SQLWarning:
104 ppProvider = &m_pWarningsImage;
105 sNormalImageID = "dialog-warning";
106 break;
108 case SQLExceptionInfo::TYPE::SQLContext:
109 ppProvider = &m_pInfoImage;
110 sNormalImageID = "dialog-information";
111 break;
113 default:
114 break;
117 if ( !ppProvider->get() )
118 (*ppProvider) = std::make_shared<ImageProvider>(sNormalImageID);
119 return *ppProvider;
122 std::shared_ptr< LabelProvider > const & getLabelProvider( SQLExceptionInfo::TYPE _eType, bool _bSubLabel ) const
124 std::shared_ptr< LabelProvider >* ppProvider( &m_pErrorLabel );
125 TranslateId pLabelID( STR_EXCEPTION_ERROR );
127 switch ( _eType )
129 case SQLExceptionInfo::TYPE::SQLWarning:
130 ppProvider = &m_pWarningsLabel;
131 pLabelID = STR_EXCEPTION_WARNING;
132 break;
134 case SQLExceptionInfo::TYPE::SQLContext:
135 ppProvider = &m_pInfoLabel;
136 pLabelID = _bSubLabel ? STR_EXCEPTION_DETAILS : STR_EXCEPTION_INFO;
137 break;
138 default:
139 break;
142 if ( !ppProvider->get() )
143 (*ppProvider) = std::make_shared<LabelProvider>( pLabelID );
144 return *ppProvider;
149 /// a stripped version of the SQLException, packed for displaying
150 struct ExceptionDisplayInfo
152 SQLExceptionInfo::TYPE eType;
154 std::shared_ptr< ImageProvider > pImageProvider;
155 std::shared_ptr< LabelProvider > pLabelProvider;
157 bool bSubEntry;
159 OUString sMessage;
160 OUString sSQLState;
161 OUString sErrorCode;
163 ExceptionDisplayInfo() : eType( SQLExceptionInfo::TYPE::Undefined ), bSubEntry( false ) { }
164 explicit ExceptionDisplayInfo( SQLExceptionInfo::TYPE _eType ) : eType( _eType ), bSubEntry( false ) { }
167 bool lcl_hasDetails( const ExceptionDisplayInfo& _displayInfo )
169 return ( !_displayInfo.sErrorCode.isEmpty() )
170 || ( !_displayInfo.sSQLState.isEmpty()
171 && _displayInfo.sSQLState != "S1000"
175 typedef std::vector< ExceptionDisplayInfo > ExceptionDisplayChain;
177 /// strips the [OOoBase] vendor identifier from the given error message, if applicable
178 OUString lcl_stripOOoBaseVendor( const OUString& _rErrorMessage )
180 OUString sErrorMessage( _rErrorMessage );
182 const OUString sVendorIdentifier( ::connectivity::SQLError::getMessagePrefix() );
183 if ( sErrorMessage.startsWith( sVendorIdentifier ) )
185 // characters to strip
186 sal_Int32 nStripLen( sVendorIdentifier.getLength() );
187 // usually, there should be a whitespace between the vendor and the real message
188 while ( ( sErrorMessage.getLength() > nStripLen )
189 && ( sErrorMessage[nStripLen] == ' ' )
191 ++nStripLen;
192 sErrorMessage = sErrorMessage.copy( nStripLen );
195 return sErrorMessage;
198 void lcl_buildExceptionChain( const SQLExceptionInfo& _rErrorInfo, const ProviderFactory& _rFactory, ExceptionDisplayChain& _out_rChain )
200 ExceptionDisplayChain().swap(_out_rChain);
202 SQLExceptionIteratorHelper iter( _rErrorInfo );
203 while ( iter.hasMoreElements() )
205 // current chain element
206 SQLExceptionInfo aCurrentElement;
207 iter.next( aCurrentElement );
209 const SQLException* pCurrentError = aCurrentElement;
210 assert(pCurrentError && "lcl_buildExceptionChain: iterator failure!");
211 // hasMoreElements should not have returned <TRUE/> in this case
213 ExceptionDisplayInfo aDisplayInfo( aCurrentElement.getType() );
215 aDisplayInfo.sMessage = pCurrentError->Message.trim();
216 aDisplayInfo.sSQLState = pCurrentError->SQLState;
217 if ( pCurrentError->ErrorCode )
218 aDisplayInfo.sErrorCode = OUString::number( pCurrentError->ErrorCode );
220 if ( aDisplayInfo.sMessage.isEmpty()
221 && !lcl_hasDetails( aDisplayInfo )
224 OSL_FAIL( "lcl_buildExceptionChain: useless exception: no state, no error code, no message!" );
225 continue;
228 aDisplayInfo.pImageProvider = _rFactory.getImageProvider( aCurrentElement.getType() );
229 aDisplayInfo.pLabelProvider = _rFactory.getLabelProvider( aCurrentElement.getType(), false );
231 _out_rChain.push_back( aDisplayInfo );
233 if ( aCurrentElement.getType() == SQLExceptionInfo::TYPE::SQLContext )
235 const SQLContext* pContext = aCurrentElement;
236 if ( !pContext->Details.isEmpty() )
238 ExceptionDisplayInfo aSubInfo( aCurrentElement.getType() );
240 aSubInfo.sMessage = pContext->Details;
241 aSubInfo.pImageProvider = _rFactory.getImageProvider( aCurrentElement.getType() );
242 aSubInfo.pLabelProvider = _rFactory.getLabelProvider( aCurrentElement.getType(), true );
243 aSubInfo.bSubEntry = true;
245 _out_rChain.push_back( aSubInfo );
251 void lcl_insertExceptionEntry(weld::TreeView& rList, size_t nElementPos, const ExceptionDisplayInfo& rEntry)
253 rList.append(OUString::number(nElementPos), rEntry.pLabelProvider->getLabel(), rEntry.pImageProvider->getImage());
257 namespace {
259 class OExceptionChainDialog : public weld::GenericDialogController
261 std::unique_ptr<weld::TreeView> m_xExceptionList;
262 std::unique_ptr<weld::TextView> m_xExceptionText;
264 OUString m_sStatusLabel;
265 OUString m_sErrorCodeLabel;
267 ExceptionDisplayChain m_aExceptions;
269 public:
270 OExceptionChainDialog(weld::Window* pParent, ExceptionDisplayChain&& rExceptions);
272 protected:
273 DECL_LINK(OnExceptionSelected, weld::TreeView&, void);
278 OExceptionChainDialog::OExceptionChainDialog(weld::Window* pParent, ExceptionDisplayChain&& rExceptions)
279 : GenericDialogController(pParent, u"dbaccess/ui/sqlexception.ui"_ustr, u"SQLExceptionDialog"_ustr)
280 , m_xExceptionList(m_xBuilder->weld_tree_view(u"list"_ustr))
281 , m_xExceptionText(m_xBuilder->weld_text_view(u"description"_ustr))
282 , m_aExceptions(std::move(rExceptions))
284 int nListWidth = m_xExceptionText->get_approximate_digit_width() * 28;
285 int nTextWidth = m_xExceptionText->get_approximate_digit_width() * 42;
286 int nHeight = m_xExceptionList->get_height_rows(6);
287 m_xExceptionList->set_size_request(nListWidth, nHeight);
288 m_xExceptionText->set_size_request(nTextWidth, nHeight);
290 m_sStatusLabel = DBA_RES( STR_EXCEPTION_STATUS );
291 m_sErrorCodeLabel = DBA_RES( STR_EXCEPTION_ERRORCODE );
293 m_xExceptionList->connect_selection_changed(
294 LINK(this, OExceptionChainDialog, OnExceptionSelected));
296 bool bHave22018 = false;
297 size_t elementPos = 0;
299 for (auto const& elem : m_aExceptions)
301 lcl_insertExceptionEntry(*m_xExceptionList, elementPos, elem);
302 bHave22018 = elem.sSQLState == "22018";
303 ++elementPos;
306 // if the error has the code 22018, then add an additional explanation
307 // #i24021#
308 if ( bHave22018 )
310 ProviderFactory aProviderFactory;
312 ExceptionDisplayInfo aInfo22018;
313 aInfo22018.sMessage = DBA_RES( STR_EXPLAN_STRINGCONVERSION_ERROR );
314 aInfo22018.pLabelProvider = aProviderFactory.getLabelProvider( SQLExceptionInfo::TYPE::SQLContext, false );
315 aInfo22018.pImageProvider = aProviderFactory.getImageProvider( SQLExceptionInfo::TYPE::SQLContext );
316 m_aExceptions.push_back( aInfo22018 );
318 lcl_insertExceptionEntry(*m_xExceptionList, m_aExceptions.size() - 1, aInfo22018);
321 if (m_xExceptionList->n_children())
323 m_xExceptionList->select(0);
324 OnExceptionSelected(*m_xExceptionList);
328 IMPL_LINK_NOARG(OExceptionChainDialog, OnExceptionSelected, weld::TreeView&, void)
330 OUString sText;
332 OUString sId(m_xExceptionList->get_selected_id());
333 if (!sId.isEmpty())
335 const ExceptionDisplayInfo& aExceptionInfo(m_aExceptions[sId.toUInt32()]);
337 if ( !aExceptionInfo.sSQLState.isEmpty() )
339 sText += m_sStatusLabel + ": " + aExceptionInfo.sSQLState + "\n";
342 if ( !aExceptionInfo.sErrorCode.isEmpty() )
344 sText += m_sErrorCodeLabel + ": " + aExceptionInfo.sErrorCode + "\n";
347 if ( !sText.isEmpty() )
348 sText += "\n";
350 sText += aExceptionInfo.sMessage;
353 m_xExceptionText->set_text(sText);
356 // SQLMessageBox_Impl
357 struct SQLMessageBox_Impl
359 ExceptionDisplayChain aDisplayInfo;
361 explicit SQLMessageBox_Impl( const SQLExceptionInfo& _rExceptionInfo )
363 // transform the exception chain to a form more suitable for displaying it here
364 ProviderFactory aProviderFactory;
365 lcl_buildExceptionChain( _rExceptionInfo, aProviderFactory, aDisplayInfo );
369 namespace
371 void lcl_addButton(weld::MessageDialog* pDialog, StandardButtonType eType, bool bDefault)
373 sal_uInt16 nButtonID = 0;
374 switch (eType)
376 case StandardButtonType::Yes:
377 nButtonID = RET_YES;
378 pDialog->add_button(GetStandardText(StandardButtonType::Yes), nButtonID);
379 break;
380 case StandardButtonType::No:
381 nButtonID = RET_NO;
382 pDialog->add_button(GetStandardText(StandardButtonType::No), nButtonID);
383 break;
384 case StandardButtonType::OK:
385 nButtonID = RET_OK;
386 pDialog->add_button(GetStandardText(StandardButtonType::OK), nButtonID);
387 break;
388 case StandardButtonType::Cancel:
389 nButtonID = RET_CANCEL;
390 pDialog->add_button(GetStandardText(StandardButtonType::Cancel), nButtonID);
391 break;
392 case StandardButtonType::Retry:
393 nButtonID = RET_RETRY;
394 pDialog->add_button(GetStandardText(StandardButtonType::Retry), nButtonID);
395 break;
396 case StandardButtonType::Help:
397 nButtonID = RET_HELP;
398 pDialog->add_button(GetStandardText(StandardButtonType::Help), nButtonID);
399 break;
400 default:
401 OSL_FAIL( "lcl_addButton: invalid button id!" );
402 break;
404 if (bDefault)
405 pDialog->set_default_response(nButtonID);
409 void OSQLMessageBox::impl_fillMessages()
411 OSL_PRECOND( !m_pImpl->aDisplayInfo.empty(), "OSQLMessageBox::impl_fillMessages: nothing to display at all?" );
413 if ( m_pImpl->aDisplayInfo.empty() )
414 return;
415 const ExceptionDisplayInfo* pSecondInfo = nullptr;
417 const ExceptionDisplayInfo& rFirstInfo = *m_pImpl->aDisplayInfo.begin();
418 if ( m_pImpl->aDisplayInfo.size() > 1 )
419 pSecondInfo = &m_pImpl->aDisplayInfo[1];
420 OUString sPrimary, sSecondary;
421 sPrimary = rFirstInfo.sMessage;
422 // one or two texts to display?
423 if ( pSecondInfo )
425 // we show two elements in the main dialog if and only if one of
426 // - the first element in the chain is an SQLContext, and the second
427 // element denotes its sub entry
428 // - the first and the second element are both independent (i.e. the second
429 // is no sub entry), and none of them is a context.
430 bool bFirstElementIsContext = ( rFirstInfo.eType == SQLExceptionInfo::TYPE::SQLContext );
431 bool bSecondElementIsContext = ( pSecondInfo->eType == SQLExceptionInfo::TYPE::SQLContext );
433 if ( bFirstElementIsContext && pSecondInfo->bSubEntry )
434 sSecondary = pSecondInfo->sMessage;
435 if ( !bFirstElementIsContext && !bSecondElementIsContext )
436 sSecondary = pSecondInfo->sMessage;
439 // primary text
440 m_xDialog->set_primary_text(lcl_stripOOoBaseVendor(sPrimary));
442 // secondary text (if applicable)
443 m_xDialog->set_secondary_text(lcl_stripOOoBaseVendor(sSecondary));
446 void OSQLMessageBox::impl_createStandardButtons( MessBoxStyle _nStyle )
448 if ( _nStyle & MessBoxStyle::YesNoCancel )
450 lcl_addButton(m_xDialog.get(), StandardButtonType::Yes, bool(_nStyle & MessBoxStyle::DefaultYes));
451 lcl_addButton(m_xDialog.get(), StandardButtonType::No, bool(_nStyle & MessBoxStyle::DefaultNo));
452 lcl_addButton(m_xDialog.get(), StandardButtonType::Cancel, bool(_nStyle & MessBoxStyle::DefaultCancel));
454 else if ( _nStyle & MessBoxStyle::OkCancel )
456 lcl_addButton(m_xDialog.get(), StandardButtonType::OK, bool(_nStyle & MessBoxStyle::DefaultOk));
457 lcl_addButton(m_xDialog.get(), StandardButtonType::Cancel, bool(_nStyle & MessBoxStyle::DefaultCancel));
459 else if ( _nStyle & MessBoxStyle::YesNo )
461 lcl_addButton(m_xDialog.get(), StandardButtonType::Yes, bool(_nStyle & MessBoxStyle::DefaultYes));
462 lcl_addButton(m_xDialog.get(), StandardButtonType::No, bool(_nStyle & MessBoxStyle::DefaultNo));
464 else if ( _nStyle & MessBoxStyle::RetryCancel )
466 lcl_addButton(m_xDialog.get(), StandardButtonType::Retry, bool(_nStyle & MessBoxStyle::DefaultRetry));
467 lcl_addButton(m_xDialog.get(), StandardButtonType::Cancel, bool(_nStyle & MessBoxStyle::DefaultCancel));
469 else if ( _nStyle & MessBoxStyle::Ok )
471 lcl_addButton(m_xDialog.get(), StandardButtonType::OK, true);
474 if ( m_sHelpURL.isEmpty() )
475 return;
477 lcl_addButton(m_xDialog.get(), StandardButtonType::Help, false);
479 OUString aTmp;
480 INetURLObject aHID( m_sHelpURL );
481 if ( aHID.GetProtocol() == INetProtocol::Hid )
482 aTmp = aHID.GetURLPath();
483 else
484 aTmp = m_sHelpURL;
486 m_xDialog->set_help_id(aTmp);
489 void OSQLMessageBox::impl_addDetailsButton()
491 size_t nFirstPageVisible = m_xDialog->get_secondary_text().isEmpty() ? 1 : 2;
493 bool bMoreDetailsAvailable = m_pImpl->aDisplayInfo.size() > nFirstPageVisible;
494 if ( !bMoreDetailsAvailable )
496 // even if the text fits into what we can display, we might need to details button
497 // if there is more non-trivial information in the errors than the mere messages
498 for (auto const& error : m_pImpl->aDisplayInfo)
500 if ( lcl_hasDetails(error) )
502 bMoreDetailsAvailable = true;
503 break;
508 if ( bMoreDetailsAvailable )
510 m_xDialog->add_button(GetStandardText(StandardButtonType::More), RET_MORE);
511 m_xMoreButton = m_xDialog->weld_button_for_response(RET_MORE);
512 m_xMoreButton->connect_clicked(LINK(this, OSQLMessageBox, ButtonClickHdl));
516 void OSQLMessageBox::Construct(weld::Window* pParent, MessBoxStyle _nStyle, MessageType _eImage)
518 // init the image
519 MessageType eType( _eImage );
520 if ( eType == AUTO )
522 switch ( m_pImpl->aDisplayInfo[0].eType )
524 case SQLExceptionInfo::TYPE::SQLException: eType = Error; break;
525 case SQLExceptionInfo::TYPE::SQLWarning: eType = Warning; break;
526 case SQLExceptionInfo::TYPE::SQLContext: eType = Info; break;
527 default: OSL_FAIL( "OSQLMessageBox::Construct: invalid type!" );
530 VclMessageType eMessageType;
531 switch (eType)
533 default:
534 OSL_FAIL( "OSQLMessageBox::impl_initImage: unsupported image type!" );
535 [[fallthrough]];
536 case Info:
537 eMessageType = VclMessageType::Info;
538 break;
539 case Warning:
540 eMessageType = VclMessageType::Warning;
541 break;
542 case Error:
543 eMessageType = VclMessageType::Error;
544 break;
545 case Query:
546 eMessageType = VclMessageType::Question;
547 break;
550 m_xDialog.reset(Application::CreateMessageDialog(pParent, eMessageType, VclButtonsType::NONE, u""_ustr));
551 m_xDialog->set_title(utl::ConfigManager::getProductName() + " Base");
553 impl_fillMessages();
555 // create buttons
556 impl_createStandardButtons( _nStyle );
557 impl_addDetailsButton();
560 OSQLMessageBox::OSQLMessageBox(weld::Window* pParent, const SQLExceptionInfo& rException, MessBoxStyle nStyle, OUString sHelpURL)
561 : m_pImpl(new SQLMessageBox_Impl(rException))
562 , m_sHelpURL(std::move(sHelpURL))
564 Construct(pParent, nStyle, AUTO);
567 OSQLMessageBox::OSQLMessageBox(weld::Window* pParent, const OUString& rTitle, const OUString& rMessage, MessBoxStyle nStyle, MessageType eType, const ::dbtools::SQLExceptionInfo* pAdditionalErrorInfo )
569 css::uno::Any next;
570 if (pAdditionalErrorInfo)
571 next = pAdditionalErrorInfo->get();
572 SQLContext aError(rTitle, {}, {}, 0, next, rMessage);
574 m_pImpl.reset(new SQLMessageBox_Impl(SQLExceptionInfo(aError)));
576 Construct(pParent, nStyle, eType);
579 OSQLMessageBox::~OSQLMessageBox()
583 IMPL_LINK_NOARG(OSQLMessageBox, ButtonClickHdl, weld::Button&, void)
585 OExceptionChainDialog aDlg(m_xDialog.get(), std::vector(m_pImpl->aDisplayInfo));
586 aDlg.run();
589 // OSQLWarningBox
590 OSQLWarningBox::OSQLWarningBox(weld::Window* pParent, const OUString& rMessage, MessBoxStyle nStyle,
591 const ::dbtools::SQLExceptionInfo* pAdditionalErrorInfo )
592 : OSQLMessageBox(pParent, DBA_RES(STR_EXCEPTION_WARNING), rMessage, nStyle, MessageType::Warning, pAdditionalErrorInfo)
596 // OSQLErrorBox
597 OSQLErrorBox::OSQLErrorBox(weld::Window* pParent, const OUString& rMessage)
598 : OSQLMessageBox(pParent, DBA_RES(STR_EXCEPTION_ERROR), rMessage, MessBoxStyle::Ok | MessBoxStyle::DefaultOk,
599 MessageType::Error, nullptr)
603 } // namespace dbaui
605 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */