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 <osl/diagnose.h>
21 #include <rtl/instance.hxx>
22 #include <sal/log.hxx>
23
24 #include <tools/debug.hxx>
25 #include <vcl/errinf.hxx>
26
27 #include <algorithm>
28 #include <vector>
29
30 class ErrorHandler;
31
32 namespace {
33
34 class TheErrorRegistry: public rtl::Static<ErrorRegistry, TheErrorRegistry> {};
35
36 }
37
CreateString(const ErrorInfo * pInfo,OUString & rStr)38 bool ErrorStringFactory::CreateString(const ErrorInfo* pInfo, OUString& rStr)
39 {
40 for(const ErrorHandler *pHdlr : TheErrorRegistry::get().errorHandlers)
41 {
42 if(pHdlr->CreateString(pInfo, rStr))
43 return true;
44 }
45 return false;
46 }
47
ErrorRegistry()48 ErrorRegistry::ErrorRegistry()
49 : pDsp(nullptr)
50 , bIsWindowDsp(false)
51 , nNextError(0)
52 {
53 for(DynamicErrorInfo*& rp : ppDynErrInfo)
54 rp = nullptr;
55 }
56
RegisterDisplay(BasicDisplayErrorFunc * aDsp)57 void ErrorRegistry::RegisterDisplay(BasicDisplayErrorFunc *aDsp)
58 {
59 ErrorRegistry &rData = TheErrorRegistry::get();
60 rData.bIsWindowDsp = false;
61 rData.pDsp = reinterpret_cast< DisplayFnPtr >(aDsp);
62 }
63
RegisterDisplay(WindowDisplayErrorFunc * aDsp)64 void ErrorRegistry::RegisterDisplay(WindowDisplayErrorFunc *aDsp)
65 {
66 ErrorRegistry &rData = TheErrorRegistry::get();
67 rData.bIsWindowDsp = true;
68 rData.pDsp = reinterpret_cast< DisplayFnPtr >(aDsp);
69 }
70
Reset()71 void ErrorRegistry::Reset()
72 {
73 ErrorRegistry &rData = TheErrorRegistry::get();
74 rData = ErrorRegistry();
75 }
76
aDspFunc(const OUString & rErr,const OUString & rAction)77 static void aDspFunc(const OUString &rErr, const OUString &rAction)
78 {
79 SAL_WARN("vcl", "Action: " << rAction << " Error: " << rErr);
80 }
81
ErrorHandler()82 ErrorHandler::ErrorHandler()
83 {
84 ErrorRegistry &rData = TheErrorRegistry::get();
85 rData.errorHandlers.insert(rData.errorHandlers.begin(), this);
86
87 if(!rData.pDsp)
88 ErrorRegistry::RegisterDisplay(&aDspFunc);
89 }
90
~ErrorHandler()91 ErrorHandler::~ErrorHandler()
92 {
93 auto &rErrorHandlers = TheErrorRegistry::get().errorHandlers;
94 rErrorHandlers.erase( ::std::remove(rErrorHandlers.begin(), rErrorHandlers.end(), this),
95 rErrorHandlers.end());
96 }
97
GetErrorString(ErrCode nErrCodeId,OUString & rErrStr)98 bool ErrorHandler::GetErrorString(ErrCode nErrCodeId, OUString& rErrStr)
99 {
100 OUString aErr;
101
102 if(!nErrCodeId || nErrCodeId == ERRCODE_ABORT)
103 return false;
104
105 std::unique_ptr<ErrorInfo> pInfo = ErrorInfo::GetErrorInfo(nErrCodeId);
106
107 if (ErrorStringFactory::CreateString(pInfo.get(),aErr))
108 {
109 rErrStr = aErr;
110 return true;
111 }
112
113 return false;
114 }
115
HandleError(ErrCode nErrCodeId,weld::Window * pParent,DialogMask nFlags)116 DialogMask ErrorHandler::HandleError(ErrCode nErrCodeId, weld::Window *pParent, DialogMask nFlags)
117 {
118 if (nErrCodeId == ERRCODE_NONE || nErrCodeId == ERRCODE_ABORT)
119 return DialogMask::NONE;
120
121 ErrorRegistry &rData = TheErrorRegistry::get();
122 std::unique_ptr<ErrorInfo> pInfo = ErrorInfo::GetErrorInfo(nErrCodeId);
123 OUString aAction;
124
125 if (!rData.contexts.empty())
126 {
127 rData.contexts.front()->GetString(pInfo->GetErrorCode(), aAction);
128
129 for(ErrorContext *pCtx : rData.contexts)
130 {
131 if(pCtx->GetParent())
132 {
133 pParent = pCtx->GetParent();
134 break;
135 }
136 }
137 }
138
139 bool bWarning = nErrCodeId.IsWarning();
140 DialogMask nErrFlags = DialogMask::ButtonDefaultsOk | DialogMask::ButtonsOk;
141 if (bWarning)
142 nErrFlags |= DialogMask::MessageWarning;
143 else
144 nErrFlags |= DialogMask::MessageError;
145
146 DynamicErrorInfo* pDynPtr = dynamic_cast<DynamicErrorInfo*>(pInfo.get());
147 if(pDynPtr)
148 {
149 DialogMask nDynFlags = pDynPtr->GetDialogMask();
150 if( nDynFlags != DialogMask::NONE )
151 nErrFlags = nDynFlags;
152 }
153
154 OUString aErr;
155 if (ErrorStringFactory::CreateString(pInfo.get(), aErr))
156 {
157 if(!rData.pDsp)
158 {
159 SAL_WARN( "vcl", "Action: " << aAction << "Error: " << aErr);
160 }
161 else
162 {
163 if(!rData.bIsWindowDsp)
164 {
165 (*reinterpret_cast<BasicDisplayErrorFunc*>(rData.pDsp))(aErr,aAction);
166 return DialogMask::NONE;
167 }
168 else
169 {
170 if (nFlags != DialogMask::MAX)
171 nErrFlags = nFlags;
172
173 return (*reinterpret_cast<WindowDisplayErrorFunc*>(rData.pDsp))(
174 pParent, nErrFlags, aErr, aAction);
175 }
176 }
177 }
178
179 SAL_WARN( "vcl", "Error not handled " << pInfo->GetErrorCode());
180 // Error 1 (ERRCODE_ABORT) is classified as a General Error in sfx
181 if (pInfo->GetErrorCode() != ERRCODE_ABORT)
182 HandleError(ERRCODE_ABORT);
183 else
184 OSL_FAIL("ERRCODE_ABORT not handled");
185
186 return DialogMask::NONE;
187 }
188
189 struct ImplErrorContext
190 {
191 weld::Window *pWin;
192 };
193
ErrorContext(weld::Window * pWinP)194 ErrorContext::ErrorContext(weld::Window *pWinP)
195 : pImpl( new ImplErrorContext )
196 {
197 pImpl->pWin = pWinP;
198 TheErrorRegistry::get().contexts.insert(TheErrorRegistry::get().contexts.begin(), this);
199 }
200
~ErrorContext()201 ErrorContext::~ErrorContext()
202 {
203 auto &rContexts = TheErrorRegistry::get().contexts;
204 rContexts.erase( ::std::remove(rContexts.begin(), rContexts.end(), this), rContexts.end());
205 }
206
GetContext()207 ErrorContext *ErrorContext::GetContext()
208 {
209 return TheErrorRegistry::get().contexts.empty() ? nullptr : TheErrorRegistry::get().contexts.front();
210 }
211
GetParent()212 weld::Window* ErrorContext::GetParent()
213 {
214 return pImpl ? pImpl->pWin : nullptr;
215 }
216
217 class ImplDynamicErrorInfo
218 {
219 friend class DynamicErrorInfo;
220 friend class ErrorInfo;
221
222 private:
ImplDynamicErrorInfo(DialogMask nInMask)223 explicit ImplDynamicErrorInfo(DialogMask nInMask)
224 : nMask(nInMask)
225 {
226 }
227 void RegisterError(DynamicErrorInfo *);
228 static void UnRegisterError(DynamicErrorInfo const *);
229 static std::unique_ptr<ErrorInfo> GetDynamicErrorInfo(ErrCode nId);
230
231 ErrCode nErrId;
232 DialogMask nMask;
233
234 };
235
RegisterError(DynamicErrorInfo * pDynErrInfo)236 void ImplDynamicErrorInfo::RegisterError(DynamicErrorInfo *pDynErrInfo)
237 {
238 // Register dynamic identifier
239 ErrorRegistry& rData = TheErrorRegistry::get();
240 nErrId = ErrCode(((sal_uInt32(rData.nNextError) + 1) << ERRCODE_DYNAMIC_SHIFT) +
241 sal_uInt32(pDynErrInfo->GetErrorCode()));
242
243 if(rData.ppDynErrInfo[rData.nNextError])
244 delete rData.ppDynErrInfo[rData.nNextError];
245
246 rData.ppDynErrInfo[rData.nNextError] = pDynErrInfo;
247
248 if(++rData.nNextError>=ERRCODE_DYNAMIC_COUNT)
249 rData.nNextError=0;
250 }
251
UnRegisterError(DynamicErrorInfo const * pDynErrInfo)252 void ImplDynamicErrorInfo::UnRegisterError(DynamicErrorInfo const *pDynErrInfo)
253 {
254 DynamicErrorInfo **ppDynErrInfo = TheErrorRegistry::get().ppDynErrInfo;
255 sal_uInt32 nIdx = ErrCode(*pDynErrInfo).GetDynamic() - 1;
256 DBG_ASSERT(ppDynErrInfo[nIdx] == pDynErrInfo, "ErrHdl: Error not found");
257
258 if(ppDynErrInfo[nIdx]==pDynErrInfo)
259 ppDynErrInfo[nIdx]=nullptr;
260 }
261
GetDynamicErrorInfo(ErrCode nId)262 std::unique_ptr<ErrorInfo> ImplDynamicErrorInfo::GetDynamicErrorInfo(ErrCode nId)
263 {
264 sal_uInt32 nIdx = nId.GetDynamic() - 1;
265 DynamicErrorInfo* pDynErrInfo = TheErrorRegistry::get().ppDynErrInfo[nIdx];
266
267 if(pDynErrInfo && ErrCode(*pDynErrInfo)==nId)
268 return std::unique_ptr<ErrorInfo>(pDynErrInfo);
269 else
270 return std::make_unique<ErrorInfo>(nId.StripDynamic());
271 }
272
GetErrorInfo(ErrCode nId)273 std::unique_ptr<ErrorInfo> ErrorInfo::GetErrorInfo(ErrCode nId)
274 {
275 if(nId.IsDynamic())
276 return ImplDynamicErrorInfo::GetDynamicErrorInfo(nId);
277 else
278 return std::make_unique<ErrorInfo>(nId);
279 }
280
~ErrorInfo()281 ErrorInfo::~ErrorInfo()
282 {
283 }
284
DynamicErrorInfo(ErrCode nArgUserId,DialogMask nMask)285 DynamicErrorInfo::DynamicErrorInfo(ErrCode nArgUserId, DialogMask nMask)
286 : ErrorInfo(nArgUserId),
287 pImpl(new ImplDynamicErrorInfo(nMask))
288 {
289 pImpl->RegisterError(this);
290 }
291
~DynamicErrorInfo()292 DynamicErrorInfo::~DynamicErrorInfo()
293 {
294 ImplDynamicErrorInfo::UnRegisterError(this);
295 }
296
operator ErrCode() const297 DynamicErrorInfo::operator ErrCode() const
298 {
299 return pImpl->nErrId;
300 }
301
GetDialogMask() const302 DialogMask DynamicErrorInfo::GetDialogMask() const
303 {
304 return pImpl->nMask;
305 }
306
StringErrorInfo(ErrCode nArgUserId,const OUString & aStringP,DialogMask nMask)307 StringErrorInfo::StringErrorInfo(
308 ErrCode nArgUserId, const OUString& aStringP, DialogMask nMask)
309 : DynamicErrorInfo(nArgUserId, nMask), aString(aStringP)
310 {
311 }
312
313 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
314