1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <vcl/errinf.hxx>
21 #include <vcl/svapp.hxx>
22 #include <vcl/stdtext.hxx>
23 #include <vcl/weld.hxx>
24 
25 #include <com/sun/star/task/XInteractionAbort.hpp>
26 #include <com/sun/star/task/XInteractionApprove.hpp>
27 #include <com/sun/star/task/XInteractionDisapprove.hpp>
28 #include <com/sun/star/task/XInteractionRetry.hpp>
29 
30 #include <svx/svxerr.hxx>
31 #include <svx/dialogs.hrc>
32 #include <unotools/resmgr.hxx>
33 #include <osl/diagnose.h>
34 
35 #include <ids.hxx>
36 #include <ids.hrc>
37 #include "getcontinuations.hxx"
38 
39 #include "iahndl.hxx"
40 #include <memory>
41 
42 using namespace com::sun::star;
43 
44 namespace {
45 
46 enum class MessageBoxStyle {
47     NONE              = 0x0000,
48     Ok                = 0x0001,
49     OkCancel          = 0x0002,
50     YesNo             = 0x0004,
51     YesNoCancel       = 0x0008,
52     RetryCancel       = 0x0010
53 };
54 
55 DialogMask
executeErrorDialog(weld::Window * pParent,task::InteractionClassification eClassification,OUString const & rContext,OUString const & rMessage,MessageBoxStyle nButtonMask)56 executeErrorDialog(
57     weld::Window* pParent,
58     task::InteractionClassification eClassification,
59     OUString const & rContext,
60     OUString const & rMessage,
61     MessageBoxStyle nButtonMask)
62 {
63     SolarMutexGuard aGuard;
64 
65     OUStringBuffer aText(rContext);
66     if (!rContext.isEmpty() && !rMessage.isEmpty())
67         aText.append(":\n");
68             //TODO! must be internationalized
69     aText.append(rMessage);
70 
71     std::unique_ptr<weld::MessageDialog> xBox;
72 
73     switch (eClassification)
74     {
75         case task::InteractionClassification_ERROR:
76             xBox.reset(Application::CreateMessageDialog(pParent,
77                         VclMessageType::Error, VclButtonsType::NONE, aText.makeStringAndClear()));
78             break;
79         case task::InteractionClassification_WARNING:
80             xBox.reset(Application::CreateMessageDialog(pParent,
81                         VclMessageType::Warning, VclButtonsType::NONE, aText.makeStringAndClear()));
82             break;
83         case task::InteractionClassification_INFO:
84             xBox.reset(Application::CreateMessageDialog(pParent,
85                         VclMessageType::Info, VclButtonsType::NONE, aText.makeStringAndClear()));
86             break;
87         case task::InteractionClassification_QUERY:
88             xBox.reset(Application::CreateMessageDialog(pParent,
89                         VclMessageType::Question, VclButtonsType::NONE, aText.makeStringAndClear()));
90             break;
91         default:
92             assert(false);
93             break;
94     }
95 
96 
97     switch (nButtonMask)
98     {
99         case MessageBoxStyle::NONE:
100             break;
101         case MessageBoxStyle::Ok:
102             xBox->add_button(GetStandardText(StandardButtonType::OK), static_cast<int>(DialogMask::ButtonsOk));
103             break;
104         case MessageBoxStyle::OkCancel:
105             xBox->add_button(GetStandardText(StandardButtonType::OK), static_cast<int>(DialogMask::ButtonsOk));
106             xBox->add_button(GetStandardText(StandardButtonType::Cancel), static_cast<int>(DialogMask::ButtonsCancel));
107             break;
108         case MessageBoxStyle::YesNo:
109             xBox->add_button(GetStandardText(StandardButtonType::Yes), static_cast<int>(DialogMask::ButtonsYes));
110             xBox->add_button(GetStandardText(StandardButtonType::No), static_cast<int>(DialogMask::ButtonsNo));
111             break;
112         case MessageBoxStyle::YesNoCancel:
113             xBox->add_button(GetStandardText(StandardButtonType::Yes), static_cast<int>(DialogMask::ButtonsYes));
114             xBox->add_button(GetStandardText(StandardButtonType::No), static_cast<int>(DialogMask::ButtonsNo));
115             xBox->add_button(GetStandardText(StandardButtonType::Cancel), static_cast<int>(DialogMask::ButtonsCancel));
116             break;
117         case MessageBoxStyle::RetryCancel:
118             xBox->add_button(GetStandardText(StandardButtonType::Retry), static_cast<int>(DialogMask::ButtonsRetry));
119             xBox->add_button(GetStandardText(StandardButtonType::Cancel), static_cast<int>(DialogMask::ButtonsCancel));
120             break;
121     }
122 
123     return static_cast<DialogMask>(xBox->run());
124 }
125 
126 }
127 
128 void
handleErrorHandlerRequest(task::InteractionClassification eClassification,ErrCode nErrorCode,std::vector<OUString> const & rArguments,uno::Sequence<uno::Reference<task::XInteractionContinuation>> const & rContinuations,bool bObtainErrorStringOnly,bool & bHasErrorString,OUString & rErrorString)129 UUIInteractionHelper::handleErrorHandlerRequest(
130     task::InteractionClassification eClassification,
131     ErrCode nErrorCode,
132     std::vector< OUString > const & rArguments,
133     uno::Sequence< uno::Reference< task::XInteractionContinuation > > const &
134         rContinuations,
135     bool bObtainErrorStringOnly,
136     bool & bHasErrorString,
137     OUString & rErrorString)
138 {
139     if (bObtainErrorStringOnly)
140     {
141         bHasErrorString = isInformationalErrorMessageRequest(rContinuations);
142         if (!bHasErrorString)
143             return;
144     }
145 
146     OUString aMessage;
147     {
148         enum Source { SOURCE_DEFAULT, SOURCE_SVX, SOURCE_UUI };
149         static char const * const aManager[3] = { "svt", "svx", "uui" };
150         static const ErrMsgCode* const aId[3]
151             = { RID_ERRHDL,
152                 RID_SVXERRCODE,
153                 RID_UUI_ERRHDL };
154         ErrCodeArea nErrorArea = nErrorCode.GetArea();
155         Source eSource =
156             nErrorArea < ErrCodeArea::Svx ? SOURCE_DEFAULT
157                 : nErrorArea == ErrCodeArea::Svx ? SOURCE_SVX : SOURCE_UUI;
158 
159         std::locale aResLocale = Translate::Create(aManager[eSource]);
160         ErrorResource aErrorResource(aId[eSource], aResLocale);
161         if (!aErrorResource.getString(nErrorCode, aMessage))
162             return;
163     }
164 
165     aMessage = replaceMessageWithArguments( aMessage, rArguments );
166 
167     if (bObtainErrorStringOnly)
168     {
169         rErrorString = aMessage;
170         return;
171     }
172     else
173     {
174         //TODO! It can happen that the buttons calculated below do not match
175         // the error text from the resource (e.g., some text that is not a
176         // question, but YES and NO buttons).  Some error texts have
177         // ExtraData that specifies a set of buttons, but that data is not
178         // really useful, because a single error text may well make sense
179         // both with only an OK button and with RETRY and CANCEL buttons.
180 
181         uno::Reference< task::XInteractionApprove > xApprove;
182         uno::Reference< task::XInteractionDisapprove > xDisapprove;
183         uno::Reference< task::XInteractionRetry > xRetry;
184         uno::Reference< task::XInteractionAbort > xAbort;
185         getContinuations(
186             rContinuations, &xApprove, &xDisapprove, &xRetry, &xAbort);
187 
188         // The following mapping uses the bit mask
189         //     Approve = 8,
190         //     Disapprove = 4,
191         //     Retry = 2,
192         //     Abort = 1
193 
194         // The mapping has five properties on which the code to select the
195         // correct continuation relies:
196         // 1  The OK button is mapped to Approve if that is available,
197         //    otherwise to Abort if that is available, otherwise to none.
198         // 2  The CANCEL button is always mapped to Abort.
199         // 3  The RETRY button is always mapped to Retry.
200         // 4  The NO button is always mapped to Disapprove.
201         // 5  The YES button is always mapped to Approve.
202 
203         // Because the WinBits button combinations are quite restricted, not
204         // every request can be served here.
205 
206         // Finally, it seems to be better to leave default button
207         // determination to VCL (the favouring of CANCEL as default button
208         // seems to not always be what the user wants)...
209         MessageBoxStyle const aButtonMask[16]
210             = { MessageBoxStyle::NONE,
211                 MessageBoxStyle::Ok /*| MessBoxStyle::DefaultOk*/, // Abort
212                 MessageBoxStyle::NONE,
213                 MessageBoxStyle::RetryCancel /*| MessBoxStyle::DefaultCancel*/, // Retry, Abort
214                 MessageBoxStyle::NONE,
215                 MessageBoxStyle::NONE,
216                 MessageBoxStyle::NONE,
217                 MessageBoxStyle::NONE,
218                 MessageBoxStyle::Ok /*| MessBoxStyle::DefaultOk*/, // Approve
219                 MessageBoxStyle::OkCancel /*| MessBoxStyle::DefaultCancel*/, // Approve, Abort
220                 MessageBoxStyle::NONE,
221                 MessageBoxStyle::NONE,
222                 MessageBoxStyle::YesNo /*| MessBoxStyle::DefaultNo*/, // Approve, Disapprove
223                 MessageBoxStyle::YesNoCancel /*| MessBoxStyle::DefaultCancel*/,
224                 // Approve, Disapprove, Abort
225                 MessageBoxStyle::NONE,
226                 MessageBoxStyle::NONE };
227 
228         MessageBoxStyle nButtonMask = aButtonMask[(xApprove.is() ? 8 : 0)
229                                           | (xDisapprove.is() ? 4 : 0)
230                                           | (xRetry.is() ? 2 : 0)
231                                           | (xAbort.is() ? 1 : 0)];
232         if (nButtonMask == MessageBoxStyle::NONE)
233             return;
234 
235         //TODO! remove this backwards compatibility?
236         OUString aContext(m_aContextParam);
237         if (aContext.isEmpty() && nErrorCode != ERRCODE_NONE)
238         {
239             SolarMutexGuard aGuard;
240             ErrorContext * pContext = ErrorContext::GetContext();
241             if (pContext)
242             {
243                 OUString aContextString;
244                 if (pContext->GetString(nErrorCode, aContextString))
245                     aContext = aContextString;
246             }
247         }
248 
249         uno::Reference<awt::XWindow> xParent = getParentXWindow();
250         DialogMask nResult = executeErrorDialog(Application::GetFrameWeld(xParent),
251                 eClassification, aContext, aMessage, nButtonMask );
252 
253         switch (nResult)
254         {
255         case DialogMask::ButtonsOk:
256             OSL_ENSURE(xApprove.is() || xAbort.is(), "unexpected situation");
257             if (xApprove.is())
258                 xApprove->select();
259             else if (xAbort.is())
260                 xAbort->select();
261             break;
262 
263         case DialogMask::ButtonsCancel:
264             OSL_ENSURE(xAbort.is(), "unexpected situation");
265             if (xAbort.is())
266                 xAbort->select();
267             break;
268 
269         case DialogMask::ButtonsRetry:
270             OSL_ENSURE(xRetry.is(), "unexpected situation");
271             if (xRetry.is())
272                 xRetry->select();
273             break;
274 
275         case DialogMask::ButtonsNo:
276             OSL_ENSURE(xDisapprove.is(), "unexpected situation");
277             if (xDisapprove.is())
278                 xDisapprove->select();
279             break;
280 
281         case DialogMask::ButtonsYes:
282             OSL_ENSURE(xApprove.is(), "unexpected situation");
283             if (xApprove.is())
284                 xApprove->select();
285             break;
286 
287         default: break;
288         }
289 
290     }
291 }
292 
293 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
294