1 /**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ErrorReportDialog.cpp
6
7 Dmitry Vedenko
8
9 **********************************************************************/
10
11 #include "ErrorReportDialog.h"
12
13 #include <wx/app.h>
14 #include <wx/artprov.h>
15 #include <wx/button.h>
16 #include <wx/collpane.h>
17 #include <wx/dialog.h>
18 #include <wx/html/htmlwin.h>
19 #include <wx/icon.h>
20 #include <wx/intl.h>
21 #include <wx/settings.h>
22 #include <wx/sizer.h>
23 #include <wx/statbmp.h>
24 #include <wx/stattext.h>
25 #include <wx/statusbr.h>
26 #include <wx/textctrl.h>
27 #include <wx/bmpbuttn.h>
28
29 #include "ui/AccessibleLinksFormatter.h"
30 #include "AllThemeResources.h"
31 #include "Theme.h"
32 #include "HelpText.h"
33 #include "Prefs.h"
34 #include "ShuttleGui.h"
35 #include "HelpSystem.h"
36
37 #include "SentryReport.h"
38 #include "CodeConversions.h"
39
40 namespace
41 {
42 // wxWidgets set an inaccessible border for the wxCollapsiblePane
43 // which makes layout of the dialog even more difficult.
44 // This code was copied from the wxWidgets 3.1.3 collpaneg.cpp
GetCollapsiblePaneBorder(wxWindow * root)45 int GetCollapsiblePaneBorder(wxWindow* root)
46 {
47 #if defined(__WXMAC__)
48 (void)root;
49 return 6;
50 #elif defined(__WXMSW__)
51 return root->ConvertDialogToPixels(wxSize(2, 0)).x;
52 #else
53 (void)root;
54 return 5;
55 #endif
56 }
57 }
58
59 constexpr int MaxUserCommentLength = 2000;
60 constexpr bool ErrorReportDialogHasUserComment = false;
61
BEGIN_EVENT_TABLE(ErrorReportDialog,wxDialogWrapper)62 BEGIN_EVENT_TABLE(ErrorReportDialog, wxDialogWrapper)
63 EVT_BUTTON(wxID_YES, ErrorReportDialog::OnSend)
64 EVT_BUTTON(wxID_NO, ErrorReportDialog::OnDontSend)
65 EVT_BUTTON(wxID_HELP, ErrorReportDialog::OnHelp)
66 END_EVENT_TABLE()
67
68 ErrorReportDialog::ErrorReportDialog(
69 wxWindow* parent, const TranslatableString& dlogTitle,
70 const TranslatableString& message, const ManualPageID& helpUrl,
71 const wxString& log, const bool modal)
72 : wxDialogWrapper(
73 parent, wxID_ANY, dlogTitle, wxDefaultPosition, wxDefaultSize,
74 wxDEFAULT_DIALOG_STYLE)
75 , mHelpUrl(helpUrl)
76 , mIsModal(modal)
77 {
78 audacity::sentry::Exception ex = audacity::sentry::Exception::Create(
79 audacity::ToUTF8(dlogTitle.Debug()),message.Debug());
80
81 if (!log.empty())
82 ex.AddData("log", log);
83
84 mReport = std::make_unique<audacity::sentry::Report> (ex);
85
86 ShuttleGui S(this, eIsCreating);
87
88 const wxFont headingFont = wxFont(wxFontInfo(12).Bold());
89 const wxFont textFont = wxFont(wxFontInfo(10));
90
91 const int CollapsibleBorderSize = GetCollapsiblePaneBorder(this);
92
93 S.SetBorder(0);
94
95 S.StartHorizontalLay(wxEXPAND, 0);
96 {
97 S.AddSpace(40 - CollapsibleBorderSize, 0);
98
99 S.StartVerticalLay(wxEXPAND, 0);
100 {
101 S.AddSpace(0, 32);
102
103 S.StartHorizontalLay(wxEXPAND, 0);
104 {
105 S.AddSpace(CollapsibleBorderSize);
106
107 S.StartVerticalLay(wxEXPAND, 0);
108 {
109 S.StartHorizontalLay(wxEXPAND, 1);
110 {
111 S.StartVerticalLay(0);
112 {
113 wxBitmap bitmap = wxArtProvider::GetBitmap(
114 wxART_WARNING, wxART_MESSAGE_BOX, wxSize(24, 24));
115
116 S.Prop(0).AddWindow(
117 safenew wxStaticBitmap(S.GetParent(), -1, bitmap));
118
119 S.AddSpace(0, 0, 1);
120 }
121 S.EndVerticalLay();
122
123 S.AddSpace(10, 0);
124
125 S.StartVerticalLay(0);
126 {
127 S.AddSpace(0, 7);
128
129 S.Prop(1)
130 .AddVariableText(message, false, 0, 560)
131 ->SetFont(headingFont);
132 }
133 S.EndVerticalLay();
134 }
135 S.EndHorizontalLay();
136
137 S.AddSpace(0, 20);
138
139 S.AddVariableText(XO(
140 "Click \"Send\" to submit the report to Audacity. This information is collected anonymously."))
141 ->SetFont(textFont);
142
143 S.AddSpace(0, 6);
144
145 /* i18n-hint: %s will be replaced with "our Privacy Policy" */
146 AccessibleLinksFormatter privacyPolicy(
147 XO("See %s for more info."));
148
149 privacyPolicy.FormatLink(
150 /* i18n-hint: Title of hyperlink to the privacy policy. This is an object of "See". */
151 wxT("%s"),
152 XO("our Privacy Policy"),
153 "https://www.audacityteam.org/about/desktop-privacy-notice/");
154
155 privacyPolicy.Populate(S);
156 }
157 S.EndVerticalLay();
158 }
159 S.EndHorizontalLay();
160
161 S.AddSpace(0, 6);
162
163 S.StartHorizontalLay(wxEXPAND, 0);
164 {
165 auto pane = safenew wxCollapsiblePane(
166 S.GetParent(), wxID_ANY, XO("Problem details").Translation());
167
168 S.Style(wxEXPAND | wxALIGN_LEFT);
169 S.Prop(1);
170 S.AddWindow(pane);
171
172 ShuttleGui SI(pane->GetPane(), eIsCreating);
173
174 SI.StartVerticalLay();
175 {
176 SI.Style(
177 wxTE_RICH | wxTE_READONLY | wxTE_MULTILINE | wxTE_DONTWRAP)
178 .MinSize(wxSize(0, 152))
179 .Name(XO("Problem details"))
180 .AddTextBox({}, mReport->GetReportPreview(), 0);
181
182 if constexpr (ErrorReportDialogHasUserComment)
183 {
184 SI.AddSpace(0, 20);
185
186 SI.AddVariableText(XO("Comments"))->SetFont(textFont);
187
188 SI.AddSpace(0, 6);
189
190 mCommentsControl = SI.Style(wxTE_MULTILINE)
191 .MinSize(wxSize(0, 76))
192 .Name(XO("Comments"))
193 .AddTextBox({}, {}, 0);
194
195 mCommentsControl->SetMaxLength(MaxUserCommentLength);
196 }
197 }
198 SI.EndVerticalLay();
199 }
200 S.EndHorizontalLay();
201
202 S.AddSpace(0, 20);
203
204 S.StartHorizontalLay(wxEXPAND);
205 {
206 if (!mHelpUrl.empty())
207 {
208 wxBitmapButton* helpButton =
209 S.Id(wxID_HELP).AddBitmapButton(theTheme.Bitmap(bmpHelpIcon));
210 // For screen readers
211 helpButton->SetToolTip(XO("Help").Translation());
212 helpButton->SetLabel(XO("Help").Translation());
213 }
214
215 S.AddSpace(0, 0, 1);
216
217 S.Id(wxID_NO).AddButton(XC("&Don't send", "crash reporter button"));
218
219 S.AddSpace(13, 0);
220
221 S.Id(wxID_YES).AddButton(XC("&Send", "crash reporter button"));
222 }
223 S.EndHorizontalLay();
224
225 S.AddSpace(0, 20);
226 }
227 S.EndVerticalLay();
228
229 S.AddSpace(28, 0);
230 }
231 S.EndHorizontalLay();
232
233 S.SetBorder(2);
234
235 Layout();
236 GetSizer()->Fit(this);
237 SetMinSize(GetSize());
238 Center();
239 }
240
~ErrorReportDialog()241 ErrorReportDialog::~ErrorReportDialog()
242 {
243 }
244
OnSend(wxCommandEvent & event)245 void ErrorReportDialog::OnSend(wxCommandEvent& event)
246 {
247 Disable();
248
249 if (mCommentsControl != nullptr)
250 mReport->AddUserComment(audacity::ToUTF8(mCommentsControl->GetValue()));
251
252 mReport->Send(
253 [this](int code, std::string body) {
254 CallAfter([this]() {
255 EndModal(true);
256 });
257 });
258 }
259
OnDontSend(wxCommandEvent & event)260 void ErrorReportDialog::OnDontSend(wxCommandEvent& event)
261 {
262 EndModal(true);
263 }
264
OnHelp(wxCommandEvent & event)265 void ErrorReportDialog::OnHelp(wxCommandEvent& event)
266 {
267 const auto &helpUrl = mHelpUrl.GET();
268 if (helpUrl.StartsWith(wxT("innerlink:")))
269 {
270 HelpSystem::ShowHtmlText(
271 this, TitleText(helpUrl.Mid(10)), HelpText(helpUrl.Mid(10)), false,
272 true);
273 return;
274 }
275
276 HelpSystem::ShowHelp(this, mHelpUrl, false);
277 }
278