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