1 // Copyright (c) 2011-present The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 5 #ifndef BITCOIN_QT_GUIUTIL_H
6 #define BITCOIN_QT_GUIUTIL_H
7 8 #include <consensus/amount.h>
9 #include <net.h>
10 #include <netaddress.h>
11 #include <util/check.h>
12 #include <util/fs.h>
13 14 #include <QApplication>
15 #include <QEvent>
16 #include <QHeaderView>
17 #include <QItemDelegate>
18 #include <QLabel>
19 #include <QMessageBox>
20 #include <QMetaObject>
21 #include <QObject>
22 #include <QProgressBar>
23 #include <QString>
24 #include <QTableView>
25 26 #include <cassert>
27 #include <chrono>
28 #include <utility>
29 30 class PlatformStyle;
31 class QValidatedLineEdit;
32 class SendCoinsRecipient;
33 34 namespace interfaces
35 {
36 class Node;
37 }
38 39 QT_BEGIN_NAMESPACE
40 class QAbstractButton;
41 class QAbstractItemView;
42 class QAction;
43 class QDateTime;
44 class QDialog;
45 class QFont;
46 class QKeySequence;
47 class QLineEdit;
48 class QMenu;
49 class QPoint;
50 class QProgressDialog;
51 class QUrl;
52 class QWidget;
53 QT_END_NAMESPACE
54 55 /** Utility functions used by the Bitcoin Qt UI.
56 */
57 namespace GUIUtil
58 {
59 // Use this flags to prevent a "What's This" button in the title bar of the dialog on Windows.
60 constexpr auto dialog_flags = Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
61 62 // Create human-readable string from date
63 QString dateTimeStr(const QDateTime &datetime);
64 QString dateTimeStr(qint64 nTime);
65 66 // Return a monospace font
67 QFont fixedPitchFont(bool use_embedded_font = false);
68 69 // Set up widget for address
70 void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent);
71 72 /**
73 * Connects an additional shortcut to a QAbstractButton. Works around the
74 * one shortcut limitation of the button's shortcut property.
75 * @param[in] button QAbstractButton to assign shortcut to
76 * @param[in] shortcut QKeySequence to use as shortcut
77 */
78 void AddButtonShortcut(QAbstractButton* button, const QKeySequence& shortcut);
79 80 // Parse "bitcoin:" URI into recipient object, return true on successful parsing
81 bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out);
82 bool parseBitcoinURI(QString uri, SendCoinsRecipient *out);
83 QString formatBitcoinURI(const SendCoinsRecipient &info);
84 85 // Returns true if given address+amount meets "dust" definition
86 bool isDust(interfaces::Node& node, const QString& address, const CAmount& amount);
87 88 // HTML escaping for rich text controls
89 QString HtmlEscape(const QString& str, bool fMultiLine=false);
90 QString HtmlEscape(const std::string& str, bool fMultiLine=false);
91 92 /** Copy a field of the currently selected entry of a view to the clipboard. Does nothing if nothing
93 is selected.
94 @param[in] column Data column to extract from the model
95 @param[in] role Data role to extract from the model
96 @see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress
97 */
98 void copyEntryData(const QAbstractItemView *view, int column, int role=Qt::EditRole);
99 100 /** Return a field of the currently selected entry as a QString. Does nothing if nothing
101 is selected.
102 @param[in] column Data column to extract from the model
103 @see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress
104 */
105 QList<QModelIndex> getEntryData(const QAbstractItemView *view, int column);
106 107 /** Returns true if the specified field of the currently selected view entry is not empty.
108 @param[in] column Data column to extract from the model
109 @param[in] role Data role to extract from the model
110 @see TransactionView::contextualMenu
111 */
112 bool hasEntryData(const QAbstractItemView *view, int column, int role);
113 114 void setClipboard(const QString& str);
115 116 /**
117 * Loads the font from the file specified by file_name, aborts if it fails.
118 */
119 void LoadFont(const QString& file_name);
120 121 /**
122 * Determine default data directory for operating system.
123 */
124 QString getDefaultDataDirectory();
125 126 /**
127 * Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...).
128 *
129 * @param[in] filter Filter specification such as "Comma Separated Files (*.csv)"
130 * @return QString
131 */
132 QString ExtractFirstSuffixFromFilter(const QString& filter);
133 134 /** Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix
135 when no suffix is provided by the user.
136 137 @param[in] parent Parent window (or 0)
138 @param[in] caption Window caption (or empty, for default)
139 @param[in] dir Starting directory (or empty, to default to documents directory)
140 @param[in] filter Filter specification such as "Comma Separated Files (*.csv)"
141 @param[out] selectedSuffixOut Pointer to return the suffix (file type) that was selected (or 0).
142 Can be useful when choosing the save file format based on suffix.
143 */
144 QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
145 const QString &filter,
146 QString *selectedSuffixOut);
147 148 /** Get open filename, convenience wrapper for QFileDialog::getOpenFileName.
149 150 @param[in] parent Parent window (or 0)
151 @param[in] caption Window caption (or empty, for default)
152 @param[in] dir Starting directory (or empty, to default to documents directory)
153 @param[in] filter Filter specification such as "Comma Separated Files (*.csv)"
154 @param[out] selectedSuffixOut Pointer to return the suffix (file type) that was selected (or 0).
155 Can be useful when choosing the save file format based on suffix.
156 */
157 QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir,
158 const QString &filter,
159 QString *selectedSuffixOut);
160 161 /** Get connection type to call object slot in GUI thread with invokeMethod. The call will be blocking.
162 163 @returns If called from the GUI thread, return a Qt::DirectConnection.
164 If called from another thread, return a Qt::BlockingQueuedConnection.
165 */
166 Qt::ConnectionType blockingGUIThreadConnection();
167 168 // Determine whether a widget is hidden behind other windows
169 bool isObscured(QWidget *w);
170 171 // Activate, show and raise the widget
172 void bringToFront(QWidget* w);
173 174 // Set shortcut to close window
175 void handleCloseWindowShortcut(QWidget* w);
176 177 // Open debug.log
178 void openDebugLogfile();
179 180 // Open the config file
181 bool openBitcoinConf();
182 183 /** Qt event filter that intercepts ToolTipChange events, and replaces the tooltip with a rich text
184 representation if needed. This assures that Qt can word-wrap long tooltip messages.
185 Tooltips longer than the provided size threshold (in characters) are wrapped.
186 */
187 class ToolTipToRichTextFilter : public QObject
188 {
189 Q_OBJECT
190 191 public:
192 explicit ToolTipToRichTextFilter(int size_threshold, QObject *parent = nullptr);
193 194 protected:
195 bool eventFilter(QObject *obj, QEvent *evt) override;
196 197 private:
198 int size_threshold;
199 };
200 201 /**
202 * Qt event filter that intercepts QEvent::FocusOut events for QLabel objects, and
203 * resets their `textInteractionFlags' property to get rid of the visible cursor.
204 *
205 * This is a temporary fix of QTBUG-59514.
206 */
207 class LabelOutOfFocusEventFilter : public QObject
208 {
209 Q_OBJECT
210 211 public:
212 explicit LabelOutOfFocusEventFilter(QObject* parent);
213 bool eventFilter(QObject* watched, QEvent* event) override;
214 };
215 216 bool GetStartOnSystemStartup();
217 bool SetStartOnSystemStartup(bool fAutoStart);
218 219 /** Convert QString to OS specific boost path through UTF-8 */
220 fs::path QStringToPath(const QString &path);
221 222 /** Convert OS specific boost path to QString through UTF-8 */
223 QString PathToQString(const fs::path &path);
224 225 /** Convert enum Network to QString */
226 QString NetworkToQString(Network net);
227 228 /** Convert enum ConnectionType to QString */
229 QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction);
230 231 /// Convert a duration into a QString with days, hours, mins, secs. This ignores sub-seconds.
232 QString formatDurationStr(std::chrono::nanoseconds dur);
233 234 /** Convert peer connection time to a QString denominated in the most relevant unit. */
235 QString FormatPeerAge(NodeClock::time_point connected);
236 237 /** Format CNodeStats.nServices bitmask into a user-readable string */
238 QString formatServicesStr(quint64 mask);
239 240 /// Format a CNodeStats.m_last_ping_time/m_min_ping_time/m_ping_wait into a user-readable string if it exists, or display N/A
241 QString formatPingTime(NodeClock::duration ping_time);
242 243 /** Format a CNodeStateStats.time_offset into a user-readable string */
244 QString formatTimeOffset(int64_t time_offset);
245 246 QString formatNiceTimeOffset(qint64 secs);
247 248 QString formatBytes(uint64_t bytes);
249 250 qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize = 4, qreal startPointSize = 14);
251 252 class ThemedLabel : public QLabel
253 {
254 Q_OBJECT
255 256 public:
257 explicit ThemedLabel(const PlatformStyle* platform_style, QWidget* parent = nullptr);
258 void setThemedPixmap(const QString& image_filename, int width, int height);
259 260 protected:
261 void changeEvent(QEvent* e) override;
262 263 private:
264 const PlatformStyle* m_platform_style;
265 QString m_image_filename;
266 int m_pixmap_width;
267 int m_pixmap_height;
268 void updateThemedPixmap();
269 };
270 271 class ClickableLabel : public ThemedLabel
272 {
273 Q_OBJECT
274 275 public:
276 explicit ClickableLabel(const PlatformStyle* platform_style, QWidget* parent = nullptr);
277 278 Q_SIGNALS:
279 /** Emitted when the label is clicked. The relative mouse coordinates of the click are
280 * passed to the signal.
281 */
282 void clicked(const QPoint& point);
283 protected:
284 void mouseReleaseEvent(QMouseEvent *event) override;
285 };
286 287 class ClickableProgressBar : public QProgressBar
288 {
289 Q_OBJECT
290 291 Q_SIGNALS:
292 /** Emitted when the progressbar is clicked. The relative mouse coordinates of the click are
293 * passed to the signal.
294 */
295 void clicked(const QPoint& point);
296 protected:
297 void mouseReleaseEvent(QMouseEvent *event) override;
298 };
299 300 typedef ClickableProgressBar ProgressBar;
301 302 class ItemDelegate : public QItemDelegate
303 {
304 Q_OBJECT
305 public:
306 ItemDelegate(QObject* parent) : QItemDelegate(parent) {}
307 308 Q_SIGNALS:
309 void keyEscapePressed();
310 311 private:
312 bool eventFilter(QObject *object, QEvent *event) override;
313 };
314 315 // Fix known bugs in QProgressDialog class.
316 void PolishProgressDialog(QProgressDialog* dialog);
317 318 /**
319 * Returns the distance in pixels appropriate for drawing a subsequent character after text.
320 *
321 * In Qt 5.12 and before the QFontMetrics::width() is used and it is deprecated since Qt 5.13.
322 * In Qt 5.11 the QFontMetrics::horizontalAdvance() was introduced.
323 */
324 int TextWidth(const QFontMetrics& fm, const QString& text);
325 326 /**
327 * Writes to debug.log short info about the used Qt and the host system.
328 */
329 void LogQtInfo();
330 331 /**
332 * Call QMenu::popup() only on supported QT_QPA_PLATFORM.
333 */
334 void PopupMenu(QMenu* menu, const QPoint& point, QAction* at_action = nullptr);
335 336 /**
337 * Returns the start-moment of the day in local time.
338 *
339 * QDateTime::QDateTime(const QDate& date) is deprecated since Qt 5.15.
340 * QDate::startOfDay() was introduced in Qt 5.14.
341 */
342 QDateTime StartOfDay(const QDate& date);
343 344 /**
345 * Returns true if pixmap has been set.
346 *
347 * QPixmap* QLabel::pixmap() is deprecated since Qt 5.15.
348 */
349 bool HasPixmap(const QLabel* label);
350 351 /**
352 * Replaces a plain text link with an HTML tagged one.
353 */
354 QString MakeHtmlLink(const QString& source, const QString& link);
355 356 void PrintSlotException(
357 const std::exception* exception,
358 const QObject* sender,
359 const QObject* receiver);
360 361 /**
362 * A drop-in replacement of QObject::connect function
363 * (see: https://doc.qt.io/qt-5/qobject.html#connect-3), that
364 * guaranties that all exceptions are handled within the slot.
365 *
366 * NOTE: This function is incompatible with Qt private signals.
367 */
368 template <typename Sender, typename Signal, typename Receiver, typename Slot>
369 auto ExceptionSafeConnect(
370 Sender sender, Signal signal, Receiver receiver, Slot method,
371 Qt::ConnectionType type = Qt::AutoConnection)
372 {
373 return QObject::connect(
374 sender, signal, receiver,
375 [sender, receiver, method](auto&&... args) {
376 bool ok{true};
377 try {
378 (receiver->*method)(std::forward<decltype(args)>(args)...);
379 } catch (const NonFatalCheckError& e) {
380 PrintSlotException(&e, sender, receiver);
381 ok = QMetaObject::invokeMethod(
382 qApp, "handleNonFatalException",
383 blockingGUIThreadConnection(),
384 Q_ARG(QString, QString::fromStdString(e.what())));
385 } catch (const std::exception& e) {
386 PrintSlotException(&e, sender, receiver);
387 ok = QMetaObject::invokeMethod(
388 qApp, "handleRunawayException",
389 blockingGUIThreadConnection(),
390 Q_ARG(QString, QString::fromStdString(e.what())));
391 } catch (...) {
392 PrintSlotException(nullptr, sender, receiver);
393 ok = QMetaObject::invokeMethod(
394 qApp, "handleRunawayException",
395 blockingGUIThreadConnection(),
396 Q_ARG(QString, "Unknown failure occurred."));
397 }
398 assert(ok);
399 },
400 type);
401 }
402 403 /**
404 * Shows a QDialog instance asynchronously, and deletes it on close.
405 */
406 void ShowModalDialogAsynchronously(QDialog* dialog);
407 408 inline bool IsEscapeOrBack(int key)
409 {
410 if (key == Qt::Key_Escape) return true;
411 #ifdef Q_OS_ANDROID
412 if (key == Qt::Key_Back) return true;
413 #endif // Q_OS_ANDROID
414 return false;
415 }
416 417 QString WalletDisplayName(const std::string& name);
418 QString WalletDisplayName(const QString& name);
419 420 } // namespace GUIUtil
421 422 #endif // BITCOIN_QT_GUIUTIL_H
423