Revert "выбор папки для базы блоков из GUI"
[novacoin.git] / src / qt / guiutil.cpp
1 #include "guiutil.h"
2 #include "bitcoinaddressvalidator.h"
3 #include "walletmodel.h"
4 #include "bitcoinunits.h"
5 #include "util.h"
6 #include "init.h"
7
8 #include <QString>
9 #include <QDateTime>
10 #include <QDoubleValidator>
11 #include <QFont>
12 #include <QLineEdit>
13 #if QT_VERSION >= 0x050000
14 #include <QUrlQuery>
15 #else
16 #include <QUrl>
17 #endif
18 #include <QTextDocument> // For Qt::mightBeRichText
19 #include <QAbstractItemView>
20 #include <QApplication>
21 #include <QClipboard>
22 #include <QFileDialog>
23 #include <QDesktopServices>
24 #include <QThread>
25
26 #ifndef Q_MOC_RUN
27 #include <boost/filesystem.hpp>
28 #include <boost/filesystem/fstream.hpp>
29 #endif
30
31 #ifdef WIN32
32 #ifdef _WIN32_WINNT
33 #undef _WIN32_WINNT
34 #endif
35 #define _WIN32_WINNT 0x0501
36 #ifdef _WIN32_IE
37 #undef _WIN32_IE
38 #endif
39 #define _WIN32_IE 0x0501
40 #define WIN32_LEAN_AND_MEAN 1
41 #ifndef NOMINMAX
42 #define NOMINMAX
43 #endif
44 #include "shlwapi.h"
45 #include "shlobj.h"
46 #include "shellapi.h"
47 #endif
48
49 namespace GUIUtil {
50
51 QString dateTimeStr(const QDateTime &date)
52 {
53     return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
54 }
55
56 QString dateTimeStr(qint64 nTime)
57 {
58     return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
59 }
60
61 QFont bitcoinAddressFont()
62 {
63     QFont font("Monospace");
64     font.setStyleHint(QFont::TypeWriter);
65     return font;
66 }
67
68 void setupAddressWidget(QLineEdit *widget, QWidget *parent)
69 {
70     widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength);
71     widget->setValidator(new BitcoinAddressValidator(parent));
72     widget->setFont(bitcoinAddressFont());
73 }
74
75 void setupAmountWidget(QLineEdit *widget, QWidget *parent)
76 {
77     QDoubleValidator *amountValidator = new QDoubleValidator(parent);
78     amountValidator->setDecimals(8);
79     amountValidator->setBottom(0.0);
80     widget->setValidator(amountValidator);
81     widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
82 }
83
84 bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
85 {
86     // NovaCoin: check prefix
87     if(uri.scheme() != QString("novacoin"))
88         return false;
89
90     SendCoinsRecipient rv;
91     rv.address = uri.path();
92     rv.amount = 0;
93 #if QT_VERSION < 0x050000
94     QList<QPair<QString, QString> > items = uri.queryItems();
95 #else
96     QUrlQuery uriQuery(uri);
97     QList<QPair<QString, QString> > items = uriQuery.queryItems();
98 #endif
99     for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
100     {
101         bool fShouldReturnFalse = false;
102         if (i->first.startsWith("req-"))
103         {
104             i->first.remove(0, 4);
105             fShouldReturnFalse = true;
106         }
107
108         if (i->first == "label")
109         {
110             rv.label = i->second;
111             fShouldReturnFalse = false;
112         }
113         else if (i->first == "amount")
114         {
115             if(!i->second.isEmpty())
116             {
117                 if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
118                 {
119                     return false;
120                 }
121             }
122             fShouldReturnFalse = false;
123         }
124
125         if (fShouldReturnFalse)
126             return false;
127     }
128     if(out)
129     {
130         *out = rv;
131     }
132     return true;
133 }
134
135 bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
136 {
137     // Convert novacoin:// to novacoin:
138     //
139     //    Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host,
140     //    which will lower-case it (and thus invalidate the address).
141     if(uri.startsWith("novacoin://"))
142     {
143         uri.replace(0, 10, "novacoin:");
144     }
145     QUrl uriInstance(uri);
146     return parseBitcoinURI(uriInstance, out);
147 }
148
149 QString HtmlEscape(const QString& str, bool fMultiLine)
150 {
151 #if QT_VERSION < 0x050000
152     QString escaped = Qt::escape(str);
153 #else
154     QString escaped = str.toHtmlEscaped();
155 #endif
156     if(fMultiLine)
157     {
158         escaped = escaped.replace("\n", "<br>\n");
159     }
160     return escaped;
161 }
162
163 QString HtmlEscape(const std::string& str, bool fMultiLine)
164 {
165     return HtmlEscape(QString::fromStdString(str), fMultiLine);
166 }
167
168 void copyEntryData(QAbstractItemView *view, int column, int role)
169 {
170     if(!view || !view->selectionModel())
171         return;
172     QModelIndexList selection = view->selectionModel()->selectedRows(column);
173
174     if(!selection.isEmpty())
175     {
176         // Copy first item
177         QApplication::clipboard()->setText(selection.at(0).data(role).toString());
178     }
179 }
180
181 QString getSaveFileName(QWidget *parent, const QString &caption,
182                                  const QString &dir,
183                                  const QString &filter,
184                                  QString *selectedSuffixOut)
185 {
186     QString selectedFilter;
187     QString myDir;
188     if(dir.isEmpty()) // Default to user documents location
189     {
190 #if QT_VERSION < 0x050000
191         myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
192 #else
193         myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
194 #endif
195     }
196     else
197     {
198         myDir = dir;
199     }
200     QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter);
201
202     /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
203     QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
204     QString selectedSuffix;
205     if(filter_re.exactMatch(selectedFilter))
206     {
207         selectedSuffix = filter_re.cap(1);
208     }
209
210     /* Add suffix if needed */
211     QFileInfo info(result);
212     if(!result.isEmpty())
213     {
214         if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
215         {
216             /* No suffix specified, add selected suffix */
217             if(!result.endsWith("."))
218                 result.append(".");
219             result.append(selectedSuffix);
220         }
221     }
222
223     /* Return selected suffix if asked to */
224     if(selectedSuffixOut)
225     {
226         *selectedSuffixOut = selectedSuffix;
227     }
228     return result;
229 }
230
231 Qt::ConnectionType blockingGUIThreadConnection()
232 {
233     if(QThread::currentThread() != QCoreApplication::instance()->thread())
234     {
235         return Qt::BlockingQueuedConnection;
236     }
237     else
238     {
239         return Qt::DirectConnection;
240     }
241 }
242
243 bool checkPoint(const QPoint &p, const QWidget *w)
244 {
245     QWidget *atW = qApp->widgetAt(w->mapToGlobal(p));
246     if (!atW) return false;
247     return atW->topLevelWidget() == w;
248 }
249
250 bool isObscured(QWidget *w)
251 {
252     return !(checkPoint(QPoint(0, 0), w)
253         && checkPoint(QPoint(w->width() - 1, 0), w)
254         && checkPoint(QPoint(0, w->height() - 1), w)
255         && checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
256         && checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
257 }
258
259 void openDebugLogfile()
260 {
261     boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
262
263     /* Open debug.log with the associated application */
264     if (boost::filesystem::exists(pathDebug))
265         QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(pathDebug.string())));
266 }
267
268 void openConfigfile()
269 {
270     boost::filesystem::path pathConfig = GetConfigFile();
271
272     /* Open novacoin.conf with the associated application */
273     if (boost::filesystem::exists(pathConfig))
274         QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(pathConfig.string())));
275 }
276
277 ToolTipToRichTextFilter::ToolTipToRichTextFilter(int size_threshold, QObject *parent) :
278     QObject(parent), size_threshold(size_threshold)
279 {
280
281 }
282
283 bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
284 {
285     if(evt->type() == QEvent::ToolTipChange)
286     {
287         QWidget *widget = static_cast<QWidget*>(obj);
288         QString tooltip = widget->toolTip();
289         if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt>") && !Qt::mightBeRichText(tooltip))
290         {
291             // Prefix <qt/> to make sure Qt detects this as rich text
292             // Escape the current message as HTML and replace \n by <br>
293             tooltip = "<qt>" + HtmlEscape(tooltip, true) + "<qt/>";
294             widget->setToolTip(tooltip);
295             return true;
296         }
297     }
298     return QObject::eventFilter(obj, evt);
299 }
300
301 #ifdef WIN32
302 boost::filesystem::path static StartupShortcutPath()
303 {
304     return GetSpecialFolderPath(CSIDL_STARTUP) / "NovaCoin.lnk";
305 }
306
307 bool GetStartOnSystemStartup()
308 {
309     // check for Bitcoin.lnk
310     return boost::filesystem::exists(StartupShortcutPath());
311 }
312
313 bool SetStartOnSystemStartup(bool fAutoStart)
314 {
315     // If the shortcut exists already, remove it for updating
316     boost::filesystem::remove(StartupShortcutPath());
317
318     if (fAutoStart)
319     {
320         CoInitialize(NULL);
321
322         // Get a pointer to the IShellLink interface.
323         IShellLink* psl = NULL;
324         HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
325                                 CLSCTX_INPROC_SERVER, IID_IShellLink,
326                                 reinterpret_cast<void**>(&psl));
327
328         if (SUCCEEDED(hres))
329         {
330             // Get the current executable path
331             TCHAR pszExePath[MAX_PATH];
332             GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
333
334             TCHAR pszArgs[5] = TEXT("-min");
335
336             // Set the path to the shortcut target
337             psl->SetPath(pszExePath);
338             PathRemoveFileSpec(pszExePath);
339             psl->SetWorkingDirectory(pszExePath);
340             psl->SetShowCmd(SW_SHOWMINNOACTIVE);
341             psl->SetArguments(pszArgs);
342
343             // Query IShellLink for the IPersistFile interface for
344             // saving the shortcut in persistent storage.
345             IPersistFile* ppf = NULL;
346             hres = psl->QueryInterface(IID_IPersistFile,
347                                        reinterpret_cast<void**>(&ppf));
348             if (SUCCEEDED(hres))
349             {
350                 WCHAR pwsz[MAX_PATH];
351                 // Ensure that the string is ANSI.
352                 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH);
353                 // Save the link by calling IPersistFile::Save.
354                 hres = ppf->Save(pwsz, TRUE);
355                 ppf->Release();
356                 psl->Release();
357                 CoUninitialize();
358                 return true;
359             }
360             psl->Release();
361         }
362         CoUninitialize();
363         return false;
364     }
365     return true;
366 }
367
368 #elif defined(LINUX)
369
370 // Follow the Desktop Application Autostart Spec:
371 //  http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
372
373 boost::filesystem::path static GetAutostartDir()
374 {
375     namespace fs = boost::filesystem;
376
377     char* pszConfigHome = getenv("XDG_CONFIG_HOME");
378     if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
379     char* pszHome = getenv("HOME");
380     if (pszHome) return fs::path(pszHome) / ".config" / "autostart";
381     return fs::path();
382 }
383
384 boost::filesystem::path static GetAutostartFilePath()
385 {
386     return GetAutostartDir() / "novacoin.desktop";
387 }
388
389 bool GetStartOnSystemStartup()
390 {
391     boost::filesystem::ifstream optionFile(GetAutostartFilePath());
392     if (!optionFile.good())
393         return false;
394     // Scan through file for "Hidden=true":
395     std::string line;
396     while (!optionFile.eof())
397     {
398         getline(optionFile, line);
399         if (line.find("Hidden") != std::string::npos &&
400             line.find("true") != std::string::npos)
401             return false;
402     }
403     optionFile.close();
404
405     return true;
406 }
407
408 bool SetStartOnSystemStartup(bool fAutoStart)
409 {
410     if (!fAutoStart)
411         boost::filesystem::remove(GetAutostartFilePath());
412     else
413     {
414         char pszExePath[MAX_PATH+1];
415         memset(pszExePath, 0, sizeof(pszExePath));
416         if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
417             return false;
418
419         boost::filesystem::create_directories(GetAutostartDir());
420
421         boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
422         if (!optionFile.good())
423             return false;
424         // Write a bitcoin.desktop file to the autostart directory:
425         optionFile << "[Desktop Entry]\n";
426         optionFile << "Type=Application\n";
427         optionFile << "Name=NovaCoin\n";
428         optionFile << "Exec=" << pszExePath << " -min\n";
429         optionFile << "Terminal=false\n";
430         optionFile << "Hidden=false\n";
431         optionFile.close();
432     }
433     return true;
434 }
435 #else
436
437 // TODO: OSX startup stuff; see:
438 // https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
439
440 bool GetStartOnSystemStartup() { return false; }
441 bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
442
443 #endif
444
445 HelpMessageBox::HelpMessageBox(QWidget *parent) :
446     QMessageBox(parent)
447 {
448     header = tr("NovaCoin-Qt") + " " + tr("version") + " " +
449         QString::fromStdString(FormatFullVersion()) + "\n\n" +
450         tr("Usage:") + "\n" +
451         "  novacoin-qt [" + tr("command-line options") + "]                     " + "\n";
452
453     coreOptions = QString::fromStdString(HelpMessage());
454
455     uiOptions = tr("UI options") + ":\n" +
456         "  -lang=<lang>           " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
457         "  -min                   " + tr("Start minimized") + "\n" +
458         "  -splash                " + tr("Show splash screen on startup (default: 1)") + "\n";
459
460     setWindowTitle(tr("NovaCoin-Qt"));
461     setFont(bitcoinAddressFont());
462     setTextFormat(Qt::PlainText);
463     // setMinimumWidth is ignored for QMessageBox so put in non-breaking spaces to make it wider.
464     setText(header + QString(QChar(0x2003)).repeated(50));
465     setDetailedText(coreOptions + "\n" + uiOptions);
466     addButton("OK", QMessageBox::RejectRole);   //кнопка OK будет справа от кнопки "Скрыть подробности"
467     //addButton("OK", QMessageBox::NoRole);     //кнопка OK будет слева от кнопки "Скрыть подробности"
468     setMouseTracking(true);
469     setSizeGripEnabled(true);   
470 }
471
472 void HelpMessageBox::printToConsole()
473 {
474     // On other operating systems, the expected action is to print the message to the console.
475     QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions;
476     fprintf(stdout, "%s", strUsage.toStdString().c_str());
477 }
478
479 void HelpMessageBox::showOrPrint()
480 {
481 #if defined(WIN32)
482         // On Windows, show a message box, as there is no stderr/stdout in windowed applications
483         exec();
484 #else
485         // On other operating systems, print help text to console
486         printToConsole();
487 #endif
488 }
489
490 QString formatDurationStr(int secs)
491 {
492     QStringList strList;
493     int days = secs / 86400;
494     int hours = (secs % 86400) / 3600;
495     int mins = (secs % 3600) / 60;
496     int seconds = secs % 60;
497
498     if (days)
499         strList.append(QString(QObject::tr("%1 d")).arg(days));
500     if (hours)
501         strList.append(QString(QObject::tr("%1 h")).arg(hours));
502     if (mins)
503         strList.append(QString(QObject::tr("%1 m")).arg(mins));
504     if (seconds || (!days && !hours && !mins))
505         strList.append(QString(QObject::tr("%1 s")).arg(seconds));
506
507     return strList.join(" ");
508 }
509
510 } // namespace GUIUtil
511