LCOV - code coverage report
Current view: top level - src/qt - guiutil.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 35 309 11.3 %
Date: 2015-10-12 22:39:14 Functions: 6 48 12.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2013 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             : #include "guiutil.h"
       6             : 
       7             : #include "bitcoinaddressvalidator.h"
       8             : #include "bitcoinunits.h"
       9             : #include "qvalidatedlineedit.h"
      10             : #include "walletmodel.h"
      11             : 
      12             : #include "primitives/transaction.h"
      13             : #include "init.h"
      14             : #include "main.h" // For minRelayTxFee
      15             : #include "protocol.h"
      16             : #include "script/script.h"
      17             : #include "script/standard.h"
      18             : #include "util.h"
      19             : 
      20             : #ifdef WIN32
      21             : #ifdef _WIN32_WINNT
      22             : #undef _WIN32_WINNT
      23             : #endif
      24             : #define _WIN32_WINNT 0x0501
      25             : #ifdef _WIN32_IE
      26             : #undef _WIN32_IE
      27             : #endif
      28             : #define _WIN32_IE 0x0501
      29             : #define WIN32_LEAN_AND_MEAN 1
      30             : #ifndef NOMINMAX
      31             : #define NOMINMAX
      32             : #endif
      33             : #include "shellapi.h"
      34             : #include "shlobj.h"
      35             : #include "shlwapi.h"
      36             : #endif
      37             : 
      38             : #include <boost/filesystem.hpp>
      39             : #include <boost/filesystem/fstream.hpp>
      40             : #if BOOST_FILESYSTEM_VERSION >= 3
      41             : #include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
      42             : #endif
      43             : #include <boost/scoped_array.hpp>
      44             : 
      45             : #include <QAbstractItemView>
      46             : #include <QApplication>
      47             : #include <QClipboard>
      48             : #include <QDateTime>
      49             : #include <QDesktopServices>
      50             : #include <QDesktopWidget>
      51             : #include <QDoubleValidator>
      52             : #include <QFileDialog>
      53             : #include <QFont>
      54             : #include <QLineEdit>
      55             : #include <QSettings>
      56             : #include <QTextDocument> // for Qt::mightBeRichText
      57             : #include <QThread>
      58             : 
      59             : #if QT_VERSION < 0x050000
      60             : #include <QUrl>
      61             : #else
      62             : #include <QUrlQuery>
      63             : #endif
      64             : 
      65             : #if BOOST_FILESYSTEM_VERSION >= 3
      66           1 : static boost::filesystem::detail::utf8_codecvt_facet utf8;
      67             : #endif
      68             : 
      69             : #if defined(Q_OS_MAC)
      70             : extern double NSAppKitVersionNumber;
      71             : #if !defined(NSAppKitVersionNumber10_8)
      72             : #define NSAppKitVersionNumber10_8 1187
      73             : #endif
      74             : #if !defined(NSAppKitVersionNumber10_9)
      75             : #define NSAppKitVersionNumber10_9 1265
      76             : #endif
      77             : #endif
      78             : 
      79             : namespace GUIUtil {
      80             : 
      81           0 : QString dateTimeStr(const QDateTime &date)
      82             : {
      83           0 :     return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
      84             : }
      85             : 
      86           0 : QString dateTimeStr(qint64 nTime)
      87             : {
      88           0 :     return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
      89             : }
      90             : 
      91           0 : QFont bitcoinAddressFont()
      92             : {
      93           0 :     QFont font("Monospace");
      94             : #if QT_VERSION >= 0x040800
      95           0 :     font.setStyleHint(QFont::Monospace);
      96             : #else
      97             :     font.setStyleHint(QFont::TypeWriter);
      98             : #endif
      99           0 :     return font;
     100             : }
     101             : 
     102           0 : void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
     103             : {
     104           0 :     parent->setFocusProxy(widget);
     105             : 
     106           0 :     widget->setFont(bitcoinAddressFont());
     107             : #if QT_VERSION >= 0x040700
     108             :     // We don't want translators to use own addresses in translations
     109             :     // and this is the only place, where this address is supplied.
     110           0 :     widget->setPlaceholderText(QObject::tr("Enter a Bitcoin address (e.g. %1)").arg("1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"));
     111             : #endif
     112           0 :     widget->setValidator(new BitcoinAddressEntryValidator(parent));
     113           0 :     widget->setCheckValidator(new BitcoinAddressCheckValidator(parent));
     114           0 : }
     115             : 
     116           0 : void setupAmountWidget(QLineEdit *widget, QWidget *parent)
     117             : {
     118           0 :     QDoubleValidator *amountValidator = new QDoubleValidator(parent);
     119           0 :     amountValidator->setDecimals(8);
     120           0 :     amountValidator->setBottom(0.0);
     121           0 :     widget->setValidator(amountValidator);
     122           0 :     widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
     123           0 : }
     124             : 
     125          11 : bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
     126             : {
     127             :     // return if URI is not valid or is no bitcoin: URI
     128          33 :     if(!uri.isValid() || uri.scheme() != QString("bitcoin"))
     129             :         return false;
     130             : 
     131          11 :     SendCoinsRecipient rv;
     132          11 :     rv.address = uri.path();
     133             :     // Trim any following forward slash which may have been added by the OS
     134          11 :     if (rv.address.endsWith("/")) {
     135           0 :         rv.address.truncate(rv.address.length() - 1);
     136             :     }
     137          11 :     rv.amount = 0;
     138             : 
     139             : #if QT_VERSION < 0x050000
     140          22 :     QList<QPair<QString, QString> > items = uri.queryItems();
     141             : #else
     142             :     QUrlQuery uriQuery(uri);
     143             :     QList<QPair<QString, QString> > items = uriQuery.queryItems();
     144             : #endif
     145          29 :     for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
     146             :     {
     147          12 :         bool fShouldReturnFalse = false;
     148          12 :         if (i->first.startsWith("req-"))
     149             :         {
     150           2 :             i->first.remove(0, 4);
     151             :             fShouldReturnFalse = true;
     152             :         }
     153             : 
     154          24 :         if (i->first == "label")
     155             :         {
     156           2 :             rv.label = i->second;
     157             :             fShouldReturnFalse = false;
     158             :         }
     159          24 :         if (i->first == "message")
     160             :         {
     161           3 :             rv.message = i->second;
     162             :             fShouldReturnFalse = false;
     163             :         }
     164          18 :         else if (i->first == "amount")
     165             :         {
     166          10 :             if(!i->second.isEmpty())
     167             :             {
     168           5 :                 if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
     169             :                 {
     170             :                     return false;
     171             :                 }
     172             :             }
     173             :             fShouldReturnFalse = false;
     174             :         }
     175             : 
     176          10 :         if (fShouldReturnFalse)
     177             :             return false;
     178             :     }
     179           8 :     if(out)
     180             :     {
     181           8 :         *out = rv;
     182             :     }
     183          11 :     return true;
     184             : }
     185             : 
     186           1 : bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
     187             : {
     188             :     // Convert bitcoin:// to bitcoin:
     189             :     //
     190             :     //    Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host,
     191             :     //    which will lower-case it (and thus invalidate the address).
     192           1 :     if(uri.startsWith("bitcoin://", Qt::CaseInsensitive))
     193             :     {
     194           1 :         uri.replace(0, 10, "bitcoin:");
     195             :     }
     196           1 :     QUrl uriInstance(uri);
     197           1 :     return parseBitcoinURI(uriInstance, out);
     198             : }
     199             : 
     200           0 : QString formatBitcoinURI(const SendCoinsRecipient &info)
     201             : {
     202           0 :     QString ret = QString("bitcoin:%1").arg(info.address);
     203           0 :     int paramCount = 0;
     204             : 
     205           0 :     if (info.amount)
     206             :     {
     207           0 :         ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount, false, BitcoinUnits::separatorNever));
     208           0 :         paramCount++;
     209             :     }
     210             : 
     211           0 :     if (!info.label.isEmpty())
     212             :     {
     213           0 :         QString lbl(QUrl::toPercentEncoding(info.label));
     214           0 :         ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl);
     215           0 :         paramCount++;
     216             :     }
     217             : 
     218           0 :     if (!info.message.isEmpty())
     219             :     {
     220           0 :         QString msg(QUrl::toPercentEncoding(info.message));;
     221           0 :         ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg);
     222           0 :         paramCount++;
     223             :     }
     224             : 
     225           0 :     return ret;
     226             : }
     227             : 
     228           0 : bool isDust(const QString& address, const CAmount& amount)
     229             : {
     230           0 :     CTxDestination dest = CBitcoinAddress(address.toStdString()).Get();
     231           0 :     CScript script = GetScriptForDestination(dest);
     232           0 :     CTxOut txOut(amount, script);
     233           0 :     return txOut.IsDust(::minRelayTxFee);
     234             : }
     235             : 
     236           6 : QString HtmlEscape(const QString& str, bool fMultiLine)
     237             : {
     238             : #if QT_VERSION < 0x050000
     239           6 :     QString escaped = Qt::escape(str);
     240             : #else
     241             :     QString escaped = str.toHtmlEscaped();
     242             : #endif
     243           6 :     if(fMultiLine)
     244             :     {
     245           0 :         escaped = escaped.replace("\n", "<br>\n");
     246             :     }
     247           6 :     return escaped;
     248             : }
     249             : 
     250           6 : QString HtmlEscape(const std::string& str, bool fMultiLine)
     251             : {
     252          12 :     return HtmlEscape(QString::fromStdString(str), fMultiLine);
     253             : }
     254             : 
     255           0 : void copyEntryData(QAbstractItemView *view, int column, int role)
     256             : {
     257           0 :     if(!view || !view->selectionModel())
     258           0 :         return;
     259           0 :     QModelIndexList selection = view->selectionModel()->selectedRows(column);
     260             : 
     261           0 :     if(!selection.isEmpty())
     262             :     {
     263             :         // Copy first item
     264           0 :         setClipboard(selection.at(0).data(role).toString());
     265           0 :     }
     266             : }
     267             : 
     268           0 : QString getEntryData(QAbstractItemView *view, int column, int role)
     269             : {
     270           0 :     if(!view || !view->selectionModel())
     271             :         return QString();
     272           0 :     QModelIndexList selection = view->selectionModel()->selectedRows(column);
     273             : 
     274           0 :     if(!selection.isEmpty()) {
     275             :         // Return first item
     276           0 :         return (selection.at(0).data(role).toString());
     277             :     }
     278           0 :     return QString();
     279             : }
     280             : 
     281           0 : QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
     282             :     const QString &filter,
     283             :     QString *selectedSuffixOut)
     284             : {
     285             :     QString selectedFilter;
     286           0 :     QString myDir;
     287           0 :     if(dir.isEmpty()) // Default to user documents location
     288             :     {
     289             : #if QT_VERSION < 0x050000
     290           0 :         myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
     291             : #else
     292             :         myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
     293             : #endif
     294             :     }
     295             :     else
     296             :     {
     297           0 :         myDir = dir;
     298             :     }
     299             :     /* Directly convert path to native OS path separators */
     300           0 :     QString result = QDir::toNativeSeparators(QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter));
     301             : 
     302             :     /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
     303           0 :     QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
     304           0 :     QString selectedSuffix;
     305           0 :     if(filter_re.exactMatch(selectedFilter))
     306             :     {
     307           0 :         selectedSuffix = filter_re.cap(1);
     308             :     }
     309             : 
     310             :     /* Add suffix if needed */
     311           0 :     QFileInfo info(result);
     312           0 :     if(!result.isEmpty())
     313             :     {
     314           0 :         if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
     315             :         {
     316             :             /* No suffix specified, add selected suffix */
     317           0 :             if(!result.endsWith("."))
     318           0 :                 result.append(".");
     319           0 :             result.append(selectedSuffix);
     320             :         }
     321             :     }
     322             : 
     323             :     /* Return selected suffix if asked to */
     324           0 :     if(selectedSuffixOut)
     325             :     {
     326           0 :         *selectedSuffixOut = selectedSuffix;
     327             :     }
     328           0 :     return result;
     329             : }
     330             : 
     331           0 : QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir,
     332             :     const QString &filter,
     333             :     QString *selectedSuffixOut)
     334             : {
     335             :     QString selectedFilter;
     336           0 :     QString myDir;
     337           0 :     if(dir.isEmpty()) // Default to user documents location
     338             :     {
     339             : #if QT_VERSION < 0x050000
     340           0 :         myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
     341             : #else
     342             :         myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
     343             : #endif
     344             :     }
     345             :     else
     346             :     {
     347           0 :         myDir = dir;
     348             :     }
     349             :     /* Directly convert path to native OS path separators */
     350           0 :     QString result = QDir::toNativeSeparators(QFileDialog::getOpenFileName(parent, caption, myDir, filter, &selectedFilter));
     351             : 
     352           0 :     if(selectedSuffixOut)
     353             :     {
     354             :         /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
     355           0 :         QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
     356           0 :         QString selectedSuffix;
     357           0 :         if(filter_re.exactMatch(selectedFilter))
     358             :         {
     359           0 :             selectedSuffix = filter_re.cap(1);
     360             :         }
     361           0 :         *selectedSuffixOut = selectedSuffix;
     362             :     }
     363           0 :     return result;
     364             : }
     365             : 
     366           0 : Qt::ConnectionType blockingGUIThreadConnection()
     367             : {
     368           0 :     if(QThread::currentThread() != qApp->thread())
     369             :     {
     370             :         return Qt::BlockingQueuedConnection;
     371             :     }
     372             :     else
     373             :     {
     374           0 :         return Qt::DirectConnection;
     375             :     }
     376             : }
     377             : 
     378           0 : bool checkPoint(const QPoint &p, const QWidget *w)
     379             : {
     380           0 :     QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p));
     381           0 :     if (!atW) return false;
     382           0 :     return atW->topLevelWidget() == w;
     383             : }
     384             : 
     385           0 : bool isObscured(QWidget *w)
     386             : {
     387           0 :     return !(checkPoint(QPoint(0, 0), w)
     388           0 :         && checkPoint(QPoint(w->width() - 1, 0), w)
     389           0 :         && checkPoint(QPoint(0, w->height() - 1), w)
     390           0 :         && checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
     391           0 :         && checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
     392             : }
     393             : 
     394           0 : void openDebugLogfile()
     395             : {
     396           0 :     boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
     397             : 
     398             :     /* Open debug.log with the associated application */
     399           0 :     if (boost::filesystem::exists(pathDebug))
     400           0 :         QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathDebug)));
     401           0 : }
     402             : 
     403           0 : void SubstituteFonts(const QString& language)
     404             : {
     405             : #if defined(Q_OS_MAC)
     406             : // Background:
     407             : // OSX's default font changed in 10.9 and Qt is unable to find it with its
     408             : // usual fallback methods when building against the 10.7 sdk or lower.
     409             : // The 10.8 SDK added a function to let it find the correct fallback font.
     410             : // If this fallback is not properly loaded, some characters may fail to
     411             : // render correctly.
     412             : //
     413             : // The same thing happened with 10.10. .Helvetica Neue DeskInterface is now default.
     414             : //
     415             : // Solution: If building with the 10.7 SDK or lower and the user's platform
     416             : // is 10.9 or higher at runtime, substitute the correct font. This needs to
     417             : // happen before the QApplication is created.
     418             : #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8
     419             :     if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_8)
     420             :     {
     421             :         if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9)
     422             :             /* On a 10.9 - 10.9.x system */
     423             :             QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
     424             :         else
     425             :         {
     426             :             /* 10.10 or later system */
     427             :             if (language == "zh_CN" || language == "zh_TW" || language == "zh_HK") // traditional or simplified Chinese
     428             :               QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Heiti SC");
     429             :             else if (language == "ja") // Japanesee
     430             :               QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Songti SC");
     431             :             else
     432             :               QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Lucida Grande");
     433             :         }
     434             :     }
     435             : #endif
     436             : #endif
     437           0 : }
     438             : 
     439           0 : ToolTipToRichTextFilter::ToolTipToRichTextFilter(int size_threshold, QObject *parent) :
     440             :     QObject(parent),
     441           0 :     size_threshold(size_threshold)
     442             : {
     443             : 
     444           0 : }
     445             : 
     446           0 : bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
     447             : {
     448           0 :     if(evt->type() == QEvent::ToolTipChange)
     449             :     {
     450           0 :         QWidget *widget = static_cast<QWidget*>(obj);
     451           0 :         QString tooltip = widget->toolTip();
     452           0 :         if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt") && !Qt::mightBeRichText(tooltip))
     453             :         {
     454             :             // Envelop with <qt></qt> to make sure Qt detects this as rich text
     455             :             // Escape the current message as HTML and replace \n by <br>
     456           0 :             tooltip = "<qt>" + HtmlEscape(tooltip, true) + "</qt>";
     457           0 :             widget->setToolTip(tooltip);
     458           0 :             return true;
     459           0 :         }
     460             :     }
     461           0 :     return QObject::eventFilter(obj, evt);
     462             : }
     463             : 
     464           0 : void TableViewLastColumnResizingFixer::connectViewHeadersSignals()
     465             : {
     466           0 :     connect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(on_sectionResized(int,int,int)));
     467           0 :     connect(tableView->horizontalHeader(), SIGNAL(geometriesChanged()), this, SLOT(on_geometriesChanged()));
     468           0 : }
     469             : 
     470             : // We need to disconnect these while handling the resize events, otherwise we can enter infinite loops.
     471           0 : void TableViewLastColumnResizingFixer::disconnectViewHeadersSignals()
     472             : {
     473           0 :     disconnect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(on_sectionResized(int,int,int)));
     474           0 :     disconnect(tableView->horizontalHeader(), SIGNAL(geometriesChanged()), this, SLOT(on_geometriesChanged()));
     475           0 : }
     476             : 
     477             : // Setup the resize mode, handles compatibility for Qt5 and below as the method signatures changed.
     478             : // Refactored here for readability.
     479           0 : void TableViewLastColumnResizingFixer::setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode)
     480             : {
     481             : #if QT_VERSION < 0x050000
     482           0 :     tableView->horizontalHeader()->setResizeMode(logicalIndex, resizeMode);
     483             : #else
     484             :     tableView->horizontalHeader()->setSectionResizeMode(logicalIndex, resizeMode);
     485             : #endif
     486           0 : }
     487             : 
     488           0 : void TableViewLastColumnResizingFixer::resizeColumn(int nColumnIndex, int width)
     489             : {
     490           0 :     tableView->setColumnWidth(nColumnIndex, width);
     491           0 :     tableView->horizontalHeader()->resizeSection(nColumnIndex, width);
     492           0 : }
     493             : 
     494           0 : int TableViewLastColumnResizingFixer::getColumnsWidth()
     495             : {
     496           0 :     int nColumnsWidthSum = 0;
     497           0 :     for (int i = 0; i < columnCount; i++)
     498             :     {
     499           0 :         nColumnsWidthSum += tableView->horizontalHeader()->sectionSize(i);
     500             :     }
     501           0 :     return nColumnsWidthSum;
     502             : }
     503             : 
     504           0 : int TableViewLastColumnResizingFixer::getAvailableWidthForColumn(int column)
     505             : {
     506           0 :     int nResult = lastColumnMinimumWidth;
     507           0 :     int nTableWidth = tableView->horizontalHeader()->width();
     508             : 
     509           0 :     if (nTableWidth > 0)
     510             :     {
     511           0 :         int nOtherColsWidth = getColumnsWidth() - tableView->horizontalHeader()->sectionSize(column);
     512           0 :         nResult = std::max(nResult, nTableWidth - nOtherColsWidth);
     513             :     }
     514             : 
     515           0 :     return nResult;
     516             : }
     517             : 
     518             : // Make sure we don't make the columns wider than the tables viewport width.
     519           0 : void TableViewLastColumnResizingFixer::adjustTableColumnsWidth()
     520             : {
     521           0 :     disconnectViewHeadersSignals();
     522           0 :     resizeColumn(lastColumnIndex, getAvailableWidthForColumn(lastColumnIndex));
     523           0 :     connectViewHeadersSignals();
     524             : 
     525           0 :     int nTableWidth = tableView->horizontalHeader()->width();
     526           0 :     int nColsWidth = getColumnsWidth();
     527           0 :     if (nColsWidth > nTableWidth)
     528             :     {
     529           0 :         resizeColumn(secondToLastColumnIndex,getAvailableWidthForColumn(secondToLastColumnIndex));
     530             :     }
     531           0 : }
     532             : 
     533             : // Make column use all the space available, useful during window resizing.
     534           0 : void TableViewLastColumnResizingFixer::stretchColumnWidth(int column)
     535             : {
     536           0 :     disconnectViewHeadersSignals();
     537           0 :     resizeColumn(column, getAvailableWidthForColumn(column));
     538           0 :     connectViewHeadersSignals();
     539           0 : }
     540             : 
     541             : // When a section is resized this is a slot-proxy for ajustAmountColumnWidth().
     542           0 : void TableViewLastColumnResizingFixer::on_sectionResized(int logicalIndex, int oldSize, int newSize)
     543             : {
     544           0 :     adjustTableColumnsWidth();
     545           0 :     int remainingWidth = getAvailableWidthForColumn(logicalIndex);
     546           0 :     if (newSize > remainingWidth)
     547             :     {
     548           0 :        resizeColumn(logicalIndex, remainingWidth);
     549             :     }
     550           0 : }
     551             : 
     552             : // When the tabless geometry is ready, we manually perform the stretch of the "Message" column,
     553             : // as the "Stretch" resize mode does not allow for interactive resizing.
     554           0 : void TableViewLastColumnResizingFixer::on_geometriesChanged()
     555             : {
     556           0 :     if ((getColumnsWidth() - this->tableView->horizontalHeader()->width()) != 0)
     557             :     {
     558           0 :         disconnectViewHeadersSignals();
     559           0 :         resizeColumn(secondToLastColumnIndex, getAvailableWidthForColumn(secondToLastColumnIndex));
     560           0 :         connectViewHeadersSignals();
     561             :     }
     562           0 : }
     563             : 
     564             : /**
     565             :  * Initializes all internal variables and prepares the
     566             :  * the resize modes of the last 2 columns of the table and
     567             :  */
     568           0 : TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth) :
     569             :     tableView(table),
     570             :     lastColumnMinimumWidth(lastColMinimumWidth),
     571           0 :     allColumnsMinimumWidth(allColsMinimumWidth)
     572             : {
     573           0 :     columnCount = tableView->horizontalHeader()->count();
     574           0 :     lastColumnIndex = columnCount - 1;
     575           0 :     secondToLastColumnIndex = columnCount - 2;
     576           0 :     tableView->horizontalHeader()->setMinimumSectionSize(allColumnsMinimumWidth);
     577           0 :     setViewHeaderResizeMode(secondToLastColumnIndex, QHeaderView::Interactive);
     578           0 :     setViewHeaderResizeMode(lastColumnIndex, QHeaderView::Interactive);
     579           0 : }
     580             : 
     581             : #ifdef WIN32
     582             : boost::filesystem::path static StartupShortcutPath()
     583             : {
     584             :     if (GetBoolArg("-testnet", false))
     585             :         return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin (testnet).lnk";
     586             :     else if (GetBoolArg("-regtest", false))
     587             :         return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin (regtest).lnk";
     588             : 
     589             :     return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk";
     590             : }
     591             : 
     592             : bool GetStartOnSystemStartup()
     593             : {
     594             :     // check for Bitcoin*.lnk
     595             :     return boost::filesystem::exists(StartupShortcutPath());
     596             : }
     597             : 
     598             : bool SetStartOnSystemStartup(bool fAutoStart)
     599             : {
     600             :     // If the shortcut exists already, remove it for updating
     601             :     boost::filesystem::remove(StartupShortcutPath());
     602             : 
     603             :     if (fAutoStart)
     604             :     {
     605             :         CoInitialize(NULL);
     606             : 
     607             :         // Get a pointer to the IShellLink interface.
     608             :         IShellLink* psl = NULL;
     609             :         HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
     610             :             CLSCTX_INPROC_SERVER, IID_IShellLink,
     611             :             reinterpret_cast<void**>(&psl));
     612             : 
     613             :         if (SUCCEEDED(hres))
     614             :         {
     615             :             // Get the current executable path
     616             :             TCHAR pszExePath[MAX_PATH];
     617             :             GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
     618             : 
     619             :             // Start client minimized
     620             :             QString strArgs = "-min";
     621             :             // Set -testnet /-regtest options
     622             :             strArgs += QString::fromStdString(strprintf(" -testnet=%d -regtest=%d", GetBoolArg("-testnet", false), GetBoolArg("-regtest", false)));
     623             : 
     624             : #ifdef UNICODE
     625             :             boost::scoped_array<TCHAR> args(new TCHAR[strArgs.length() + 1]);
     626             :             // Convert the QString to TCHAR*
     627             :             strArgs.toWCharArray(args.get());
     628             :             // Add missing '\0'-termination to string
     629             :             args[strArgs.length()] = '\0';
     630             : #endif
     631             : 
     632             :             // Set the path to the shortcut target
     633             :             psl->SetPath(pszExePath);
     634             :             PathRemoveFileSpec(pszExePath);
     635             :             psl->SetWorkingDirectory(pszExePath);
     636             :             psl->SetShowCmd(SW_SHOWMINNOACTIVE);
     637             : #ifndef UNICODE
     638             :             psl->SetArguments(strArgs.toStdString().c_str());
     639             : #else
     640             :             psl->SetArguments(args.get());
     641             : #endif
     642             : 
     643             :             // Query IShellLink for the IPersistFile interface for
     644             :             // saving the shortcut in persistent storage.
     645             :             IPersistFile* ppf = NULL;
     646             :             hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&ppf));
     647             :             if (SUCCEEDED(hres))
     648             :             {
     649             :                 WCHAR pwsz[MAX_PATH];
     650             :                 // Ensure that the string is ANSI.
     651             :                 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH);
     652             :                 // Save the link by calling IPersistFile::Save.
     653             :                 hres = ppf->Save(pwsz, TRUE);
     654             :                 ppf->Release();
     655             :                 psl->Release();
     656             :                 CoUninitialize();
     657             :                 return true;
     658             :             }
     659             :             psl->Release();
     660             :         }
     661             :         CoUninitialize();
     662             :         return false;
     663             :     }
     664             :     return true;
     665             : }
     666             : #elif defined(Q_OS_LINUX)
     667             : 
     668             : // Follow the Desktop Application Autostart Spec:
     669             : // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
     670             : 
     671           0 : boost::filesystem::path static GetAutostartDir()
     672             : {
     673             :     namespace fs = boost::filesystem;
     674             : 
     675           0 :     char* pszConfigHome = getenv("XDG_CONFIG_HOME");
     676           0 :     if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
     677           0 :     char* pszHome = getenv("HOME");
     678           0 :     if (pszHome) return fs::path(pszHome) / ".config" / "autostart";
     679             :     return fs::path();
     680             : }
     681             : 
     682           0 : boost::filesystem::path static GetAutostartFilePath()
     683             : {
     684           0 :     return GetAutostartDir() / "bitcoin.desktop";
     685             : }
     686             : 
     687           0 : bool GetStartOnSystemStartup()
     688             : {
     689           0 :     boost::filesystem::ifstream optionFile(GetAutostartFilePath());
     690           0 :     if (!optionFile.good())
     691             :         return false;
     692             :     // Scan through file for "Hidden=true":
     693             :     std::string line;
     694           0 :     while (!optionFile.eof())
     695             :     {
     696           0 :         getline(optionFile, line);
     697           0 :         if (line.find("Hidden") != std::string::npos &&
     698           0 :             line.find("true") != std::string::npos)
     699             :             return false;
     700             :     }
     701           0 :     optionFile.close();
     702             : 
     703           0 :     return true;
     704             : }
     705             : 
     706           0 : bool SetStartOnSystemStartup(bool fAutoStart)
     707             : {
     708           0 :     if (!fAutoStart)
     709           0 :         boost::filesystem::remove(GetAutostartFilePath());
     710             :     else
     711             :     {
     712             :         char pszExePath[MAX_PATH+1];
     713             :         memset(pszExePath, 0, sizeof(pszExePath));
     714           0 :         if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
     715           0 :             return false;
     716             : 
     717           0 :         boost::filesystem::create_directories(GetAutostartDir());
     718             : 
     719           0 :         boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
     720           0 :         if (!optionFile.good())
     721           0 :             return false;
     722             :         // Write a bitcoin.desktop file to the autostart directory:
     723           0 :         optionFile << "[Desktop Entry]\n";
     724           0 :         optionFile << "Type=Application\n";
     725           0 :         if (GetBoolArg("-testnet", false))
     726           0 :             optionFile << "Name=Bitcoin (testnet)\n";
     727           0 :         else if (GetBoolArg("-regtest", false))
     728           0 :             optionFile << "Name=Bitcoin (regtest)\n";
     729             :         else
     730           0 :             optionFile << "Name=Bitcoin\n";
     731           0 :         optionFile << "Exec=" << pszExePath << strprintf(" -min -testnet=%d -regtest=%d\n", GetBoolArg("-testnet", false), GetBoolArg("-regtest", false));
     732           0 :         optionFile << "Terminal=false\n";
     733           0 :         optionFile << "Hidden=false\n";
     734           0 :         optionFile.close();
     735             :     }
     736             :     return true;
     737             : }
     738             : 
     739             : 
     740             : #elif defined(Q_OS_MAC)
     741             : // based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m
     742             : 
     743             : #include <CoreFoundation/CoreFoundation.h>
     744             : #include <CoreServices/CoreServices.h>
     745             : 
     746             : LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl);
     747             : LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl)
     748             : {
     749             :     // loop through the list of startup items and try to find the bitcoin app
     750             :     CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, NULL);
     751             :     for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) {
     752             :         LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i);
     753             :         UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
     754             :         CFURLRef currentItemURL = NULL;
     755             : 
     756             : #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 10100
     757             :     if(&LSSharedFileListItemCopyResolvedURL)
     758             :         currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, NULL);
     759             : #if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED < 10100
     760             :     else
     761             :         LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, NULL);
     762             : #endif
     763             : #else
     764             :     LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, NULL);
     765             : #endif
     766             : 
     767             :         if(currentItemURL && CFEqual(currentItemURL, findUrl)) {
     768             :             // found
     769             :             CFRelease(currentItemURL);
     770             :             return item;
     771             :         }
     772             :         if(currentItemURL) {
     773             :             CFRelease(currentItemURL);
     774             :         }
     775             :     }
     776             :     return NULL;
     777             : }
     778             : 
     779             : bool GetStartOnSystemStartup()
     780             : {
     781             :     CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
     782             :     LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
     783             :     LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
     784             :     return !!foundItem; // return boolified object
     785             : }
     786             : 
     787             : bool SetStartOnSystemStartup(bool fAutoStart)
     788             : {
     789             :     CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
     790             :     LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
     791             :     LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
     792             : 
     793             :     if(fAutoStart && !foundItem) {
     794             :         // add bitcoin app to startup item list
     795             :         LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, bitcoinAppUrl, NULL, NULL);
     796             :     }
     797             :     else if(!fAutoStart && foundItem) {
     798             :         // remove item
     799             :         LSSharedFileListItemRemove(loginItems, foundItem);
     800             :     }
     801             :     return true;
     802             : }
     803             : #else
     804             : 
     805             : bool GetStartOnSystemStartup() { return false; }
     806             : bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
     807             : 
     808             : #endif
     809             : 
     810           0 : void saveWindowGeometry(const QString& strSetting, QWidget *parent)
     811             : {
     812           0 :     QSettings settings;
     813           0 :     settings.setValue(strSetting + "Pos", parent->pos());
     814           0 :     settings.setValue(strSetting + "Size", parent->size());
     815           0 : }
     816             : 
     817           0 : void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QWidget *parent)
     818             : {
     819           0 :     QSettings settings;
     820           0 :     QPoint pos = settings.value(strSetting + "Pos").toPoint();
     821           0 :     QSize size = settings.value(strSetting + "Size", defaultSize).toSize();
     822             : 
     823           0 :     if (!pos.x() && !pos.y()) {
     824           0 :         QRect screen = QApplication::desktop()->screenGeometry();
     825           0 :         pos.setX((screen.width() - size.width()) / 2);
     826           0 :         pos.setY((screen.height() - size.height()) / 2);
     827             :     }
     828             : 
     829           0 :     parent->resize(size);
     830           0 :     parent->move(pos);
     831           0 : }
     832             : 
     833           0 : void setClipboard(const QString& str)
     834             : {
     835           0 :     QApplication::clipboard()->setText(str, QClipboard::Clipboard);
     836           0 :     QApplication::clipboard()->setText(str, QClipboard::Selection);
     837           0 : }
     838             : 
     839             : #if BOOST_FILESYSTEM_VERSION >= 3
     840           0 : boost::filesystem::path qstringToBoostPath(const QString &path)
     841             : {
     842           0 :     return boost::filesystem::path(path.toStdString(), utf8);
     843             : }
     844             : 
     845           0 : QString boostPathToQString(const boost::filesystem::path &path)
     846             : {
     847           0 :     return QString::fromStdString(path.string(utf8));
     848             : }
     849             : #else
     850             : #warning Conversion between boost path and QString can use invalid character encoding with boost_filesystem v2 and older
     851             : boost::filesystem::path qstringToBoostPath(const QString &path)
     852             : {
     853             :     return boost::filesystem::path(path.toStdString());
     854             : }
     855             : 
     856             : QString boostPathToQString(const boost::filesystem::path &path)
     857             : {
     858             :     return QString::fromStdString(path.string());
     859             : }
     860             : #endif
     861             : 
     862           0 : QString formatDurationStr(int secs)
     863             : {
     864             :     QStringList strList;
     865           0 :     int days = secs / 86400;
     866           0 :     int hours = (secs % 86400) / 3600;
     867           0 :     int mins = (secs % 3600) / 60;
     868           0 :     int seconds = secs % 60;
     869             : 
     870           0 :     if (days)
     871           0 :         strList.append(QString(QObject::tr("%1 d")).arg(days));
     872           0 :     if (hours)
     873           0 :         strList.append(QString(QObject::tr("%1 h")).arg(hours));
     874           0 :     if (mins)
     875           0 :         strList.append(QString(QObject::tr("%1 m")).arg(mins));
     876           0 :     if (seconds || (!days && !hours && !mins))
     877           0 :         strList.append(QString(QObject::tr("%1 s")).arg(seconds));
     878             : 
     879           0 :     return strList.join(" ");
     880             : }
     881             : 
     882           0 : QString formatServicesStr(quint64 mask)
     883             : {
     884             :     QStringList strList;
     885             : 
     886             :     // Just scan the last 8 bits for now.
     887           0 :     for (int i = 0; i < 8; i++) {
     888           0 :         uint64_t check = 1 << i;
     889           0 :         if (mask & check)
     890             :         {
     891           0 :             switch (check)
     892             :             {
     893             :             case NODE_NETWORK:
     894           0 :                 strList.append("NETWORK");
     895           0 :                 break;
     896             :             case NODE_GETUTXO:
     897           0 :                 strList.append("GETUTXO");
     898           0 :                 break;
     899             :             default:
     900           0 :                 strList.append(QString("%1[%2]").arg("UNKNOWN").arg(check));
     901             :             }
     902             :         }
     903             :     }
     904             : 
     905           0 :     if (strList.size())
     906           0 :         return strList.join(" & ");
     907             :     else
     908             :         return QObject::tr("None");
     909             : }
     910             : 
     911           0 : QString formatPingTime(double dPingTime)
     912             : {
     913           0 :     return dPingTime == 0 ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(dPingTime * 1000), 10));
     914             : }
     915             : 
     916           0 : QString formatTimeOffset(int64_t nTimeOffset)
     917             : {
     918           0 :   return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10));
     919             : }
     920             : 
     921           3 : } // namespace GUIUtil

Generated by: LCOV version 1.11