1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include <memory>
29 
30 #include "vos/mutex.hxx"
31 #include "vcl/svapp.hxx"
32 #include "vcl/msgbox.hxx"
33 
34 #include "com/sun/star/task/XInteractionAbort.hpp"
35 #include "com/sun/star/task/XInteractionApprove.hpp"
36 #include "com/sun/star/task/XInteractionDisapprove.hpp"
37 #include "com/sun/star/task/XInteractionRetry.hpp"
38 
39 #include "tools/errinf.hxx" // ErrorHandler, ErrorContext, ...
40 #include "svtools/svtools.hrc" // RID_ERRHDL
41 
42 #include "ids.hrc"
43 #include "getcontinuations.hxx"
44 
45 #include "iahndl.hxx"
46 
47 using namespace com::sun::star;
48 
49 namespace {
50 
51 sal_uInt16
52 executeErrorDialog(
53     Window * pParent,
54     task::InteractionClassification eClassification,
55     rtl::OUString const & rContext,
56     rtl::OUString const & rMessage,
57     WinBits nButtonMask)
58     SAL_THROW((uno::RuntimeException))
59 {
60     vos::OGuard aGuard(Application::GetSolarMutex());
61 
62     rtl::OUStringBuffer aText(rContext);
63     if (rContext.getLength() != 0 && rMessage.getLength() != 0)
64         aText.appendAscii(RTL_CONSTASCII_STRINGPARAM(":\n"));
65             //TODO! must be internationalized
66     aText.append(rMessage);
67 
68     std::auto_ptr< MessBox > xBox;
69     try
70     {
71         switch (eClassification)
72         {
73         case task::InteractionClassification_ERROR:
74             xBox.reset(new ErrorBox(pParent,
75                                     nButtonMask,
76                                     aText.makeStringAndClear()));
77             break;
78 
79         case task::InteractionClassification_WARNING:
80             xBox.reset(new WarningBox(pParent,
81                                       nButtonMask,
82                                       aText.makeStringAndClear()));
83             break;
84 
85         case task::InteractionClassification_INFO:
86             if ((nButtonMask & 0x01F00000) == WB_DEF_OK)
87                 //TODO! missing win bit button mask define (want to ignore
88                 // any default button settings)...
89                 xBox.reset(new InfoBox(pParent,
90                                        aText.makeStringAndClear()));
91             else
92                 xBox.reset(new ErrorBox(pParent,
93                                         nButtonMask,
94                                         aText.makeStringAndClear()));
95             break;
96 
97         case task::InteractionClassification_QUERY:
98             xBox.reset(new QueryBox(pParent,
99                                     nButtonMask,
100                                     aText.makeStringAndClear()));
101             break;
102 
103         default:
104             OSL_ASSERT(false);
105             break;
106         }
107     }
108     catch (std::bad_alloc const &)
109     {
110         throw uno::RuntimeException(
111             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("out of memory")),
112             uno::Reference< uno::XInterface >());
113     }
114 
115     sal_uInt16 aResult = xBox->Execute();
116     switch( aResult )
117     {
118     case BUTTONID_OK:
119         aResult = ERRCODE_BUTTON_OK;
120         break;
121     case BUTTONID_CANCEL:
122         aResult = ERRCODE_BUTTON_CANCEL;
123         break;
124     case BUTTONID_YES:
125         aResult = ERRCODE_BUTTON_YES;
126         break;
127     case BUTTONID_NO:
128         aResult = ERRCODE_BUTTON_NO;
129         break;
130     case BUTTONID_RETRY:
131         aResult = ERRCODE_BUTTON_RETRY;
132         break;
133     }
134 
135     return aResult;
136 }
137 
138 }
139 
140 void
141 UUIInteractionHelper::handleErrorHandlerRequest(
142     task::InteractionClassification eClassification,
143     ErrCode nErrorCode,
144     std::vector< rtl::OUString > const & rArguments,
145     uno::Sequence< uno::Reference< task::XInteractionContinuation > > const &
146         rContinuations,
147     bool bObtainErrorStringOnly,
148     bool & bHasErrorString,
149     rtl::OUString & rErrorString)
150         SAL_THROW((uno::RuntimeException))
151 {
152     if (bObtainErrorStringOnly)
153     {
154         bHasErrorString = isInformationalErrorMessageRequest(rContinuations);
155         if (!bHasErrorString)
156             return;
157     }
158 
159     rtl::OUString aMessage;
160     {
161         enum Source { SOURCE_DEFAULT, SOURCE_CNT, SOURCE_SVX, SOURCE_UUI };
162         static char const * const aManager[4]
163             = { CREATEVERSIONRESMGR_NAME(ofa),
164                 CREATEVERSIONRESMGR_NAME(cnt),
165                 CREATEVERSIONRESMGR_NAME(svx),
166                 CREATEVERSIONRESMGR_NAME(uui) };
167         static sal_uInt16 const aId[4]
168             = { RID_ERRHDL,
169                 RID_CHAOS_START + 12,
170                 // cf. chaos/source/inc/cntrids.hrc, where
171                 // #define RID_CHAOS_ERRHDL (RID_CHAOS_START + 12)
172                 RID_SVX_START + 350, // RID_SVXERRCODE
173                 RID_UUI_ERRHDL };
174         ErrCode nErrorId = nErrorCode & ~ERRCODE_WARNING_MASK;
175         Source eSource = nErrorId < ERRCODE_AREA_LIB1 ?
176             SOURCE_DEFAULT :
177             nErrorId >= ERRCODE_AREA_CHAOS
178             && nErrorId < ERRCODE_AREA_CHAOS_END ?
179             SOURCE_CNT :
180             nErrorId >= ERRCODE_AREA_SVX
181             && nErrorId <= ERRCODE_AREA_SVX_END ?
182             SOURCE_SVX :
183             SOURCE_UUI;
184 
185         vos::OGuard aGuard(Application::GetSolarMutex());
186         std::auto_ptr< ResMgr > xManager;
187         xManager.reset(ResMgr::CreateResMgr(aManager[eSource]));
188         if (!xManager.get())
189             return;
190         ResId aResId(aId[eSource], *xManager.get());
191         if (!ErrorResource(aResId).getString(nErrorCode, &aMessage))
192             return;
193     }
194 
195     aMessage = replaceMessageWithArguments( aMessage, rArguments );
196 
197     if (bObtainErrorStringOnly)
198     {
199         rErrorString = aMessage;
200         return;
201     }
202     else
203     {
204         //TODO! It can happen that the buttons calculated below do not match
205         // the error text from the resource (e.g., some text that is not a
206         // question, but YES and NO buttons).  Some error texts have
207         // ExtraData that specifies a set of buttons, but that data is not
208         // really useful, because a single error text may well make sense
209         // both with only an OK button and with RETRY and CANCEL buttons.
210 
211         uno::Reference< task::XInteractionApprove > xApprove;
212         uno::Reference< task::XInteractionDisapprove > xDisapprove;
213         uno::Reference< task::XInteractionRetry > xRetry;
214         uno::Reference< task::XInteractionAbort > xAbort;
215         getContinuations(
216             rContinuations, &xApprove, &xDisapprove, &xRetry, &xAbort);
217 
218         // The following mapping uses the bit mask
219         //     Approve = 8,
220         //     Disapprove = 4,
221         //     Retry = 2,
222         //     Abort = 1
223         //
224         // The mapping has five properties on which the code to select the
225         // correct continuation relies:
226         // 1  The OK button is mapped to Approve if that is available,
227         //    otherwise to Abort if that is available, otherwise to none.
228         // 2  The CANCEL button is always mapped to Abort.
229         // 3  The RETRY button is always mapped to Retry.
230         // 4  The NO button is always mapped to Disapprove.
231         // 5  The YES button is always mapped to Approve.
232         //
233         // Because the WinBits button combinations are quite restricted, not
234         // every request can be served here.
235         //
236         // Finally, it seems to be better to leave default button
237         // determination to VCL (the favouring of CANCEL as default button
238         // seems to not always be what the user wants)...
239         WinBits const aButtonMask[16]
240             = { 0,
241                 WB_OK /*| WB_DEF_OK*/, // Abort
242                 0,
243                 WB_RETRY_CANCEL /*| WB_DEF_CANCEL*/, // Retry, Abort
244                 0,
245                 0,
246                 0,
247                 0,
248                 WB_OK /*| WB_DEF_OK*/, // Approve
249                 WB_OK_CANCEL /*| WB_DEF_CANCEL*/, // Approve, Abort
250                 0,
251                 0,
252                 WB_YES_NO /*| WB_DEF_NO*/, // Approve, Disapprove
253                 WB_YES_NO_CANCEL /*| WB_DEF_CANCEL*/,
254                 // Approve, Disapprove, Abort
255                 0,
256                 0 };
257 
258         WinBits nButtonMask = aButtonMask[(xApprove.is() ? 8 : 0)
259                                           | (xDisapprove.is() ? 4 : 0)
260                                           | (xRetry.is() ? 2 : 0)
261                                           | (xAbort.is() ? 1 : 0)];
262         if (nButtonMask == 0)
263             return;
264 
265         //TODO! remove this backwards compatibility?
266         rtl::OUString aContext(getContextProperty());
267         if (aContext.getLength() == 0 && nErrorCode != 0)
268         {
269             vos::OGuard aGuard(Application::GetSolarMutex());
270             ErrorContext * pContext = ErrorContext::GetContext();
271             if (pContext)
272             {
273                 UniString aContextString;
274                 if (pContext->GetString(nErrorCode, aContextString))
275                     aContext = aContextString;
276             }
277         }
278 
279         sal_uInt16 nResult = executeErrorDialog(
280             getParentProperty(), eClassification, aContext, aMessage, nButtonMask );
281 
282         switch (nResult)
283         {
284         case ERRCODE_BUTTON_OK:
285             OSL_ENSURE(xApprove.is() || xAbort.is(), "unexpected situation");
286             if (xApprove.is())
287                 xApprove->select();
288             else if (xAbort.is())
289                 xAbort->select();
290             break;
291 
292         case ERRCODE_BUTTON_CANCEL:
293             OSL_ENSURE(xAbort.is(), "unexpected situation");
294             if (xAbort.is())
295                 xAbort->select();
296             break;
297 
298         case ERRCODE_BUTTON_RETRY:
299             OSL_ENSURE(xRetry.is(), "unexpected situation");
300             if (xRetry.is())
301                 xRetry->select();
302             break;
303 
304         case ERRCODE_BUTTON_NO:
305             OSL_ENSURE(xDisapprove.is(), "unexpected situation");
306             if (xDisapprove.is())
307                 xDisapprove->select();
308             break;
309 
310         case ERRCODE_BUTTON_YES:
311             OSL_ENSURE(xApprove.is(), "unexpected situation");
312             if (xApprove.is())
313                 xApprove->select();
314             break;
315         }
316 
317     }
318 }
319