qt: Fix random segfault when closing "Choose data directory" dialog
[novacoin.git] / src / qt / bitcoin.cpp
1 /*
2  * W.J. van der Laan 2011-2012
3  */
4 #include "bitcoingui.h"
5 #include "clientmodel.h"
6 #include "walletmodel.h"
7 #include "optionsmodel.h"
8 #include "guiutil.h"
9 #include "guiconstants.h"
10
11 #include "init.h"
12 #include "ui_interface.h"
13 #include "qtipcserver.h"
14 #include "intro.h"
15
16 #include <QApplication>
17 #include <QMessageBox>
18 #if QT_VERSION < 0x050000
19 #include <QTextCodec>
20 #endif
21 #include <QLocale>
22 #include <QTranslator>
23 #include <QSplashScreen>
24 #include <QLibraryInfo>
25 #include <QSettings>
26
27 #if defined(BITCOIN_NEED_QT_PLUGINS) && !defined(_BITCOIN_QT_PLUGINS_INCLUDED)
28 #define _BITCOIN_QT_PLUGINS_INCLUDED
29 #define __INSURE__
30 #include <QtPlugin>
31 Q_IMPORT_PLUGIN(qcncodecs)
32 Q_IMPORT_PLUGIN(qjpcodecs)
33 Q_IMPORT_PLUGIN(qtwcodecs)
34 Q_IMPORT_PLUGIN(qkrcodecs)
35 Q_IMPORT_PLUGIN(qtaccessiblewidgets)
36 #endif
37
38 // Need a global reference for the notifications to find the GUI
39 static BitcoinGUI *guiref;
40 static QSplashScreen *splashref;
41
42 /** Set up translations */
43 static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator)
44 {
45     QSettings settings;
46     // Get desired locale (e.g. "de_DE")
47     // 1) System default language
48     QString lang_territory = QLocale::system().name();
49     // 2) Language from QSettings
50     QString lang_territory_qsettings = settings.value("language", "").toString();
51     if(!lang_territory_qsettings.isEmpty())
52         lang_territory = lang_territory_qsettings;
53     // 3) -lang command line argument
54     lang_territory = QString::fromStdString(GetArg("-lang", lang_territory.toStdString()));
55     // Convert to "de" only by truncating "_DE"
56     QString lang = lang_territory;
57     lang.truncate(lang_territory.lastIndexOf('_'));
58     // Load language files for configured locale:
59     // - First load the translator for the base language, without territory
60     // - Then load the more specific locale translator
61     // Load e.g. qt_de.qm
62     if (qtTranslatorBase.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
63         QApplication::installTranslator(&qtTranslatorBase);
64     // Load e.g. qt_de_DE.qm
65     if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
66         QApplication::installTranslator(&qtTranslator);
67     // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc)
68     if (translatorBase.load(lang, ":/translations/"))
69         QApplication::installTranslator(&translatorBase);
70     // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc)
71     if (translator.load(lang_territory, ":/translations/"))
72         QApplication::installTranslator(&translator);
73 }
74
75 static void ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style)
76 {
77     // Message from network thread
78     if(guiref)
79     {
80         bool modal = (style & CClientUIInterface::MODAL);
81         // in case of modal message, use blocking connection to wait for user to click OK
82         QMetaObject::invokeMethod(guiref, "error",
83                                    modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection,
84                                    Q_ARG(QString, QString::fromStdString(caption)),
85                                    Q_ARG(QString, QString::fromStdString(message)),
86                                    Q_ARG(bool, modal));
87     }
88     else
89     {
90         printf("%s: %s\n", caption.c_str(), message.c_str());
91         fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str());
92     }
93 }
94
95 static bool ThreadSafeAskFee(int64_t nFeeRequired, const std::string& strCaption)
96 {
97     if(!guiref)
98         return false;
99     if(nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
100         return true;
101     bool payFee = false;
102
103     QMetaObject::invokeMethod(guiref, "askFee", GUIUtil::blockingGUIThreadConnection(),
104                                Q_ARG(qint64, nFeeRequired),
105                                Q_ARG(bool*, &payFee));
106
107     return payFee;
108 }
109
110 static void ThreadSafeHandleURI(const std::string& strURI)
111 {
112     if(!guiref)
113         return;
114
115     QMetaObject::invokeMethod(guiref, "handleURI", GUIUtil::blockingGUIThreadConnection(),
116                                Q_ARG(QString, QString::fromStdString(strURI)));
117 }
118
119 static void InitMessage(const std::string &message)
120 {
121     if(splashref)
122     {
123         splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom|Qt::AlignHCenter, QColor(255,255,200));
124         QApplication::instance()->processEvents();
125     }
126 }
127
128 static void QueueShutdown()
129 {
130     QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
131 }
132
133 /*
134    Translate string to current locale using Qt.
135  */
136 static std::string Translate(const char* psz)
137 {
138     return QCoreApplication::translate("bitcoin-core", psz).toStdString();
139 }
140
141 /* Handle runaway exceptions. Shows a message box with the problem and quits the program.
142  */
143 static void handleRunawayException(std::exception *e)
144 {
145     PrintExceptionContinue(e, "Runaway exception");
146     QMessageBox::critical(0, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. NovaCoin can no longer continue safely and will quit.") + QString("\n\n") + QString::fromStdString(strMiscWarning));
147     exit(1);
148 }
149
150 #ifndef BITCOIN_QT_TEST
151 int main(int argc, char *argv[])
152 {
153     // Do this early as we don't want to bother initializing if we are just calling IPC
154     ipcScanRelay(argc, argv);
155 #if QT_VERSION < 0x050000
156     // Internal string conversion is all UTF-8
157     QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
158     QTextCodec::setCodecForCStrings(QTextCodec::codecForTr());
159 #endif
160     Q_INIT_RESOURCE(bitcoin);
161     QApplication app(argc, argv);
162
163     // Application identification (must be set before OptionsModel is initialized,
164     // as it is used to locate QSettings)
165     app.setOrganizationName("NovaCoin");
166     app.setOrganizationDomain("novacoin.su");
167     if(GetBoolArg("-testnet")) // Separate UI settings for testnet
168         app.setApplicationName("NovaCoin-Qt-testnet");
169     else
170         app.setApplicationName("NovaCoin-Qt");
171     // Now that QSettings are accessible, initialize translations
172     QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
173     initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
174
175     // Command-line options take precedence:
176     ParseParameters(argc, argv);
177
178     // User language is set up: pick a data directory
179     if (!Intro::pickDataDirectory())
180         return 0;
181
182     // Install global event filter that makes sure that long tooltips can be word-wrapped
183     app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
184
185     // ... then bitcoin.conf:
186     if (!boost::filesystem::is_directory(GetDataDir(false)))
187     {
188         QMessageBox::critical(0, "NovaCoin",
189                               QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(mapArgs["-datadir"])));
190         return 1;
191     }
192     ReadConfigFile(mapArgs, mapMultiArgs);
193
194     // ... then GUI settings:
195     OptionsModel optionsModel;
196
197     // Subscribe to global signals from core
198     uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox);
199     uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee);
200     uiInterface.ThreadSafeHandleURI.connect(ThreadSafeHandleURI);
201     uiInterface.InitMessage.connect(InitMessage);
202     uiInterface.QueueShutdown.connect(QueueShutdown);
203     uiInterface.Translate.connect(Translate);
204
205     // Show help message immediately after parsing command-line options (for "-lang") and setting locale,
206     // but before showing splash screen.
207     if (mapArgs.count("-?") || mapArgs.count("--help"))
208     {
209         GUIUtil::HelpMessageBox help;
210         help.showOrPrint();
211         return 1;
212     }
213
214     QSplashScreen splash(QPixmap(":/images/splash"), 0);
215     if (GetBoolArg("-splash", true) && !GetBoolArg("-min"))
216     {
217         splash.show();
218         splash.setAutoFillBackground(true);
219         splashref = &splash;
220     }
221
222     app.processEvents();
223
224     app.setQuitOnLastWindowClosed(false);
225
226     try
227     {
228         // Regenerate startup link, to fix links to old versions
229         if (GUIUtil::GetStartOnSystemStartup())
230             GUIUtil::SetStartOnSystemStartup(true);
231
232         BitcoinGUI window;
233         guiref = &window;
234         if(AppInit2())
235         {
236             {
237                 // Put this in a block, so that the Model objects are cleaned up before
238                 // calling Shutdown().
239
240                 if (splashref)
241                     splash.finish(&window);
242
243                 ClientModel clientModel(&optionsModel);
244                 WalletModel walletModel(pwalletMain, &optionsModel);
245
246                 window.setClientModel(&clientModel);
247                 window.setWalletModel(&walletModel);
248
249                 // If -min option passed, start window minimized.
250                 if(GetBoolArg("-min"))
251                 {
252                     window.showMinimized();
253                 }
254                 else
255                 {
256                     window.show();
257                 }
258
259                 // Place this here as guiref has to be defined if we don't want to lose URIs
260                 ipcInit(argc, argv);
261
262                 app.exec();
263
264                 window.hide();
265                 window.setClientModel(0);
266                 window.setWalletModel(0);
267                 guiref = 0;
268             }
269             // Shutdown the core and its threads, but don't exit Bitcoin-Qt here
270             Shutdown(NULL);
271         }
272         else
273         {
274             return 1;
275         }
276     } catch (std::exception& e) {
277         handleRunawayException(&e);
278     } catch (...) {
279         handleRunawayException(NULL);
280     }
281     return 0;
282 }
283 #endif // BITCOIN_QT_TEST