1 /*!********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 @file BasicUI.h
6 @brief Toolkit-neutral facade for basic user interface services
7 
8 Paul Licameli
9 
10 **********************************************************************/
11 #ifndef __AUDACITY_BASIC_UI__
12 #define __AUDACITY_BASIC_UI__
13 
14 #include <functional>
15 #include <memory>
16 #include "Identifier.h"
17 #include "Internat.h"
18 
19 namespace BasicUI {
20 
21 //! @name Types used in the Services interface
22 //! @{
23 
24 using Action = std::function<void()>;
25 
26 //! Subclasses may hold information such as a parent window pointer for a dialog.
27 /*! The default-constructed empty value of this base class must be accepted by overrides of methods of
28  Services */
29 class BASIC_UI_API WindowPlacement {
30 public:
31    WindowPlacement() = default;
32 
33    //! Don't slice
34    WindowPlacement( const WindowPlacement& ) PROHIBITED;
35    //! Don't slice
36    WindowPlacement &operator=( const WindowPlacement& ) PROHIBITED;
37    virtual ~WindowPlacement();
38 };
39 
40 enum class ErrorDialogType {
41    ModelessError,
42    ModalError,
43    ModalErrorReport, /*!< If error reporting is enabled, may give option to
44                       send; if not, then like ModalError
45                       */
46 };
47 
48 //! Options for variations of error dialogs; the default is for modal dialogs
49 struct ErrorDialogOptions {
50 
51    ErrorDialogOptions() = default;
52    //! Non-explicit
ErrorDialogOptionsErrorDialogOptions53    ErrorDialogOptions( ErrorDialogType type ) : type{ type } {}
54 
55    //! @name Chain-call style initializers
56    //! @{
57 
ModalHelpErrorDialogOptions58    ErrorDialogOptions &&ModalHelp( bool modalHelp_ ) &&
59    { modalHelp = modalHelp_; return std::move(*this); }
60 
LogErrorDialogOptions61    ErrorDialogOptions &&Log( std::wstring log_ ) &&
62    { log = std::move(log_); return std::move(*this); }
63 
64    //! @}
65 
66    //! Type of help dialog
67    ErrorDialogType type{ ErrorDialogType::ModalError };
68    //! Whether the secondary help dialog with more information should be modal
69    bool modalHelp{ true };
70    //! Optional extra logging information to be shown
71    std::wstring log;
72 };
73 
74 //! "Message", suitably translated
75 BASIC_UI_API TranslatableString DefaultCaption();
76 
77 enum class Icon {
78    None,
79    Warning,
80    Error,
81    Question,
82    Information,
83 };
84 
85 enum class Button {
86    Default, //!< Like Ok, except maybe minor difference of dialog position
87    Ok,      //!< One button
88    YesNo    //!< Two buttons
89 };
90 
91 struct MessageBoxOptions {
92    //! @name Chain-call style initializers
93    //! @{
94 
ParentMessageBoxOptions95    MessageBoxOptions &&Parent(WindowPlacement *parent_) &&
96    { parent = parent_; return std::move(*this); }
97 
CaptionMessageBoxOptions98    MessageBoxOptions &&Caption(TranslatableString caption_) &&
99    { caption = std::move(caption_); return std::move(*this); }
100 
IconStyleMessageBoxOptions101    MessageBoxOptions &&IconStyle(Icon style) &&
102    { iconStyle = style; return std::move(*this); }
103 
ButtonStyleMessageBoxOptions104    MessageBoxOptions &&ButtonStyle(Button style) &&
105    { buttonStyle = style; return std::move(*this); }
106 
107    //! Override the usual defaulting to Yes; affects only the YesNo case
DefaultIsNoMessageBoxOptions108    MessageBoxOptions &&DefaultIsNo() &&
109    { yesOrOkDefaultButton = false; return std::move(*this); }
110 
CancelButtonMessageBoxOptions111    MessageBoxOptions &&CancelButton() &&
112    { cancelButton = true; return std::move(*this); }
113 
114    //! Center the dialog on its parent window, if any
CenteredMessageBoxOptions115    MessageBoxOptions &&Centered() &&
116    { centered = true; return std::move(*this); }
117 
118    //! @}
119 
120    WindowPlacement *parent{ nullptr };
121    TranslatableString caption{ DefaultCaption() };
122    Icon iconStyle{ Icon::None };
123    Button buttonStyle{ Button::Default };
124    bool yesOrOkDefaultButton{ true };
125    bool cancelButton{ false };
126    bool centered{ false };
127 };
128 
129 enum class MessageBoxResult : int {
130    None, //!< May be returned if no Services are installed
131    Yes,
132    No,
133    Ok,
134    Cancel,
135 };
136 
137 enum ProgressDialogOptions : unsigned {
138    ProgressShowStop            = (1 << 0),
139    ProgressShowCancel          = (1 << 1),
140    ProgressHideTime            = (1 << 2),
141    ProgressConfirmStopOrCancel = (1 << 3),
142 };
143 
144 enum class ProgressResult : unsigned
145 {
146    Cancelled = 0, //<! User says that whatever is happening is undesirable and shouldn't have happened at all
147    Success,       //<! User says nothing, everything works fine, continue doing whatever we're doing
148    Failed,        //<! Something has gone wrong, we should stop and cancel everything we did
149    Stopped        //<! Nothing is wrong, but user says we should stop now and leave things as they are now
150 };
151 
152 //! Abstraction of a progress dialog with well defined time-to-completion estimate
153 class BASIC_UI_API ProgressDialog
154 {
155 public:
156    virtual ~ProgressDialog();
157 
158    //! Update the bar and poll for clicks.  Call only on the main thread.
159    virtual ProgressResult Poll(
160       unsigned long long numerator,
161       unsigned long long denominator,
162       const TranslatableString &message = {}) = 0;
163 };
164 
165 //! Abstraction of a progress dialog with undefined time-to-completion estimate
166 class BASIC_UI_API GenericProgressDialog
167 {
168 public:
169    virtual ~GenericProgressDialog();
170    //! Give some visual indication of progress.  Call only on the main thread.
171    virtual void Pulse() = 0;
172 };
173 
174 //! @}
175 
176 //! Abstract class defines a few user interface services, not mentioning particular toolkits
177 /*! The intention is that the application supplies a concrete implementation at
178  startup.  Most code will not use this class directly, but call the inline
179  functions that follow. */
180 class BASIC_UI_API Services {
181 public:
182    virtual ~Services();
183    virtual void DoCallAfter(const Action &action) = 0;
184    virtual void DoYield() = 0;
185    virtual void DoShowErrorDialog(const WindowPlacement &placement,
186       const TranslatableString &dlogTitle,
187       const TranslatableString &message,
188       const ManualPageID &helpPage,
189       const ErrorDialogOptions &options) = 0;
190    virtual MessageBoxResult DoMessageBox(
191       const TranslatableString& message,
192       MessageBoxOptions options) = 0;
193    virtual std::unique_ptr<ProgressDialog>
194    DoMakeProgress(const TranslatableString &title,
195       const TranslatableString &message,
196       unsigned flag,
197       const TranslatableString &remainingLabelText) = 0;
198    virtual std::unique_ptr<GenericProgressDialog>
199    DoMakeGenericProgress(const WindowPlacement &placement,
200       const TranslatableString &title,
201       const TranslatableString &message) = 0;
202 };
203 
204 //! Fetch the global instance, or nullptr if none is yet installed
205 BASIC_UI_API Services *Get();
206 
207 //! Install an implementation; return the previously installed instance
208 BASIC_UI_API Services *Install(Services *pInstance);
209 
210 /*! @name Functions that invoke global Services
211    These dispatch to the global Services, if supplied.  If none was supplied,
212    they are mostly no-ops, with exceptions as noted.  All should be called on
213    the main thread only, except as noted.
214  */
215 //! @{
216 
217 //! Schedule an action to be done later, and in the main thread
218 /*! This function may be called in other threads than the main.  If no Services
219  are yet installed, the action is not lost, but may be dispatched by Yield().
220  The action may itself invoke CallAfter to enqueue other actions.
221  */
222 BASIC_UI_API void CallAfter(Action action);
223 
224 //! Dispatch waiting events, including actions enqueued by CallAfter
225 /*! This function must be called by the main thread.  Actions enqueued by
226  CallAfter before Services were installed will be dispatched in the sequence
227  they were enqueued, unless an exception thrown by one of them stops the
228  dispatching.
229  */
230 BASIC_UI_API void Yield();
231 
232 //! Show an error dialog with a link to the manual for further help
233 inline void ShowErrorDialog(
234    const WindowPlacement &placement,    //!< how to parent the dialog
235    const TranslatableString &dlogTitle, //!< Text for title bar
236    const TranslatableString &message,   //!< The main message text
237    const ManualPageID &helpPage,          //!< Identifies manual page (and maybe an anchor)
238    const ErrorDialogOptions &options = {})
239 {
240    if (auto p = Get())
241       p->DoShowErrorDialog(placement, dlogTitle, message, helpPage, options);
242 }
243 
244 //! Show a modal message box with either Ok or Yes and No, and optionally Cancel
245 /*!
246  @return indicates which button was pressed
247  */
248 inline MessageBoxResult ShowMessageBox( const TranslatableString &message,
249    MessageBoxOptions options = {})
250 {
251    if (auto p = Get())
252       return p->DoMessageBox(message, std::move(options));
253    else
254       return MessageBoxResult::None;
255 }
256 
257 //! Create and display a progress dialog
258 /*!
259  @param flags bitwise OR of values in ProgressDialogOptions
260  @param remainingLabelText if not empty substitutes for "Remaining Time:"
261  @return nullptr if Services not installed
262  */
263 inline std::unique_ptr<ProgressDialog> MakeProgress(
264    const TranslatableString & title,
265    const TranslatableString & message,
266    unsigned flags = (ProgressShowStop | ProgressShowCancel),
267    const TranslatableString & remainingLabelText = {})
268 {
269    if (auto p = Get())
270       return p->DoMakeProgress(title, message, flags, remainingLabelText);
271    else
272       return nullptr;
273 }
274 
275 //! Create and display a progress dialog (return nullptr if Services not installed)
276 /*!
277  This function makes a "generic" progress dialog, for the case when time
278  to completion cannot be estimated, but some indication of progress is still
279  given
280  */
MakeGenericProgress(const WindowPlacement & placement,const TranslatableString & title,const TranslatableString & message)281 inline std::unique_ptr<GenericProgressDialog> MakeGenericProgress(
282    const WindowPlacement &placement,
283    const TranslatableString &title, const TranslatableString &message)
284 {
285    if (auto p = Get())
286       return p->DoMakeGenericProgress(placement, title, message);
287    else
288       return nullptr;
289 }
290 
291 //! @}
292 }
293 
294 #endif
295