mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-11-22 08:46:39 -06:00
Make the runtime configuration dialog non-modal. Fixes #5490
Major refactoring of the runtime code, Specifically: - Move the bulk of the core code from main() into a new Runtime class. - Break up the mass of code that was main() into a number of relatively simple functions. - Make the Configuration dialog synchronous so the Log dialog can be properly viewed. - Enable/disable menu options at the right time. - Remove support for Qt < 5.0. - Remove the application name constant and hardcode the name to simplify the code. - Improve log messages. - Replace the sdbm hashing with Qt's MD5 hashing.
This commit is contained in:
parent
ed0dc62b69
commit
46ba0310fa
@ -21,4 +21,5 @@ Bug fixes
|
||||
*********
|
||||
|
||||
| `Issue #4810 <https://redmine.postgresql.org/issues/4810>`_ - Fixed an issue where the user is not able to save the new row if the table is empty.
|
||||
| `Issue #5490 <https://redmine.postgresql.org/issues/5490>`_ - Make the runtime configuration dialog non-modal.
|
||||
| `Issue #5646 <https://redmine.postgresql.org/issues/5646>`_ - Ensure that RLS Policy node should be searchable using search object.
|
||||
|
@ -10,10 +10,11 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
|
||||
#include "ConfigWindow.h"
|
||||
#include "ui_ConfigWindow.h"
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
ConfigWindow::ConfigWindow(QWidget *parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
@ -21,34 +22,13 @@ ConfigWindow::ConfigWindow(QWidget *parent) :
|
||||
}
|
||||
|
||||
void ConfigWindow::initConfigWindow()
|
||||
{
|
||||
ui = new Ui::ConfigWindow;
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
void ConfigWindow::on_buttonBox_accepted()
|
||||
{
|
||||
this->close();
|
||||
}
|
||||
|
||||
void ConfigWindow::on_buttonBox_rejected()
|
||||
{
|
||||
this->close();
|
||||
}
|
||||
|
||||
void ConfigWindow::on_chkFixedPort_stateChanged(int state)
|
||||
{
|
||||
if (state == Qt::Checked)
|
||||
ui->spinPortNumber->setEnabled(true);
|
||||
else
|
||||
ui->spinPortNumber->setEnabled(false);
|
||||
}
|
||||
|
||||
void ConfigWindow::LoadSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
setWindowTitle(QString(tr("%1 Configuration")).arg(PGA_APP_NAME));
|
||||
ui = new Ui::ConfigWindow;
|
||||
ui->setupUi(this);
|
||||
|
||||
m_needRestart = false;
|
||||
|
||||
ui->browserCommandLineEdit->setText(settings.value("BrowserCommand").toString());
|
||||
|
||||
@ -78,7 +58,7 @@ void ConfigWindow::LoadSettings()
|
||||
ui->applicationPathLineEdit->setText(settings.value("ApplicationPath").toString());
|
||||
}
|
||||
|
||||
bool ConfigWindow::SaveSettings()
|
||||
void ConfigWindow::on_buttonBox_accepted()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
@ -90,10 +70,10 @@ bool ConfigWindow::SaveSettings()
|
||||
QString pythonpath = ui->pythonPathLineEdit->text();
|
||||
QString applicationpath = ui->applicationPathLineEdit->text();
|
||||
|
||||
bool needRestart = (settings.value("FixedPort").toBool() != fixedport ||
|
||||
settings.value("PortNumber").toInt() != portnumber ||
|
||||
settings.value("PythonPath").toString() != pythonpath ||
|
||||
settings.value("ApplicationPath").toString() != applicationpath);
|
||||
m_needRestart = (settings.value("FixedPort").toBool() != fixedport ||
|
||||
settings.value("PortNumber").toInt() != portnumber ||
|
||||
settings.value("PythonPath").toString() != pythonpath ||
|
||||
settings.value("ApplicationPath").toString() != applicationpath);
|
||||
|
||||
settings.setValue("BrowserCommand", browsercommand);
|
||||
settings.setValue("FixedPort", fixedport);
|
||||
@ -104,6 +84,23 @@ bool ConfigWindow::SaveSettings()
|
||||
|
||||
settings.sync();
|
||||
|
||||
return needRestart;
|
||||
emit accepted(m_needRestart);
|
||||
emit closing(true);
|
||||
|
||||
this->close();
|
||||
}
|
||||
|
||||
void ConfigWindow::on_buttonBox_rejected()
|
||||
{
|
||||
emit closing(false);
|
||||
this->close();
|
||||
}
|
||||
|
||||
void ConfigWindow::on_chkFixedPort_stateChanged(int state)
|
||||
{
|
||||
if (state == Qt::Checked)
|
||||
ui->spinPortNumber->setEnabled(true);
|
||||
else
|
||||
ui->spinPortNumber->setEnabled(false);
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,11 @@ class ConfigWindow : public QDialog
|
||||
public:
|
||||
explicit ConfigWindow(QWidget *parent = Q_NULLPTR);
|
||||
|
||||
void LoadSettings();
|
||||
bool SaveSettings();
|
||||
bool NeedRestart() { return m_needRestart; };
|
||||
|
||||
signals:
|
||||
void accepted(bool needRestart);
|
||||
void closing(bool accepted);
|
||||
|
||||
private slots:
|
||||
void on_buttonBox_accepted();
|
||||
@ -35,6 +38,8 @@ private slots:
|
||||
|
||||
private:
|
||||
Ui::ConfigWindow *ui;
|
||||
bool m_needRestart;
|
||||
|
||||
void initConfigWindow();
|
||||
};
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
<string>pgAdmin 4 Configuration</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
|
@ -9,16 +9,20 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "FloatingWindow.h"
|
||||
#include "ui_FloatingWindow.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
|
||||
|
||||
FloatingWindow::FloatingWindow(QWidget *parent) :
|
||||
QMainWindow(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool FloatingWindow::Init()
|
||||
{
|
||||
ui = new Ui::FloatingWindow;
|
||||
@ -41,12 +45,13 @@ bool FloatingWindow::Init()
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Create the menu
|
||||
void FloatingWindow::createMenu()
|
||||
{
|
||||
createActions();
|
||||
|
||||
m_floatingWindowMenu = menuBar()->addMenu(QString(tr("&%1")).arg(PGA_APP_NAME));
|
||||
m_floatingWindowMenu = menuBar()->addMenu(tr("&pgAdmin 4"));
|
||||
m_floatingWindowMenu->addAction(m_newAction);
|
||||
m_floatingWindowMenu->addAction(m_copyUrlAction);
|
||||
m_floatingWindowMenu->addSeparator();
|
||||
@ -56,19 +61,24 @@ void FloatingWindow::createMenu()
|
||||
m_floatingWindowMenu->addAction(m_quitAction);
|
||||
}
|
||||
|
||||
|
||||
// Create the menu actions
|
||||
void FloatingWindow::createActions()
|
||||
{
|
||||
m_newAction = new QAction(QString(tr("&New %1 window...")).arg(PGA_APP_NAME), this);
|
||||
m_newAction = new QAction(tr("&New pgAdmin 4 window..."), this);
|
||||
m_newAction->setEnabled(false);
|
||||
connect(m_newAction, SIGNAL(triggered()), m_menuActions, SLOT(onNew()));
|
||||
|
||||
m_copyUrlAction = new QAction(tr("&Copy server URL"), this);
|
||||
m_copyUrlAction->setEnabled(false);
|
||||
connect(m_copyUrlAction, SIGNAL(triggered()), m_menuActions, SLOT(onCopyUrl()));
|
||||
|
||||
m_configAction = new QAction(tr("C&onfigure..."), this);
|
||||
m_configAction->setEnabled(true);
|
||||
connect(m_configAction, SIGNAL(triggered()), m_menuActions, SLOT(onConfig()));
|
||||
|
||||
m_logAction = new QAction(tr("&View log..."), this);
|
||||
m_logAction->setEnabled(true);
|
||||
connect(m_logAction, SIGNAL(triggered()), m_menuActions, SLOT(onLog()));
|
||||
|
||||
m_quitAction = new QAction(tr("&Shut down server"), this);
|
||||
@ -76,12 +86,23 @@ void FloatingWindow::createActions()
|
||||
connect(m_quitAction, SIGNAL(triggered()), m_menuActions, SLOT(onQuit()));
|
||||
}
|
||||
|
||||
void FloatingWindow::enableShutdownMenu()
|
||||
|
||||
void FloatingWindow::enablePostStartOptions()
|
||||
{
|
||||
if (m_newAction != Q_NULLPTR)
|
||||
m_newAction->setEnabled(true);
|
||||
|
||||
if (m_copyUrlAction != Q_NULLPTR)
|
||||
m_copyUrlAction->setEnabled(true);
|
||||
|
||||
if (m_configAction != Q_NULLPTR)
|
||||
m_configAction->setEnabled(true);
|
||||
|
||||
if (m_logAction != Q_NULLPTR)
|
||||
m_logAction->setEnabled(true);
|
||||
|
||||
if (m_quitAction != Q_NULLPTR)
|
||||
{
|
||||
m_quitAction->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingWindow::setMenuActions(MenuActions * menuActions)
|
||||
@ -89,6 +110,7 @@ void FloatingWindow::setMenuActions(MenuActions * menuActions)
|
||||
m_menuActions = menuActions;
|
||||
}
|
||||
|
||||
|
||||
void FloatingWindow::closeEvent(QCloseEvent * event)
|
||||
{
|
||||
// Emit the signal to shut down the python server.
|
||||
|
@ -9,11 +9,9 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef FLOATINGWINDOW_H
|
||||
#define FLOATINGWINDOW_H
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "MenuActions.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
@ -30,7 +28,7 @@ public:
|
||||
explicit FloatingWindow(QWidget *parent = Q_NULLPTR);
|
||||
|
||||
bool Init();
|
||||
void enableShutdownMenu();
|
||||
void enablePostStartOptions();
|
||||
void setMenuActions(MenuActions * menuActions);
|
||||
|
||||
private:
|
||||
|
@ -17,7 +17,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
<string>pgAdmin 4</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
|
@ -13,13 +13,13 @@
|
||||
#include "LogWindow.h"
|
||||
#include "ui_LogWindow.h"
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <QTime>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
LogWindow::LogWindow(QWidget *parent, QString serverLogFile) :
|
||||
QDialog(parent),
|
||||
m_serverLogFile(serverLogFile)
|
||||
LogWindow::LogWindow(QWidget *parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
initLogWindow();
|
||||
}
|
||||
@ -35,15 +35,13 @@ void LogWindow::LoadLog()
|
||||
int startupLines;
|
||||
int serverLines;
|
||||
|
||||
QString startup_log = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + (QString("/.%1.startup.log").arg(PGA_APP_NAME)).remove(" ");
|
||||
|
||||
ui->lblStatus->setText(tr("Loading logfiles..."));
|
||||
|
||||
ui->lblStartupLog->setText(tr("Startup Log (%1):").arg(startup_log));
|
||||
ui->lblServerLog->setText(tr("Server Log (%1):").arg(m_serverLogFile));
|
||||
ui->lblStartupLog->setText(tr("Startup Log (%1):").arg(getStartupLogFile()));
|
||||
ui->lblServerLog->setText(tr("Server Log (%1):").arg(getServerLogFile()));
|
||||
|
||||
startupLines = this->readLog(startup_log, ui->textStartupLog);
|
||||
serverLines = this->readLog(m_serverLogFile, ui->textServerLog);
|
||||
startupLines = this->readLog(getStartupLogFile(), ui->textStartupLog);
|
||||
serverLines = this->readLog(getServerLogFile(), ui->textServerLog);
|
||||
|
||||
ui->lblStatus->setText(QString(tr("Loaded startup log (%1 lines) and server log (%2 lines).")).arg(startupLines).arg(serverLines));
|
||||
}
|
||||
@ -75,7 +73,7 @@ int LogWindow::readLog(QString logFile, QPlainTextEdit *logWidget)
|
||||
log = fopen(logFile.toUtf8().data(), "r");
|
||||
if (log == Q_NULLPTR)
|
||||
{
|
||||
logWidget->setPlainText(QString(tr("The log file (%1) could not be opened.")).arg(m_serverLogFile));
|
||||
logWidget->setPlainText(QString(tr("The log file (%1) could not be opened.")).arg(getServerLogFile()));
|
||||
this->setDisabled(false);
|
||||
QApplication::restoreOverrideCursor();
|
||||
return 0;
|
||||
|
@ -24,7 +24,7 @@ class LogWindow : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LogWindow(QWidget *parent = Q_NULLPTR, QString serverLogFile = "");
|
||||
explicit LogWindow(QWidget *parent = Q_NULLPTR);
|
||||
void LoadLog();
|
||||
|
||||
private slots:
|
||||
@ -33,9 +33,6 @@ private slots:
|
||||
private:
|
||||
Ui::LogWindow *ui;
|
||||
|
||||
QString m_startupLogFile;
|
||||
QString m_serverLogFile;
|
||||
|
||||
void initLogWindow();
|
||||
int readLog(QString logFile, QPlainTextEdit *logWidget);
|
||||
};
|
||||
|
@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
<string>pgAdmin 4 Log</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
|
@ -11,8 +11,12 @@
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QTextStream>
|
||||
#include <QStandardPaths>
|
||||
|
||||
Logger* Logger::m_pThis = Q_NULLPTR;
|
||||
QString Logger::m_sFileName = "";
|
||||
QFile* Logger::m_Logfile = Q_NULLPTR;
|
||||
|
||||
Logger::Logger()
|
||||
@ -28,9 +32,8 @@ Logger* Logger::GetLogger()
|
||||
if (m_pThis == Q_NULLPTR)
|
||||
{
|
||||
m_pThis = new Logger();
|
||||
m_sFileName = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + (QString("/.%1.startup.log").arg(PGA_APP_NAME)).remove(" ");
|
||||
m_Logfile = new QFile;
|
||||
m_Logfile->setFileName(m_sFileName);
|
||||
m_Logfile->setFileName(getStartupLogFile());
|
||||
m_Logfile->open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
m_Logfile->setPermissions(QFile::ReadOwner|QFile::WriteOwner);
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ private:
|
||||
virtual ~Logger();
|
||||
|
||||
static Logger* m_pThis;
|
||||
static QString m_sFileName;
|
||||
static QFile *m_Logfile;
|
||||
};
|
||||
|
||||
|
@ -9,11 +9,16 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "MenuActions.h"
|
||||
|
||||
// QT headers
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QEventLoop>
|
||||
#include <QMessageBox>
|
||||
#include <QProcess>
|
||||
#include <QSettings>
|
||||
|
||||
MenuActions::MenuActions()
|
||||
{
|
||||
@ -24,10 +29,6 @@ void MenuActions::setAppServerUrl(QString appServerUrl)
|
||||
m_appServerUrl = appServerUrl;
|
||||
}
|
||||
|
||||
void MenuActions::setLogFile(QString logFile)
|
||||
{
|
||||
m_logFile = logFile;
|
||||
}
|
||||
|
||||
// Create a new application browser window on user request
|
||||
void MenuActions::onNew()
|
||||
@ -64,18 +65,24 @@ void MenuActions::onCopyUrl()
|
||||
// Show the config dialogue
|
||||
void MenuActions::onConfig()
|
||||
{
|
||||
bool ok;
|
||||
if (!m_configWindow)
|
||||
m_configWindow = new ConfigWindow();
|
||||
|
||||
ConfigWindow *dlg = new ConfigWindow();
|
||||
dlg->LoadSettings();
|
||||
dlg->setModal(true);
|
||||
ok = dlg->exec();
|
||||
m_configWindow->show();
|
||||
m_configWindow->raise();
|
||||
m_configWindow->activateWindow();
|
||||
connect(m_configWindow, SIGNAL(accepted(bool)), this, SLOT(onConfigDone(bool)));
|
||||
}
|
||||
|
||||
if (ok)
|
||||
|
||||
void MenuActions::onConfigDone(bool needRestart)
|
||||
{
|
||||
if (needRestart)
|
||||
{
|
||||
bool needRestart = dlg->SaveSettings();
|
||||
|
||||
if (needRestart && QMessageBox::Yes == QMessageBox::question(Q_NULLPTR, tr("Shut down server?"), QString(tr("The %1 server must be restarted for changes to take effect. Do you want to shut down the server now?")).arg(PGA_APP_NAME), QMessageBox::Yes | QMessageBox::No))
|
||||
if (QMessageBox::Yes == QMessageBox::question(Q_NULLPTR,
|
||||
tr("Shut down server?"),
|
||||
tr("The pgAdmin 4 server must be restarted for changes to take effect. Do you want to shut down the server now?"),
|
||||
QMessageBox::Yes | QMessageBox::No))
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
@ -87,16 +94,13 @@ void MenuActions::onLog()
|
||||
QSettings settings;
|
||||
|
||||
if (!m_logWindow)
|
||||
{
|
||||
m_logWindow = new LogWindow(Q_NULLPTR, m_logFile);
|
||||
m_logWindow->setWindowTitle(QString(tr("%1 Log")).arg(PGA_APP_NAME));
|
||||
}
|
||||
m_logWindow = new LogWindow();
|
||||
|
||||
m_logWindow->show();
|
||||
m_logWindow->raise();
|
||||
m_logWindow->activateWindow();
|
||||
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 100 );
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
|
||||
|
||||
m_logWindow->LoadLog();
|
||||
}
|
||||
@ -105,10 +109,10 @@ void MenuActions::onLog()
|
||||
// Exit
|
||||
void MenuActions::onQuit()
|
||||
{
|
||||
if (QMessageBox::Yes == QMessageBox::question(Q_NULLPTR, tr("Shut down server?"), QString(tr("Are you sure you want to shut down the %1 server?")).arg(PGA_APP_NAME), QMessageBox::Yes | QMessageBox::No))
|
||||
if (QMessageBox::Yes == QMessageBox::question(Q_NULLPTR, tr("Shut down server?"), tr("Are you sure you want to shut down the pgAdmin 4 server?"), QMessageBox::Yes | QMessageBox::No))
|
||||
{
|
||||
// Emit the signal to shut down the python server.
|
||||
emit shutdownSignal(m_appServerUrl);
|
||||
exit(0);
|
||||
QApplication::quit();
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,6 @@
|
||||
#ifndef MENUACTIONS_H
|
||||
#define MENUACTIONS_H
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
|
||||
// App headers
|
||||
#include "LogWindow.h"
|
||||
#include "ConfigWindow.h"
|
||||
|
||||
@ -25,13 +22,15 @@ public:
|
||||
MenuActions();
|
||||
|
||||
void setAppServerUrl(QString appServerUrl);
|
||||
void setLogFile(QString logFile);
|
||||
QString getAppServerUrl() { return m_appServerUrl; }
|
||||
|
||||
private:
|
||||
QString m_appServerUrl = "";
|
||||
QString m_logFile = "";
|
||||
LogWindow *m_logWindow = Q_NULLPTR;
|
||||
ConfigWindow *m_configWindow = Q_NULLPTR;
|
||||
|
||||
public slots:
|
||||
void onConfigDone(bool needRestart);
|
||||
|
||||
protected slots:
|
||||
void onNew();
|
||||
|
605
runtime/Runtime.cpp
Normal file
605
runtime/Runtime.cpp
Normal file
@ -0,0 +1,605 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2020, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// Runtime.cpp - Core of the runtime
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "Runtime.h"
|
||||
#include "Server.h"
|
||||
#include "TrayIcon.h"
|
||||
#include "MenuActions.h"
|
||||
#include "ConfigWindow.h"
|
||||
#include "FloatingWindow.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include "macos.h"
|
||||
#endif
|
||||
|
||||
// Must be before QT
|
||||
#include <Python.h>
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QNetworkProxyFactory>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QTime>
|
||||
|
||||
|
||||
Runtime::Runtime()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool Runtime::go(int argc, char *argv[])
|
||||
{
|
||||
// Before starting main application, need to set 'QT_X11_NO_MITSHM=1'
|
||||
// to make the runtime work with IBM PPC machines.
|
||||
#if defined (Q_OS_LINUX)
|
||||
QByteArray val("1");
|
||||
qputenv("QT_X11_NO_MITSHM", val);
|
||||
#endif
|
||||
|
||||
// Create the QT application
|
||||
QApplication app(argc, argv);
|
||||
app.setQuitOnLastWindowClosed(false);
|
||||
|
||||
// Setup look n feel
|
||||
setupStyling(&app);
|
||||
|
||||
// Setup the settings management
|
||||
QCoreApplication::setOrganizationName("pgadmin");
|
||||
QCoreApplication::setOrganizationDomain("pgadmin.org");
|
||||
QCoreApplication::setApplicationName("pgadmin4");
|
||||
|
||||
// Interlock
|
||||
if (alreadyRunning())
|
||||
exit(0);
|
||||
|
||||
// Proxy config
|
||||
configureProxy();
|
||||
|
||||
// Display the spash screen
|
||||
QSplashScreen *splash = displaySplash(&app);
|
||||
|
||||
// Get the port number to use
|
||||
quint16 port = getPort();
|
||||
|
||||
// Generate a random key to authenticate the client to the server
|
||||
QString key = QUuid::createUuid().toString();
|
||||
key = key.mid(1, key.length() - 2);
|
||||
|
||||
// Create Menu Actions
|
||||
MenuActions *menuActions = new MenuActions();
|
||||
|
||||
// Create the control object (tray icon or floating window
|
||||
FloatingWindow *floatingWindow = Q_NULLPTR;
|
||||
TrayIcon *trayIcon = Q_NULLPTR;
|
||||
|
||||
splash->showMessage(QString(QWidget::tr("Checking for system tray...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable())
|
||||
trayIcon = createTrayIcon(splash, menuActions);
|
||||
else
|
||||
floatingWindow = createFloatingWindow(splash, menuActions);
|
||||
|
||||
// Fire up the app server
|
||||
Server *server = startServerLoop(splash, floatingWindow, trayIcon, port, key);
|
||||
|
||||
// Ensure we'll cleanup
|
||||
QObject::connect(server, SIGNAL(finished()), server, SLOT(deleteLater()));
|
||||
atexit(cleanup);
|
||||
|
||||
// Generate the app server URL
|
||||
QString url = QString("http://127.0.0.1:%1/?key=%2").arg(port).arg(key);
|
||||
Logger::GetLogger()->Log(QString(QWidget::tr("Application Server URL: %1")).arg(url));
|
||||
|
||||
// Check the server is running
|
||||
checkServer(splash, url);
|
||||
|
||||
// Stash the URL for any duplicate processes to open
|
||||
createAddressFile(url);
|
||||
|
||||
// Go!
|
||||
menuActions->setAppServerUrl(url);
|
||||
|
||||
// Enable the shutdown server menu as server started successfully.
|
||||
if (trayIcon != Q_NULLPTR)
|
||||
trayIcon->enablePostStartOptions();
|
||||
if (floatingWindow != Q_NULLPTR)
|
||||
floatingWindow->enablePostStartOptions();
|
||||
|
||||
// Open the browser if needed
|
||||
if (m_settings.value("OpenTabAtStartup", true).toBool())
|
||||
openBrowserTab(url);
|
||||
|
||||
// Make sure the server is shutdown if the server is quit by the user
|
||||
QObject::connect(menuActions, SIGNAL(shutdownSignal(QUrl)), server, SLOT(shutdown(QUrl)));
|
||||
|
||||
// Final cleanup
|
||||
splash->finish(Q_NULLPTR);
|
||||
|
||||
if (floatingWindow != Q_NULLPTR)
|
||||
floatingWindow->show();
|
||||
|
||||
Logger::GetLogger()->Log("Everything works fine, successfully started pgAdmin4.");
|
||||
Logger::ReleaseLogger();
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
|
||||
// Setup the styling
|
||||
void Runtime::setupStyling(QApplication *app)
|
||||
{
|
||||
// Setup the styling
|
||||
#ifndef Q_OS_LINUX
|
||||
QFile stylesheet;
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
QSettings registry("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", QSettings::Registry64Format);
|
||||
if (!registry.value("AppsUseLightTheme", true).toBool())
|
||||
{
|
||||
qDebug( "Windows Dark Mode..." );
|
||||
stylesheet.setFileName(":/qdarkstyle/style.qss");
|
||||
stylesheet.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream stream(&stylesheet);
|
||||
app->setStyleSheet(stream.readAll());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
if (IsDarkMode())
|
||||
{
|
||||
qDebug( "macOS Dark Mode...");
|
||||
stylesheet.setFileName(":/qdarkstyle/style.qss");
|
||||
stylesheet.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream stream(&stylesheet);
|
||||
app->setStyleSheet(stream.readAll());
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Set high DPI pixmap to display icons clear on Qt widget.
|
||||
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
}
|
||||
|
||||
// Check if we're already running. If we are, open a new browser tab.
|
||||
bool Runtime::alreadyRunning()
|
||||
{
|
||||
// Create a system-wide semaphore keyed by app name, exe hash and the username
|
||||
// to ensure instances are unique to the user and path
|
||||
QString userName = qgetenv("USER"); // *nix
|
||||
if (userName.isEmpty())
|
||||
userName = qgetenv("USERNAME"); // Windows
|
||||
|
||||
QString semaName = QString("pgadmin4-%1-%2-sema").arg(userName).arg(getExeHash());
|
||||
QString shmemName = QString("pgadmin4-%1-%2-shmem").arg(userName).arg(getExeHash());
|
||||
qDebug() << "Semaphore name:" << semaName;
|
||||
qDebug() << "Shared memory segment name:" << shmemName;
|
||||
|
||||
QSystemSemaphore sema(semaName, 1);
|
||||
sema.acquire();
|
||||
|
||||
#ifndef Q_OS_WIN32
|
||||
// We may need to clean up stale shmem segments on *nix. Attaching and detaching
|
||||
// should remove the segment if it is orphaned.
|
||||
QSharedMemory stale_shmem(shmemName);
|
||||
if (stale_shmem.attach())
|
||||
stale_shmem.detach();
|
||||
#endif
|
||||
|
||||
m_shmem = new QSharedMemory(shmemName);
|
||||
bool is_running;
|
||||
if (m_shmem->attach())
|
||||
is_running = true;
|
||||
else
|
||||
{
|
||||
m_shmem->create(1);
|
||||
is_running = false;
|
||||
}
|
||||
sema.release();
|
||||
|
||||
if (is_running)
|
||||
{
|
||||
QFile addressFile(getAddressFile());
|
||||
addressFile.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
QTextStream in(&addressFile);
|
||||
QString url = in.readLine();
|
||||
|
||||
qDebug() << "Already running. Opening browser tab to: " << url << "and exiting.";
|
||||
openBrowserTab(url);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Runtime::configureProxy()
|
||||
{
|
||||
// In windows and linux, it is required to set application level proxy
|
||||
// because socket bind logic to find free port gives socket creation error
|
||||
// when system proxy is configured. We are also setting
|
||||
// "setUseSystemConfiguration"=true to use the system proxy which will
|
||||
// override this application level proxy. As this bug is fixed in Qt 5.9 so
|
||||
// need to set application proxy for Qt version < 5.9.
|
||||
//
|
||||
#if defined (Q_OS_WIN) && QT_VERSION <= 0x050800
|
||||
// Give dummy URL required to find proxy server configured in windows.
|
||||
QNetworkProxyQuery proxyQuery(QUrl("https://www.pgadmin.org"));
|
||||
QNetworkProxy l_proxy;
|
||||
QList<QNetworkProxy> listOfProxies = QNetworkProxyFactory::systemProxyForQuery(proxyQuery);
|
||||
|
||||
if (listOfProxies.size())
|
||||
{
|
||||
l_proxy = listOfProxies[0];
|
||||
|
||||
// If host name is not empty means proxy server is configured.
|
||||
if (!l_proxy.hostName().isEmpty()) {
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (Q_OS_LINUX) && QT_VERSION <= 0x050800
|
||||
QByteArray proxy_env;
|
||||
proxy_env = qgetenv("http_proxy");
|
||||
// If http_proxy environment is defined in linux then proxy server is configured.
|
||||
if (!proxy_env.isEmpty()) {
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Display the splash screen
|
||||
QSplashScreen * Runtime::displaySplash(QApplication *app)
|
||||
{
|
||||
QSplashScreen *splash = new QSplashScreen();
|
||||
splash->setPixmap(QPixmap(":/splash.png"));
|
||||
splash->setWindowFlags(splash->windowFlags() | Qt::WindowStaysOnTopHint);
|
||||
splash->show();
|
||||
app->processEvents(QEventLoop::AllEvents);
|
||||
|
||||
return splash;
|
||||
}
|
||||
|
||||
|
||||
// Get the port number we're going to use
|
||||
quint16 Runtime::getPort()
|
||||
{
|
||||
quint16 port = 0L;
|
||||
|
||||
if (m_settings.value("FixedPort", false).toBool())
|
||||
{
|
||||
// Use the fixed port number
|
||||
port = m_settings.value("PortNumber", 5050).toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find an unused port number. Essentially, we're just reserving one
|
||||
// here that Flask will use when we start up the server.
|
||||
QTcpSocket socket;
|
||||
|
||||
#if QT_VERSION >= 0x050900
|
||||
socket.setProxy(QNetworkProxy::NoProxy);
|
||||
#endif
|
||||
|
||||
socket.bind(0, QTcpSocket::ShareAddress);
|
||||
port = socket.localPort();
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
|
||||
// Create a tray icon
|
||||
TrayIcon * Runtime::createTrayIcon(QSplashScreen *splash, MenuActions *menuActions)
|
||||
{
|
||||
TrayIcon *trayIcon = Q_NULLPTR;
|
||||
|
||||
splash->showMessage(QString(QWidget::tr("Checking for system tray...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("Checking for system tray...");
|
||||
|
||||
// Start the tray service
|
||||
trayIcon = new TrayIcon();
|
||||
|
||||
// Set the MenuActions object to connect to slot
|
||||
if (trayIcon != Q_NULLPTR)
|
||||
trayIcon->setMenuActions(menuActions);
|
||||
|
||||
trayIcon->Init();
|
||||
|
||||
return trayIcon;
|
||||
}
|
||||
|
||||
|
||||
// Create a floating window
|
||||
FloatingWindow * Runtime::createFloatingWindow(QSplashScreen *splash, MenuActions *menuActions)
|
||||
{
|
||||
FloatingWindow *floatingWindow = Q_NULLPTR;
|
||||
|
||||
splash->showMessage(QString(QWidget::tr("System tray not found, creating floating window...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("System tray not found, creating floating window...");
|
||||
floatingWindow = new FloatingWindow();
|
||||
if (floatingWindow == Q_NULLPTR)
|
||||
{
|
||||
QString error = QString(QWidget::tr("Unable to initialize either a tray icon or floating window."));
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set the MenuActions object to connect to slot
|
||||
floatingWindow->setMenuActions(menuActions);
|
||||
floatingWindow->Init();
|
||||
|
||||
return floatingWindow;
|
||||
}
|
||||
|
||||
|
||||
// Server startup loop
|
||||
Server * Runtime::startServerLoop(QSplashScreen *splash, FloatingWindow *floatingWindow, TrayIcon *trayIcon, int port, QString key)
|
||||
{
|
||||
bool done = false;
|
||||
Server *server;
|
||||
|
||||
while (!done)
|
||||
{
|
||||
server = startServer(splash, port, key);
|
||||
if (server == NULL)
|
||||
{
|
||||
Logger::ReleaseLogger();
|
||||
QApplication::quit();
|
||||
}
|
||||
|
||||
// Check for server startup errors
|
||||
if (server->isFinished() || server->getError().length() > 0)
|
||||
{
|
||||
splash->finish(Q_NULLPTR);
|
||||
|
||||
qDebug() << server->getError();
|
||||
|
||||
QString error = QString(QWidget::tr("An error occurred initialising the pgAdmin 4 server:\n\n%1")).arg(server->getError());
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
Logger::GetLogger()->Log(error);
|
||||
|
||||
delete server;
|
||||
|
||||
// Allow the user to tweak the Python Path if needed
|
||||
m_configDone = false;
|
||||
|
||||
ConfigWindow *dlg = new ConfigWindow();
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dlg->show();
|
||||
dlg->raise();
|
||||
dlg->activateWindow();
|
||||
QObject::connect(dlg, SIGNAL(closing(bool)), this, SLOT(onConfigDone(bool)));
|
||||
|
||||
// Wait for configuration to be completed
|
||||
while (!m_configDone)
|
||||
delay(100);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Startup appears successful
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
// Slot called when re-configuration is done.
|
||||
void Runtime::onConfigDone(bool accepted)
|
||||
{
|
||||
if (accepted)
|
||||
m_configDone = true;
|
||||
else
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
// Start the server
|
||||
Server * Runtime::startServer(QSplashScreen *splash, int port, QString key)
|
||||
{
|
||||
Server *server;
|
||||
|
||||
splash->showMessage(QString(QWidget::tr("Starting pgAdmin4 server...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("Starting pgAdmin4 server...");
|
||||
|
||||
QString msg = QString(QWidget::tr("Creating server object, port:%1, key:%2, logfile:%3")).arg(port).arg(key).arg(getServerLogFile());
|
||||
Logger::GetLogger()->Log(msg);
|
||||
server = new Server(this, port, key, getServerLogFile());
|
||||
|
||||
Logger::GetLogger()->Log("Initializing server...");
|
||||
if (!server->Init())
|
||||
{
|
||||
splash->finish(Q_NULLPTR);
|
||||
|
||||
qDebug() << server->getError();
|
||||
|
||||
QString error = QString(QWidget::tr("An error occurred initialising the pgAdmin 4 server:\n\n%1")).arg(server->getError());
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Logger::GetLogger()->Log("Server initialized, starting server thread...");
|
||||
server->start();
|
||||
|
||||
// This is a hack to give the server a chance to start and potentially fail. As
|
||||
// the Python interpreter is a synchronous call, we can't check for proper startup
|
||||
// easily in a more robust way - we have to rely on a clean startup not returning.
|
||||
// It should always fail pretty quickly, and take longer to start if it succeeds, so
|
||||
// we don't really get a visible delay here.
|
||||
delay(1000);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
// Check the server is running properly
|
||||
void Runtime::checkServer(QSplashScreen *splash, QString url)
|
||||
{
|
||||
// Read the server connection timeout from the registry or set the default timeout.
|
||||
int timeout = m_settings.value("ConnectionTimeout", 90).toInt();
|
||||
|
||||
// Now the server should be up, we'll attempt to connect and get a response.
|
||||
// We'll retry in a loop a few time before aborting if necessary.
|
||||
|
||||
QTime endTime = QTime::currentTime().addSecs(timeout);
|
||||
QTime midTime1 = QTime::currentTime().addSecs(timeout/3);
|
||||
QTime midTime2 = QTime::currentTime().addSecs(timeout*2/3);
|
||||
bool alive = false;
|
||||
|
||||
Logger::GetLogger()->Log("The server should be up. Attempting to connect and get a response.");
|
||||
while(QTime::currentTime() <= endTime)
|
||||
{
|
||||
alive = pingServer(QUrl(url));
|
||||
|
||||
if (alive)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if(QTime::currentTime() >= midTime1)
|
||||
{
|
||||
if(QTime::currentTime() < midTime2) {
|
||||
splash->showMessage(QString(QWidget::tr("Taking longer than usual...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
}
|
||||
else
|
||||
{
|
||||
splash->showMessage(QString(QWidget::tr("Almost there...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
}
|
||||
}
|
||||
|
||||
delay(200);
|
||||
}
|
||||
|
||||
// Attempt to connect one more time in case of a long network timeout while looping
|
||||
Logger::GetLogger()->Log("Attempt to connect one more time in case of a long network timeout while looping");
|
||||
if (!alive && !pingServer(QUrl(url)))
|
||||
{
|
||||
splash->finish(Q_NULLPTR);
|
||||
QString error(QWidget::tr("The pgAdmin 4 server could not be contacted."));
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create the address file
|
||||
void Runtime::createAddressFile(QString url)
|
||||
{
|
||||
QFile addressFile(getAddressFile());
|
||||
if (addressFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
addressFile.setPermissions(QFile::ReadOwner|QFile::WriteOwner);
|
||||
QTextStream out(&addressFile);
|
||||
out << url << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Open a browser tab
|
||||
void Runtime::openBrowserTab(QString url)
|
||||
{
|
||||
QString cmd = m_settings.value("BrowserCommand").toString();
|
||||
|
||||
if (!cmd.isEmpty())
|
||||
{
|
||||
cmd.replace("%URL%", url);
|
||||
QProcess::startDetached(cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!QDesktopServices::openUrl(url))
|
||||
{
|
||||
QString error(QWidget::tr("Failed to open the system default web browser. Is one installed?."));
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Make a request to the Python API server
|
||||
QString Runtime::serverRequest(QUrl url, QString path)
|
||||
{
|
||||
QNetworkAccessManager manager;
|
||||
QEventLoop loop;
|
||||
QNetworkReply *reply;
|
||||
QVariant redirectUrl;
|
||||
|
||||
|
||||
url.setPath(path);
|
||||
QString requestUrl = url.toString();
|
||||
|
||||
do
|
||||
{
|
||||
reply = manager.get(QNetworkRequest(url));
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
|
||||
redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
url = redirectUrl.toUrl();
|
||||
|
||||
if (!redirectUrl.isNull())
|
||||
delete reply;
|
||||
|
||||
} while (!redirectUrl.isNull());
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
qDebug() << "Failed to connect to the server:" << reply->errorString() << "- request URL:" << requestUrl << ".";
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString response = reply->readAll();
|
||||
qDebug() << "Server response:" << response << "- request URL:" << requestUrl << ".";
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
// Ping the application server to see if it's alive
|
||||
bool Runtime::pingServer(QUrl url)
|
||||
{
|
||||
return serverRequest(url, "/misc/ping") == "PING";
|
||||
}
|
||||
|
||||
|
||||
// Shutdown the application server
|
||||
bool Runtime::shutdownServer(QUrl url)
|
||||
{
|
||||
return serverRequest(url, "/misc/shutdown") == "SHUTDOWN";
|
||||
}
|
||||
|
||||
|
||||
void Runtime::delay(int milliseconds)
|
||||
{
|
||||
QTime endTime = QTime::currentTime().addMSecs(milliseconds);
|
||||
while(QTime::currentTime() < endTime)
|
||||
{
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
|
||||
}
|
||||
}
|
||||
|
65
runtime/Runtime.h
Normal file
65
runtime/Runtime.h
Normal file
@ -0,0 +1,65 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2020, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// Runtime.h - Core of the runtime
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef RUNTIME_H
|
||||
#define RUNTIME_H
|
||||
|
||||
// Include the Python header here as it needs to appear before any QT
|
||||
// headers anywhere in the app.
|
||||
#ifdef __MINGW32__
|
||||
#include <cmath>
|
||||
#endif
|
||||
#include <Python.h>
|
||||
|
||||
#include "TrayIcon.h"
|
||||
#include "MenuActions.h"
|
||||
#include "FloatingWindow.h"
|
||||
|
||||
// QT headers
|
||||
#include <QtWidgets>
|
||||
|
||||
class Server;
|
||||
|
||||
class Runtime: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Runtime();
|
||||
|
||||
bool alreadyRunning();
|
||||
bool go(int argc, char *argv[]);
|
||||
void delay(int milliseconds);
|
||||
bool shutdownServer(QUrl url);
|
||||
|
||||
private:
|
||||
QSettings m_settings;
|
||||
QSharedMemory *m_shmem;
|
||||
bool m_configDone;
|
||||
|
||||
void setupStyling(QApplication *app);
|
||||
void configureProxy();
|
||||
QSplashScreen *displaySplash(QApplication *app);
|
||||
quint16 getPort();
|
||||
TrayIcon *createTrayIcon(QSplashScreen *splash, MenuActions *menuActions);
|
||||
FloatingWindow *createFloatingWindow(QSplashScreen *splash, MenuActions *menuActions);
|
||||
Server *startServerLoop(QSplashScreen *splash, FloatingWindow *floatingWindow, TrayIcon *trayIcon, int port, QString key);
|
||||
Server *startServer(QSplashScreen *splash, int port, QString key);
|
||||
void checkServer(QSplashScreen *splash, QString url);
|
||||
void createAddressFile(QString url);
|
||||
void openBrowserTab(QString url);
|
||||
QString serverRequest(QUrl url, QString path);
|
||||
bool pingServer(QUrl url);
|
||||
|
||||
private slots:
|
||||
void onConfigDone(bool accepted);
|
||||
};
|
||||
|
||||
#endif // RUNTIME_H
|
@ -10,19 +10,21 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "Logger.h"
|
||||
|
||||
// Must be before QT
|
||||
#include <Python.h>
|
||||
|
||||
#include "Server.h"
|
||||
#include "Logger.h"
|
||||
|
||||
// QT headers
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
|
||||
// App headers
|
||||
#include "Server.h"
|
||||
|
||||
static void add_to_path(QString &python_path, QString path, bool prepend=false)
|
||||
{
|
||||
@ -55,7 +57,8 @@ static void add_to_path(QString &python_path, QString path, bool prepend=false)
|
||||
}
|
||||
}
|
||||
|
||||
Server::Server(quint16 port, QString key, QString logFileName):
|
||||
Server::Server(Runtime *runtime, quint16 port, QString key, QString logFileName):
|
||||
m_runtime(runtime),
|
||||
m_port(port),
|
||||
m_key(key),
|
||||
m_logFileName(logFileName)
|
||||
@ -65,7 +68,7 @@ Server::Server(quint16 port, QString key, QString logFileName):
|
||||
Py_NoUserSiteDirectory=1;
|
||||
Py_DontWriteBytecodeFlag=1;
|
||||
|
||||
PGA_APP_NAME_UTF8 = PGA_APP_NAME.toUtf8();
|
||||
PGA_APP_NAME_UTF8 = QString("pgAdmin 4").toUtf8();
|
||||
|
||||
// Python3 requires conversion of char * to wchar_t *, so...
|
||||
const char *appName = PGA_APP_NAME_UTF8.data();
|
||||
@ -349,7 +352,7 @@ void Server::run()
|
||||
|
||||
void Server::shutdown(QUrl url)
|
||||
{
|
||||
if (!shutdownServer(url))
|
||||
if (!m_runtime->shutdownServer(url))
|
||||
setError(tr("Failed to shut down application server thread."));
|
||||
|
||||
QThread::quit();
|
||||
@ -357,7 +360,7 @@ void Server::shutdown(QUrl url)
|
||||
while(!this->isFinished())
|
||||
{
|
||||
Logger::GetLogger()->Log("Waiting for server to shut down.");
|
||||
delay(250);
|
||||
m_runtime->delay(250);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,18 +12,17 @@
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "Runtime.h"
|
||||
|
||||
// QT headers
|
||||
#include <QThread>
|
||||
#include <QMessageBox>
|
||||
#include <QUrl>
|
||||
|
||||
class Server : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Server(quint16 port, QString key, QString logFileName);
|
||||
Server(Runtime *runtime, quint16 port, QString key, QString logFileName);
|
||||
~Server();
|
||||
|
||||
bool Init();
|
||||
@ -41,7 +40,8 @@ private:
|
||||
QString m_appfile;
|
||||
QString m_error;
|
||||
|
||||
quint16 m_port;
|
||||
Runtime *m_runtime;
|
||||
quint16 m_port;
|
||||
QString m_key;
|
||||
QString m_logFileName;
|
||||
|
||||
|
@ -9,13 +9,17 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin4.h"
|
||||
#include "TrayIcon.h"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
|
||||
TrayIcon::TrayIcon()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void TrayIcon::Init()
|
||||
{
|
||||
createTrayIcon();
|
||||
@ -24,6 +28,7 @@ void TrayIcon::Init()
|
||||
m_trayIcon->show();
|
||||
}
|
||||
|
||||
|
||||
// Create the tray icon
|
||||
void TrayIcon::createTrayIcon()
|
||||
{
|
||||
@ -60,19 +65,24 @@ void TrayIcon::createTrayIcon()
|
||||
setWindowIcon(icon);
|
||||
}
|
||||
|
||||
|
||||
// Create the menu actions
|
||||
void TrayIcon::createActions()
|
||||
{
|
||||
m_newAction = new QAction(QString(tr("&New %1 window...")).arg(PGA_APP_NAME), this);
|
||||
m_newAction = new QAction(tr("&New pgAdmin 4 window..."), this);
|
||||
m_newAction->setEnabled(false);
|
||||
connect(m_newAction, SIGNAL(triggered()), m_menuActions, SLOT(onNew()));
|
||||
|
||||
m_copyUrlAction = new QAction(tr("&Copy server URL"), this);
|
||||
m_copyUrlAction->setEnabled(false);
|
||||
connect(m_copyUrlAction, SIGNAL(triggered()), m_menuActions, SLOT(onCopyUrl()));
|
||||
|
||||
m_configAction = new QAction(tr("&Configure..."), this);
|
||||
m_configAction->setEnabled(true);
|
||||
connect(m_configAction, SIGNAL(triggered()), m_menuActions, SLOT(onConfig()));
|
||||
|
||||
m_logAction = new QAction(tr("&View log..."), this);
|
||||
m_logAction->setEnabled(true);
|
||||
connect(m_logAction, SIGNAL(triggered()), m_menuActions, SLOT(onLog()));
|
||||
|
||||
m_quitAction = new QAction(tr("&Shut down server"), this);
|
||||
@ -80,12 +90,23 @@ void TrayIcon::createActions()
|
||||
connect(m_quitAction, SIGNAL(triggered()), m_menuActions, SLOT(onQuit()));
|
||||
}
|
||||
|
||||
void TrayIcon::enableShutdownMenu()
|
||||
|
||||
void TrayIcon::enablePostStartOptions()
|
||||
{
|
||||
if (m_newAction != Q_NULLPTR)
|
||||
m_newAction->setEnabled(true);
|
||||
|
||||
if (m_copyUrlAction != Q_NULLPTR)
|
||||
m_copyUrlAction->setEnabled(true);
|
||||
|
||||
if (m_configAction != Q_NULLPTR)
|
||||
m_configAction->setEnabled(true);
|
||||
|
||||
if (m_logAction != Q_NULLPTR)
|
||||
m_logAction->setEnabled(true);
|
||||
|
||||
if (m_quitAction != Q_NULLPTR)
|
||||
{
|
||||
m_quitAction->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TrayIcon::setMenuActions(MenuActions * menuActions)
|
||||
|
@ -12,12 +12,11 @@
|
||||
#ifndef TRAYICON_H
|
||||
#define TRAYICON_H
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
|
||||
// QT headers
|
||||
#include <QWidget>
|
||||
#include "MenuActions.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
class TrayIcon : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -26,7 +25,7 @@ public:
|
||||
TrayIcon();
|
||||
|
||||
void Init();
|
||||
void enableShutdownMenu();
|
||||
void enablePostStartOptions();
|
||||
void setMenuActions(MenuActions * menuActions);
|
||||
|
||||
private:
|
||||
|
@ -10,543 +10,72 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "Runtime.h"
|
||||
|
||||
// Must be before QT
|
||||
#include <Python.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QtWidgets>
|
||||
#include <QNetworkProxyFactory>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#else
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QtNetwork>
|
||||
#include <QLineEdit>
|
||||
#include <QInputDialog>
|
||||
#include <QSplashScreen>
|
||||
#include <QUuid>
|
||||
#include <QNetworkProxyFactory>
|
||||
#endif
|
||||
|
||||
// App headers
|
||||
#include "ConfigWindow.h"
|
||||
#include "Server.h"
|
||||
#include "TrayIcon.h"
|
||||
#include "MenuActions.h"
|
||||
#include "FloatingWindow.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include "macos.h"
|
||||
#endif
|
||||
|
||||
#include <QTime>
|
||||
|
||||
QString logFileName;
|
||||
QString addrFileName;
|
||||
// Global vars for caching and avoing shutdown issues
|
||||
QString g_startupLogFile;
|
||||
QString g_serverLogFile;
|
||||
QString g_addressFile;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
/*
|
||||
* Before starting main application, need to set 'QT_X11_NO_MITSHM=1'
|
||||
* to make the runtime work with IBM PPC machine.
|
||||
*/
|
||||
#if defined (Q_OS_LINUX)
|
||||
QByteArray val("1");
|
||||
qputenv("QT_X11_NO_MITSHM", val);
|
||||
#endif
|
||||
|
||||
// Create the QT application
|
||||
QApplication app(argc, argv);
|
||||
app.setQuitOnLastWindowClosed(false);
|
||||
|
||||
// Setup the styling
|
||||
#ifndef Q_OS_LINUX
|
||||
QFile stylesheet;
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
QSettings registry("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", QSettings::Registry64Format);
|
||||
if (!registry.value("AppsUseLightTheme", true).toBool())
|
||||
{
|
||||
qDebug( "Windows Dark Mode..." );
|
||||
stylesheet.setFileName(":/qdarkstyle/style.qss");
|
||||
stylesheet.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream stream(&stylesheet);
|
||||
app.setStyleSheet(stream.readAll());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
if (IsDarkMode())
|
||||
{
|
||||
qDebug( "macOS Dark Mode...");
|
||||
stylesheet.setFileName(":/qdarkstyle/style.qss");
|
||||
stylesheet.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream stream(&stylesheet);
|
||||
app.setStyleSheet(stream.readAll());
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Setup the settings management
|
||||
QCoreApplication::setOrganizationName("pgadmin");
|
||||
QCoreApplication::setOrganizationDomain("pgadmin.org");
|
||||
QCoreApplication::setApplicationName(PGA_APP_NAME.toLower().replace(" ", ""));
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
// Set high DPI pixmap to display icons clear on Qt widget.
|
||||
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#endif
|
||||
|
||||
// Create a hash of the executable path so we can run copies side-by-side
|
||||
unsigned long exeHash = sdbm(reinterpret_cast<unsigned char *>(argv[0]));
|
||||
|
||||
// Create the address file, that will be used to store the appserver URL for this instance
|
||||
// Make sure we can create logs etc.
|
||||
QDir workdir;
|
||||
workdir.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
|
||||
addrFileName = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + (QString("/.%1.%2.addr").arg(PGA_APP_NAME).arg(exeHash)).remove(" ");
|
||||
QFile addrFile(addrFileName);
|
||||
|
||||
// Create a system-wide semaphore keyed by app name, exe hash and the username
|
||||
// to ensure instances are unique to the user and path
|
||||
QString userName = qgetenv("USER"); // *nix
|
||||
if (userName.isEmpty())
|
||||
userName = qgetenv("USERNAME"); // Windows
|
||||
|
||||
QString semaName = QString("%1-%2-%3-sema").arg(PGA_APP_NAME).arg(userName).arg(exeHash);
|
||||
QString shmemName = QString("%1-%2-%3-shmem").arg(PGA_APP_NAME).arg(userName).arg(exeHash);
|
||||
|
||||
QSystemSemaphore sema(semaName, 1);
|
||||
sema.acquire();
|
||||
|
||||
#ifndef Q_OS_WIN32
|
||||
// We may need to clean up stale shmem segments on *nix. Attaching and detaching
|
||||
// should remove the segment if it is orphaned.
|
||||
QSharedMemory stale_shmem(shmemName);
|
||||
if (stale_shmem.attach())
|
||||
stale_shmem.detach();
|
||||
#endif
|
||||
|
||||
QSharedMemory shmem(shmemName);
|
||||
bool is_running;
|
||||
if (shmem.attach())
|
||||
{
|
||||
is_running = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
shmem.create(1);
|
||||
is_running = false;
|
||||
}
|
||||
sema.release();
|
||||
|
||||
QSettings settings;
|
||||
|
||||
if (is_running){
|
||||
addrFile.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
QTextStream in(&addrFile);
|
||||
QString addr = in.readLine();
|
||||
|
||||
QString cmd = settings.value("BrowserCommand").toString();
|
||||
|
||||
if (!cmd.isEmpty())
|
||||
{
|
||||
cmd.replace("%URL%", addr);
|
||||
QProcess::startDetached(cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!QDesktopServices::openUrl(addr))
|
||||
{
|
||||
QString error(QWidget::tr("Failed to open the system default web browser. Is one installed?."));
|
||||
QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
atexit(cleanup);
|
||||
|
||||
// In windows and linux, it is required to set application level proxy
|
||||
// because socket bind logic to find free port gives socket creation error
|
||||
// when system proxy is configured. We are also setting
|
||||
// "setUseSystemConfiguration"=true to use the system proxy which will
|
||||
// override this application level proxy. As this bug is fixed in Qt 5.9 so
|
||||
// need to set application proxy for Qt version < 5.9.
|
||||
//
|
||||
#if defined (Q_OS_WIN) && QT_VERSION <= 0x050800
|
||||
// Give dummy URL required to find proxy server configured in windows.
|
||||
QNetworkProxyQuery proxyQuery(QUrl("https://www.pgadmin.org"));
|
||||
QNetworkProxy l_proxy;
|
||||
QList<QNetworkProxy> listOfProxies = QNetworkProxyFactory::systemProxyForQuery(proxyQuery);
|
||||
|
||||
if (listOfProxies.size())
|
||||
{
|
||||
l_proxy = listOfProxies[0];
|
||||
|
||||
// If host name is not empty means proxy server is configured.
|
||||
if (!l_proxy.hostName().isEmpty()) {
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (Q_OS_LINUX) && QT_VERSION <= 0x050800
|
||||
QByteArray proxy_env;
|
||||
proxy_env = qgetenv("http_proxy");
|
||||
// If http_proxy environment is defined in linux then proxy server is configured.
|
||||
if (!proxy_env.isEmpty()) {
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Display the spash screen
|
||||
QSplashScreen *splash = new QSplashScreen();
|
||||
splash->setPixmap(QPixmap(":/splash.png"));
|
||||
splash->setWindowFlags(splash->windowFlags() | Qt::WindowStaysOnTopHint);
|
||||
splash->show();
|
||||
app.processEvents(QEventLoop::AllEvents);
|
||||
|
||||
quint16 port = 0L;
|
||||
|
||||
if (settings.value("FixedPort", false).toBool())
|
||||
{
|
||||
// Use the fixed port number
|
||||
port = settings.value("PortNumber", 5050).toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find an unused port number. Essentially, we're just reserving one
|
||||
// here that Flask will use when we start up the server.
|
||||
#if QT_VERSION >= 0x050000
|
||||
QTcpSocket socket;
|
||||
|
||||
#if QT_VERSION >= 0x050900
|
||||
socket.setProxy(QNetworkProxy::NoProxy);
|
||||
#endif
|
||||
|
||||
socket.bind(0, QTcpSocket::ShareAddress);
|
||||
#else
|
||||
QUdpSocket socket;
|
||||
socket.bind(0, QUdpSocket::ShareAddress);
|
||||
#endif
|
||||
port = socket.localPort();
|
||||
}
|
||||
|
||||
// Generate a random key to authenticate the client to the server
|
||||
QString key = QUuid::createUuid().toString();
|
||||
key = key.mid(1, key.length() - 2);
|
||||
|
||||
// Generate the filename for the log
|
||||
logFileName = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + (QString("/.%1.%2.log").arg(PGA_APP_NAME).arg(exeHash)).remove(" ");
|
||||
|
||||
// Create Menu Actions
|
||||
MenuActions *menuActions = new MenuActions();
|
||||
if(menuActions != Q_NULLPTR)
|
||||
menuActions->setLogFile(logFileName);
|
||||
|
||||
splash->showMessage(QString(QWidget::tr("Checking for system tray...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("Checking for system tray...");
|
||||
|
||||
// Check system tray is available or not. If not then create one floating window.
|
||||
FloatingWindow *floatingWindow = Q_NULLPTR;
|
||||
TrayIcon *trayicon = Q_NULLPTR;
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable())
|
||||
{
|
||||
// Start the tray service
|
||||
trayicon = new TrayIcon();
|
||||
|
||||
// Set the MenuActions object to connect to slot
|
||||
if (trayicon != Q_NULLPTR)
|
||||
trayicon->setMenuActions(menuActions);
|
||||
|
||||
trayicon->Init();
|
||||
}
|
||||
else
|
||||
{
|
||||
splash->showMessage(QString(QWidget::tr("System tray not found, creating floating window...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("System tray not found, creating floating window...");
|
||||
// Unable to find tray icon, so creating floting window
|
||||
floatingWindow = new FloatingWindow();
|
||||
if (floatingWindow == Q_NULLPTR)
|
||||
{
|
||||
QString error = QString(QWidget::tr("Unable to initialize either a tray icon or control window."));
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set the MenuActions object to connect to slot
|
||||
floatingWindow->setMenuActions(menuActions);
|
||||
floatingWindow->Init();
|
||||
}
|
||||
|
||||
// Fire up the webserver
|
||||
Server *server;
|
||||
|
||||
bool done = false;
|
||||
|
||||
splash->showMessage(QString(QWidget::tr("Starting pgAdmin4 server...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("Starting pgAdmin4 server...");
|
||||
while (done != true)
|
||||
{
|
||||
QString msg = QString(QWidget::tr("Creating server object, port:%1, key:%2, logfile:%3")).arg(port).arg(key).arg(logFileName);
|
||||
Logger::GetLogger()->Log(msg);
|
||||
server = new Server(port, key, logFileName);
|
||||
|
||||
Logger::GetLogger()->Log("Initializing server...");
|
||||
if (!server->Init())
|
||||
{
|
||||
splash->finish(Q_NULLPTR);
|
||||
|
||||
qDebug() << server->getError();
|
||||
|
||||
QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError());
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Logger::GetLogger()->Log("Server initialized.");
|
||||
Logger::GetLogger()->Log("Starting Server Thread...");
|
||||
server->start();
|
||||
|
||||
// This is a hack to give the server a chance to start and potentially fail. As
|
||||
// the Python interpreter is a synchronous call, we can't check for proper startup
|
||||
// easily in a more robust way - we have to rely on a clean startup not returning.
|
||||
// It should always fail pretty quickly, and take longer to start if it succeeds, so
|
||||
// we don't really get a visible delay here.
|
||||
delay(1000);
|
||||
|
||||
// Any errors?
|
||||
if (server->isFinished() || server->getError().length() > 0)
|
||||
{
|
||||
splash->finish(Q_NULLPTR);
|
||||
|
||||
qDebug() << server->getError();
|
||||
|
||||
QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError());
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
Logger::GetLogger()->Log(error);
|
||||
|
||||
// Allow the user to tweak the Python Path if needed
|
||||
bool ok;
|
||||
|
||||
ConfigWindow *dlg = new ConfigWindow();
|
||||
dlg->LoadSettings();
|
||||
dlg->setModal(true);
|
||||
ok = dlg->exec();
|
||||
|
||||
if (ok)
|
||||
dlg->SaveSettings();
|
||||
else
|
||||
{
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
delete server;
|
||||
}
|
||||
else
|
||||
done = true;
|
||||
}
|
||||
|
||||
// Ensure the server gets cleaned up later
|
||||
QObject::connect(server, SIGNAL(finished()), server, SLOT(deleteLater()));
|
||||
|
||||
// Generate the app server URL
|
||||
QString appServerUrl = QString("http://127.0.0.1:%1/?key=%2").arg(port).arg(key);
|
||||
Logger::GetLogger()->Log(QString(QWidget::tr("Application Server URL: %1")).arg(appServerUrl));
|
||||
|
||||
// Read the server connection timeout from the registry or set the default timeout.
|
||||
int timeout = settings.value("ConnectionTimeout", 90).toInt();
|
||||
|
||||
// Now the server should be up, we'll attempt to connect and get a response.
|
||||
// We'll retry in a loop a few time before aborting if necessary.
|
||||
|
||||
QTime endTime = QTime::currentTime().addSecs(timeout);
|
||||
QTime midTime1 = QTime::currentTime().addSecs(timeout/3);
|
||||
QTime midTime2 = QTime::currentTime().addSecs(timeout*2/3);
|
||||
bool alive = false;
|
||||
|
||||
Logger::GetLogger()->Log("The server should be up, we'll attempt to connect and get a response. Ping the server");
|
||||
while(QTime::currentTime() <= endTime)
|
||||
{
|
||||
alive = pingServer(QUrl(appServerUrl));
|
||||
|
||||
if (alive)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if(QTime::currentTime() >= midTime1)
|
||||
{
|
||||
if(QTime::currentTime() < midTime2) {
|
||||
splash->showMessage(QString(QWidget::tr("Taking longer than usual...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
}
|
||||
else
|
||||
{
|
||||
splash->showMessage(QString(QWidget::tr("Almost there...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
}
|
||||
}
|
||||
|
||||
delay(200);
|
||||
}
|
||||
|
||||
// Attempt to connect one more time in case of a long network timeout while looping
|
||||
Logger::GetLogger()->Log("Attempt to connect one more time in case of a long network timeout while looping");
|
||||
if (!alive && !pingServer(QUrl(appServerUrl)))
|
||||
{
|
||||
splash->finish(Q_NULLPTR);
|
||||
QString error(QWidget::tr("The application server could not be contacted."));
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Stash the URL for any duplicate processes to open
|
||||
if (addrFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
addrFile.setPermissions(QFile::ReadOwner|QFile::WriteOwner);
|
||||
QTextStream out(&addrFile);
|
||||
out << appServerUrl << endl;
|
||||
}
|
||||
|
||||
// Go!
|
||||
menuActions->setAppServerUrl(appServerUrl);
|
||||
|
||||
// Enable the shutdown server menu as server started successfully.
|
||||
if (trayicon != Q_NULLPTR)
|
||||
trayicon->enableShutdownMenu();
|
||||
if (floatingWindow != Q_NULLPTR)
|
||||
floatingWindow->enableShutdownMenu();
|
||||
|
||||
if (settings.value("OpenTabAtStartup", true).toBool())
|
||||
{
|
||||
QString cmd = settings.value("BrowserCommand").toString();
|
||||
|
||||
if (!cmd.isEmpty())
|
||||
{
|
||||
cmd.replace("%URL%", appServerUrl);
|
||||
QProcess::startDetached(cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!QDesktopServices::openUrl(appServerUrl))
|
||||
{
|
||||
QString error(QWidget::tr("Failed to open the system default web browser. Is one installed?."));
|
||||
QMessageBox::critical(Q_NULLPTR, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QObject::connect(menuActions, SIGNAL(shutdownSignal(QUrl)), server, SLOT(shutdown(QUrl)));
|
||||
splash->finish(Q_NULLPTR);
|
||||
|
||||
if (floatingWindow != Q_NULLPTR)
|
||||
floatingWindow->show();
|
||||
|
||||
Logger::GetLogger()->Log("Everything works fine, successfully started pgAdmin4.");
|
||||
Logger::ReleaseLogger();
|
||||
return app.exec();
|
||||
// Let's rock...
|
||||
Runtime *runtime = new Runtime();
|
||||
return runtime->go(argc, argv);
|
||||
}
|
||||
|
||||
|
||||
QString serverRequest(QUrl url, QString path)
|
||||
// Get the filename for the startup log
|
||||
QString getStartupLogFile()
|
||||
{
|
||||
QNetworkAccessManager manager;
|
||||
QEventLoop loop;
|
||||
QNetworkReply *reply;
|
||||
QVariant redirectUrl;
|
||||
if (g_startupLogFile == "")
|
||||
g_startupLogFile = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + ("/pgadmin4.startup.log");
|
||||
|
||||
|
||||
url.setPath(path);
|
||||
QString requestUrl = url.toString();
|
||||
|
||||
do
|
||||
{
|
||||
reply = manager.get(QNetworkRequest(url));
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
|
||||
redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
url = redirectUrl.toUrl();
|
||||
|
||||
if (!redirectUrl.isNull())
|
||||
delete reply;
|
||||
|
||||
} while (!redirectUrl.isNull());
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
qDebug() << "Failed to connect to the server:" << reply->errorString() << "( request URL:" << requestUrl << ").";
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString response = reply->readAll();
|
||||
qDebug() << "Server response:" << response << "( request URL:" << requestUrl << ").";
|
||||
|
||||
return response;
|
||||
return g_startupLogFile;
|
||||
}
|
||||
|
||||
|
||||
// Ping the application server to see if it's alive
|
||||
bool pingServer(QUrl url)
|
||||
// Get the filename for the server log
|
||||
QString getServerLogFile()
|
||||
{
|
||||
return serverRequest(url, "/misc/ping") == "PING";
|
||||
if (g_serverLogFile == "")
|
||||
g_serverLogFile = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + (QString("/pgadmin4.%1.log").arg(getExeHash()));
|
||||
|
||||
return g_serverLogFile;
|
||||
}
|
||||
|
||||
|
||||
// Shutdown the application server
|
||||
bool shutdownServer(QUrl url)
|
||||
// Get the filename for the address file
|
||||
QString getAddressFile()
|
||||
{
|
||||
return serverRequest(url, "/misc/shutdown") == "SHUTDOWN";
|
||||
}
|
||||
|
||||
|
||||
void delay(int milliseconds)
|
||||
{
|
||||
QTime endTime = QTime::currentTime().addMSecs(milliseconds);
|
||||
while(QTime::currentTime() < endTime)
|
||||
{
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
|
||||
}
|
||||
if (g_addressFile == "")
|
||||
g_addressFile = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + (QString("/pgadmin4.%1.addr").arg(getExeHash()));
|
||||
|
||||
return g_addressFile;
|
||||
}
|
||||
|
||||
|
||||
// Cleanup the address and log files
|
||||
void cleanup()
|
||||
{
|
||||
// Remove the address file
|
||||
QFile addrFile(addrFileName);
|
||||
qDebug() << "Removing:" << getAddressFile();
|
||||
QFile addrFile(getAddressFile());
|
||||
addrFile.remove();
|
||||
|
||||
// Remove the log file
|
||||
QFile logFile(logFileName);
|
||||
qDebug() << "Removing:" << getServerLogFile();
|
||||
QFile logFile(getServerLogFile());
|
||||
logFile.remove();
|
||||
}
|
||||
|
||||
|
||||
unsigned long sdbm(unsigned char *str)
|
||||
// Get a hash of the executable name and path
|
||||
QString getExeHash()
|
||||
{
|
||||
unsigned long hash = 0;
|
||||
int c;
|
||||
|
||||
while ((c = *str++))
|
||||
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||
|
||||
return hash;
|
||||
}
|
||||
return QString(QCryptographicHash::hash(QCoreApplication::applicationFilePath().toUtf8(),QCryptographicHash::Md5).toHex());
|
||||
}
|
@ -12,34 +12,17 @@
|
||||
#ifndef PGADMIN4_H
|
||||
#define PGADMIN4_H
|
||||
|
||||
// Include the Python header here as it needs to appear before any QT
|
||||
// headers anywhere in the app.
|
||||
#ifdef __MINGW32__
|
||||
#include <cmath>
|
||||
#endif
|
||||
#include <Python.h>
|
||||
|
||||
// QT headers
|
||||
#include <QtGlobal>
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QtWidgets>
|
||||
#else
|
||||
#include <QApplication>
|
||||
#include <QtGui>
|
||||
#include <Qt/qurl.h>
|
||||
#endif
|
||||
|
||||
// Application name
|
||||
const QString PGA_APP_NAME = QString("pgAdmin 4");
|
||||
#include <QString>
|
||||
|
||||
// Global function prototypes
|
||||
int main(int argc, char * argv[]);
|
||||
QString serverRequest(QUrl url, QString path);
|
||||
bool pingServer(QUrl url);
|
||||
bool shutdownServer(QUrl url);
|
||||
void delay(int milliseconds);
|
||||
|
||||
QString getStartupLogFile();
|
||||
QString getServerLogFile();
|
||||
QString getAddressFile();
|
||||
QString getExeHash();
|
||||
|
||||
void cleanup();
|
||||
unsigned long sdbm(unsigned char *str);
|
||||
|
||||
#endif // PGADMIN4_H
|
||||
|
@ -8,15 +8,15 @@ message(==================================)
|
||||
message(Configuring the pgAdmin 4 runtime.)
|
||||
message(==================================)
|
||||
|
||||
# Configure QT modules for the appropriate version of QT
|
||||
greaterThan(QT_MAJOR_VERSION, 4) {
|
||||
message(Qt version: 5)
|
||||
QT += network widgets
|
||||
} else {
|
||||
message(Qt version: 4)
|
||||
QT += network
|
||||
DEFINES += Q_NULLPTR=NULL
|
||||
# Check for a suitable Qt version
|
||||
!versionAtLeast(QT_VERSION, 5.0.0) {
|
||||
error("pgAdmin 4 cannot be built with Qt $${QT_VERSION}. Use Qt 5.0 or newer.")
|
||||
}
|
||||
message(Qt version: $${QT_VERSION})
|
||||
|
||||
# Configure QT modules for the appropriate version of QT
|
||||
QT += network widgets
|
||||
|
||||
win32 {
|
||||
RC_ICONS += pgAdmin4.ico
|
||||
}
|
||||
@ -94,6 +94,7 @@ else {
|
||||
|
||||
# Source code
|
||||
HEADERS = Server.h \
|
||||
Runtime.h \
|
||||
pgAdmin4.h \
|
||||
ConfigWindow.h \
|
||||
TrayIcon.h \
|
||||
@ -103,6 +104,7 @@ HEADERS = Server.h \
|
||||
Logger.h
|
||||
|
||||
SOURCES = pgAdmin4.cpp \
|
||||
Runtime.cpp \
|
||||
Server.cpp \
|
||||
ConfigWindow.cpp \
|
||||
TrayIcon.cpp \
|
||||
|
Loading…
Reference in New Issue
Block a user