Audacity  3.0.3
AccessibleLinksFormatter.cpp
Go to the documentation of this file.
1 /*!********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  @file AccessibleLinksFormatter.h
6  @brief Define a helper class to format text with link in a way, accessible to VI users.
7 
8  Dmitry Vedenko
9  **********************************************************************/
10 
12 
13 #include "ShuttleGui.h"
14 
15 #include <unordered_map>
16 #include <algorithm>
17 
18 #include <wx/hyperlink.h>
19 
20 
21 namespace
22 {
23 size_t OffsetPosition(size_t position, size_t length)
24 {
25  if (position == wxString::npos)
26  return wxString::npos;
27 
28  return position + length;
29 }
30 }
31 
33  : mMessage(std::move(message))
34 {
35 }
36 
38  wxString placeholder, TranslatableString value, std::string targetURL)
39 {
40  mFormatArguments.push_back({
41  std::move(placeholder),
42  std::move(value),
43  {},
44  std::move(targetURL)
45  });
46 
47  return *this;
48 }
49 
51  wxString placeholder, TranslatableString value,
52  LinkClickedHandler handler)
53 {
54  mFormatArguments.push_back({
55  std::move(placeholder),
56  std::move(value),
57  std::move(handler),
58  {}
59  });
60 
61  return *this;
62 }
63 
65 {
66  // Just add the text, if there are no links to format
67  if (mFormatArguments.empty())
68  {
70  return;
71  }
72 
73  wxString translated = mMessage.Translation();
74 
75  std::vector<ProcessedArgument> processedArguments =
76  ProcessArguments(translated);
77 
78  if (processedArguments.empty())
79  {
81  return;
82  }
83 
84  const int borderSize = S.GetBorder();
85 
86  S.StartHorizontalLay(wxEXPAND);
87  {
88  S.SetBorder(0);
89  S.AddSpace(borderSize);
90 
91  S.StartWrapLay(wxEXPAND, 1);
92  {
93  size_t currentPosition = 0;
94 
95  for (const ProcessedArgument& processedArgument : processedArguments)
96  {
97  const FormatArgument* argument = processedArgument.Argument;
98 
99  // Add everything between currentPosition and PlaceholderPosition
100 
101  if (currentPosition != processedArgument.PlaceholderPosition)
102  {
103  const size_t substrLength =
104  processedArgument.PlaceholderPosition - currentPosition;
105 
106  S.Prop(0).AddFixedText(
107  Verbatim(translated.substr(currentPosition, substrLength)));
108  }
109 
110  // Add hyperlink
111 
112  wxHyperlinkCtrl* hyperlink = safenew wxHyperlinkCtrl(
113  S.GetParent(), wxID_ANY, argument->Value.Translation(),
114  argument->TargetURL);
115 
116  if (argument->Handler)
117  {
118  hyperlink->Bind(
119  wxEVT_HYPERLINK, [handler = argument->Handler](wxHyperlinkEvent& evt) {
120  handler();
121  });
122  }
123 
124  S.AddWindow(hyperlink, wxALIGN_TOP | wxALIGN_LEFT);
125 
126  // Update the currentPostion to the first symbol after the
127  // Placeholder
128 
130  processedArgument.PlaceholderPosition,
131  argument->Placeholder.Length());
132 
133  if (currentPosition >= translated.Length())
134  break;
135  }
136 
137  if (currentPosition < translated.Length())
138  S.AddFixedText(Verbatim(translated.substr(currentPosition)));
139  }
140  S.EndWrapLay();
141  }
142  S.EndHorizontalLay();
143 
144  S.SetBorder(borderSize);
145 }
146 
147 std::vector<AccessibleLinksFormatter::ProcessedArgument>
148 AccessibleLinksFormatter::ProcessArguments(wxString translatedMessage) const
149 {
150  std::vector<ProcessedArgument> result;
151  result.reserve(mFormatArguments.size());
152  // Arguments with the same placeholder are processed left-to-right.
153  // Lets track the last known position of the placeholder
154  std::unordered_map<wxString, size_t> knownPlaceholderPosition;
155 
156  for (const FormatArgument& argument : mFormatArguments)
157  {
158  auto it = knownPlaceholderPosition.find(argument.Placeholder);
159 
160  const size_t startingPosition =
161  it != knownPlaceholderPosition.end() ?
162  OffsetPosition(it->second, argument.Placeholder.length()) :
163  0;
164 
165  const size_t placeholderPosition =
166  startingPosition == wxString::npos ?
167  wxString::npos :
168  translatedMessage.find(argument.Placeholder, startingPosition);
169 
170  knownPlaceholderPosition[argument.Placeholder] = placeholderPosition;
171 
172  if (placeholderPosition != wxString::npos)
173  {
174  result.emplace_back(
175  ProcessedArgument { &argument, placeholderPosition });
176  }
177  }
178 
179  std::sort(
180  result.begin(), result.end(),
181  [](const ProcessedArgument& lhs, const ProcessedArgument& rhs) {
182  return lhs.PlaceholderPosition < rhs.PlaceholderPosition;
183  });
184 
185  return result;
186 }
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
currentPosition
size_t currentPosition
Definition: ScripterCallback.cpp:62
ShuttleGuiBase::EndWrapLay
void EndWrapLay()
Definition: ShuttleGui.cpp:1221
ShuttleGui::AddSpace
wxSizerItem * AddSpace(int width, int height, int prop=0)
Definition: ShuttleGui.cpp:2447
ShuttleGuiBase::GetBorder
int GetBorder() const noexcept
Definition: ShuttleGui.cpp:196
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1177
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1167
ShuttleGuiBase::AddFixedText
void AddFixedText(const TranslatableString &Str, bool bCenter=false, int wrapWidth=0)
Definition: ShuttleGui.cpp:440
ShuttleGuiBase::GetParent
wxWindow * GetParent()
Definition: ShuttleGui.h:496
ShuttleGuiBase::AddWindow
wxWindow * AddWindow(wxWindow *pWindow, int PositionFlags=wxALIGN_CENTRE)
Definition: ShuttleGui.cpp:299
ShuttleGui.h
ShuttleGui::Prop
ShuttleGui & Prop(int iProp)
Definition: ShuttleGui.h:725
Verbatim
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
Definition: TranslatableString.h:321
TranslatableString::Translation
wxString Translation() const
Definition: TranslatableString.h:79
ShuttleGuiBase::SetBorder
void SetBorder(int Border)
Definition: ShuttleGui.h:489
safenew
#define safenew
Definition: MemoryX.h:10
ShuttleGuiBase::StartWrapLay
void StartWrapLay(int PositionFlags=wxEXPAND, int iProp=0)
Definition: ShuttleGui.cpp:1210
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:631