Rewrite the runtime as a tray-based server which can launch a web browser. Fixes #3086

This commit is contained in:
Dave Page
2018-02-05 15:32:14 +00:00
parent 25647c16ba
commit 7192a2b675
37 changed files with 1026 additions and 3814 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,170 +0,0 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// BrowserWindow.h - Declaration of the main window class
//
//////////////////////////////////////////////////////////////////////////
#ifndef BROWSERWINDOW_H
#define BROWSERWINDOW_H
#include "pgAdmin4.h"
#include "TabWindow.h"
#include "WebViewWindow.h"
#if QT_VERSION >= 0x050000
#include <QtWidgets>
#ifdef PGADMIN4_USE_WEBENGINE
#include <QtWebEngineWidgets>
#else
#include <QtWebKitWidgets>
#include <QNetworkCookieJar>
#include <QNetworkAccessManager>
#endif
#else
#include <QMainWindow>
#ifdef PGADMIN4_USE_WEBENGINE
#include <QtWebEngineView>
#else
#include <QWebView>
#include <QNetworkCookieJar>
#include <QNetworkAccessManager>
#endif
#endif
#ifdef PGADMIN4_USE_WEBENGINE
#include <QWebEngineHistory>
#else
#include <QWebHistory>
#endif
QT_BEGIN_NAMESPACE
class QAction;
class QMenu;
QT_END_NAMESPACE
class BrowserWindow : public QMainWindow
{
Q_OBJECT
public:
BrowserWindow(QString url);
~BrowserWindow();
#ifdef _WIN32
void setRegistryMessage(const QString &msg);
#endif
protected:
void closeEvent(QCloseEvent *event);
protected slots:
void urlLinkClicked(const QUrl &);
void tabTitleChanged(const QString &);
#ifdef __APPLE__
#ifdef PGADMIN4_USE_WEBENGINE
void onMacCut();
void onMacCopy();
void onMacPaste();
#endif
#endif
private slots:
void openUrl();
void preferences();
void about();
void setZoomLevel(int zoomFlag);
#ifdef PGADMIN4_USE_WEBENGINE
void downloadRequested(QWebEngineDownloadItem *download);
#endif
void urlLoadingFinished(bool);
public slots:
void download(const QNetworkRequest &request);
void unsupportedContent(QNetworkReply * reply);
void downloadFinished();
void downloadFileProgress(qint64 , qint64 );
void progressCanceled();
void current_dir_path(const QString &dir);
void replyReady();
#ifdef PGADMIN4_USE_WEBENGINE
void createNewTabWindow(QWebEnginePage * &);
void downloadEngineFileProgress(qint64 , qint64 );
void downloadEngineFinished();
#else
void createNewTabWindowKit(QWebPage * &);
#endif
private:
QString m_appServerUrl;
WebViewWindow *m_mainWebView;
QShortcut *openUrlShortcut;
QShortcut *preferencesShortcut;
QShortcut *exitShortcut;
QShortcut *aboutShortcut;
QShortcut *zoomInShortcut;
QShortcut *zoomOutShortcut;
QShortcut *zoomResetShortcut;
QSignalMapper *signalMapper;
QGridLayout *m_tabGridLayout;
QGridLayout *m_mainGridLayout;
DockTabWidget *m_tabWidget;
QWidget *m_pgAdminMainTab;
QWidget *m_addNewTab;
QGridLayout *m_addNewGridLayout;
WebViewWindow *m_addNewWebView;
QHBoxLayout *m_horizontalLayout;
QWidget *m_widget;
QToolButton *m_toolBtnBack;
QToolButton *m_toolBtnForward;
QToolButton *m_toolBtnClose;
QString m_downloadFilename;
int m_downloadStarted;
int m_downloadCancelled;
QFile *m_file;
QProgressDialog *m_progressDialog;
QString m_defaultFilename;
QString m_last_open_folder_path;
QString m_dir;
QNetworkReply *m_reply;
bool is_readyReadSignaled;
qint64 m_readBytes;
#ifdef _WIN32
QString m_regMessage;
#endif
#ifdef PGADMIN4_USE_WEBENGINE
QWebEngineDownloadItem *m_download;
#else
QNetworkCookieJar *m_cookieJar;
QNetworkAccessManager *m_netAccessMan;
#endif
void createActions();
void pause(int seconds = 1);
int findURLTab(const QUrl &name);
void enableDisableToolButtons(WebViewWindow *webViewPtr);
#ifdef _WIN32
QString getRegistryMessage();
#endif
#ifdef __APPLE__
#ifdef PGADMIN4_USE_WEBENGINE
void triggerWebViewWindowEvents(QWebEnginePage::WebAction action);
#endif
#endif
};
#endif // BROWSERWINDOW_H

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>911</width>
<height>688</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QWebView" name="webView">
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QWebView</class>
<extends>QWidget</extends>
<header>QtWebKitWidgets/QWebView</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -34,6 +34,11 @@ void ConfigWindow::on_buttonBox_rejected()
this->close();
}
QString ConfigWindow::getBrowserCommand()
{
return ui->browserCommandLineEdit->text();
}
QString ConfigWindow::getPythonPath()
{
return ui->pythonPathLineEdit->text();
@@ -45,6 +50,11 @@ QString ConfigWindow::getApplicationPath()
}
void ConfigWindow::setBrowserCommand(QString command)
{
ui->browserCommandLineEdit->setText(command);
}
void ConfigWindow::setPythonPath(QString path)
{
ui->pythonPathLineEdit->setText(path);

View File

@@ -26,9 +26,11 @@ public:
explicit ConfigWindow(QWidget *parent = 0);
~ConfigWindow();
QString getBrowserCommand();
QString getPythonPath();
QString getApplicationPath();
void setBrowserCommand(QString command);
void setPythonPath(QString path);
void setApplicationPath(QString path);
@@ -38,7 +40,6 @@ private slots:
private:
Ui::ConfigWindow *ui;
QString m_pythonpath, m_applicationpath;
};
#endif // CONFIGWINDOW_H

View File

@@ -6,49 +6,211 @@
<rect>
<x>0</x>
<y>0</y>
<width>608</width>
<height>118</height>
<width>625</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>625</width>
<height>300</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="pythonPathLabel">
<property name="text">
<string>Python Path</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="pythonPathLineEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="applicationPathLabel">
<property name="text">
<string>Application Path</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="applicationPathLineEdit"/>
</item>
</layout>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Runtime</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>589</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Enter a command line to be used to start the browser. If blank, the system default browser will be used. %URL% will be replaced with the appropriate URL when executing the browser.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<item>
<widget class="QLabel" name="pythonPathLabel_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Browser Command</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="browserCommandLineEdit"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Python</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>The options below are intended for expert users only, and may not behave as expected as they modify fixed search paths and are not alternate values. Modify with care!</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Enter the path to the directory containing pgAdmin.py if desired.</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="pythonPathLabel">
<property name="text">
<string>Python Path</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="pythonPathLineEdit"/>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Enter a PYTHONPATH if desired. Path elements should be semi-colon delimited.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="applicationPathLabel">
<property name="text">
<string>Application Path</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="applicationPathLineEdit"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">

View File

@@ -29,5 +29,7 @@
<string>org.pgadmin.@EXECUTABLE@</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>LSUIElement</key>
<string>1</string>
</dict>
</plist>

88
runtime/LogWindow.cpp Normal file
View File

@@ -0,0 +1,88 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// LogWindow.cpp - Log viewer window
//
//////////////////////////////////////////////////////////////////////////
#include "LogWindow.h"
#include "ui_LogWindow.h"
#include <QTime>
LogWindow::LogWindow(QWidget *parent, QString logFile) :
QDialog(parent),
ui(new Ui::LogWindow),
m_logFile(logFile)
{
ui->setupUi(this);
}
LogWindow::~LogWindow()
{
delete ui;
}
void LogWindow::reload()
{
this->ReadLog();
}
// Read the logfile
void LogWindow::ReadLog()
{
FILE *log;
char *buffer;
long len = 0;
int i, lines = 0;
// Look busy!
QApplication::setOverrideCursor(Qt::WaitCursor);
ui->lblStatus->setText(tr("Loading logfile..."));
this->setDisabled(true);
QCoreApplication::processEvents( QEventLoop::AllEvents, 100 );
ui->textLog->clear();
// Attempt to open the file
log = fopen(m_logFile.toUtf8().data(), "r");
if (log == NULL)
{
ui->textLog->setPlainText(QString(tr("The log file (%1) could not be opened.")).arg(m_logFile));
this->setDisabled(false);
QApplication::restoreOverrideCursor();
return;
}
// Get the file size, and read the data
fseek(log, 0, SEEK_END);
len = ftell(log);
rewind(log);
buffer = (char *)malloc((len + 1) * sizeof(char));
for (i = 0; i < len; i++) {
if (fread(buffer + i, 1, 1, log) > 0)
{
if (buffer[i] == '\n')
lines++;
}
}
buffer[i] = 0;
fclose(log);
ui->textLog->setPlainText(buffer);
// And... relax
ui->lblStatus->setText(QString(tr("Loaded logfile (%1 lines).")).arg(lines));
this->setDisabled(false);
QApplication::restoreOverrideCursor();
}

40
runtime/LogWindow.h Normal file
View File

@@ -0,0 +1,40 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// LogWindow.h - Log viewer window
//
//////////////////////////////////////////////////////////////////////////
#ifndef LOGWINDOW_H
#define LOGWINDOW_H
#include <QDialog>
namespace Ui {
class LogWindow;
}
class LogWindow : public QDialog
{
Q_OBJECT
public:
explicit LogWindow(QWidget *parent = 0, QString logFile = "");
~LogWindow();
void ReadLog();
private slots:
void reload();
private:
Ui::LogWindow *ui;
QString m_logFile;
};
#endif // LOGWINDOW_H

106
runtime/LogWindow.ui Normal file
View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LogWindow</class>
<widget class="QDialog" name="LogWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPlainTextEdit" name="textLog">
<property name="font">
<font>
<family>Courier</family>
</font>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="plainText">
<string notr="true"/>
</property>
<property name="centerOnScroll">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnReload">
<property name="text">
<string>Reload</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblStatus">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClose">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>btnReload</sender>
<signal>clicked()</signal>
<receiver>LogWindow</receiver>
<slot>reload()</slot>
<hints>
<hint type="sourcelabel">
<x>53</x>
<y>471</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>249</y>
</hint>
</hints>
</connection>
<connection>
<sender>btnClose</sender>
<signal>clicked()</signal>
<receiver>LogWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>731</x>
<y>471</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>249</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>reload()</slot>
</slots>
</ui>

View File

@@ -53,11 +53,12 @@ static void add_to_path(QString &python_path, QString path, bool prepend=false)
}
}
Server::Server(quint16 port, QString key)
Server::Server(quint16 port, QString key, QString logFileName)
{
// Appserver port etc
m_port = port;
m_key = key;
m_logFileName = logFileName;
m_wcAppName = NULL;
m_wcPythonHome = NULL;
@@ -206,6 +207,11 @@ Server::Server(quint16 port, QString key)
#endif
#endif
}
// Redirect stderr
PyObject *sys = PyImport_ImportModule("sys");
PyObject *err = PyFile_FromString(m_logFileName.toUtf8().data(), (char *)"w");
PyObject_SetAttrString(sys, "stderr", err);
}
Server::~Server()
@@ -315,3 +321,4 @@ void Server::run()
fclose(cp);
}

View File

@@ -23,26 +23,29 @@ class Server : public QThread
Q_OBJECT
public:
Server(quint16 port, QString key);
Server(quint16 port, QString key, QString logFileName);
~Server();
bool Init();
QString getError() { return m_error; };
QString getError() { return m_error; }
protected:
void run();
private:
void setError(QString error) { m_error = error; };
void setError(QString error) { m_error = error; }
QString m_appfile;
QString m_error;
quint16 m_port;
QString m_key;
QString m_logFileName;
// Application name in UTF-8 for Python
wchar_t *m_wcAppName;
QByteArray PGA_APP_NAME_UTF8;
// PythonHome for Python
wchar_t *m_wcPythonHome;
QByteArray pythonHome_utf8;

View File

@@ -1,713 +0,0 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// TabWindow.cpp - Implementation of the custom tab widget
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin4.h"
// App headers
#include "TabWindow.h"
#ifdef PGADMIN4_USE_WEBENGINE
#include <QWebEngineHistory>
#else
#include <QWebHistory>
#endif
DockTabWidget *DockTabWidget::mainTabWidget = NULL;
DockTabWidget::DockTabWidget(QWidget *parent) :
QTabWidget(parent)
{
floatingWidget = NULL;
floatingEnabled = false;
setParent(parent);
setTabsClosable(false);
setElideMode(Qt::ElideRight);
// set custom tab bar in tab widget to receive events for docking.
setTabBar(new DockTabBar(this));
setDocumentMode(true);
setAcceptDrops(true);
// Get the system colours we need
QPalette palette = QApplication::palette("QPushButton");
QColor activebg = palette.color(QPalette::Button);
QColor activefg = palette.color(QPalette::ButtonText);
QColor inactivebg = palette.color(QPalette::Dark);
QColor inactivefg = palette.color(QPalette::ButtonText);
QColor border = palette.color(QPalette::Mid);
setStyleSheet(
"QTabBar::tab { "
"background-color: " + inactivebg.name() + "; "
"color: " + inactivefg.name() + "; "
"border: 1px solid " + border.name() + "; "
"padding: 1px 0px; "
"margin-left: 0px; "
"margin-top: 1px; "
#ifndef __APPLE__
"width: 15em; "
"height: 1.5em; "
#endif
"} "
"QTabBar::tab:selected { "
"background-color: " + activebg.name() + "; "
"color: " + activefg.name() + "; "
"border-bottom-style: none; "
"} "
"QTabWidget::pane { "
"border: 0; "
"} "
"QTabWidget::tab-bar {"
"alignment: left; "
"}"
);
if (mainTabWidget == NULL)
mainTabWidget = this;
}
DockTabWidget::DockTabWidget(DockTabWidget *other, QWidget *parent) :
QTabWidget(parent)
{
setFloatingBaseWidget(other->floatingBaseWidget());
setFloatingEnabled(other->isFloatingEnabled());
resize(other->size());
// set custom tab bar in tab widget to receive events for docking.
setTabBar(new DockTabBar(this));
setDocumentMode(true);
setAcceptDrops(true);
// Get the system colours we need
QPalette palette = QApplication::palette("QPushButton");
QColor activebg = palette.color(QPalette::Button);
QColor activefg = palette.color(QPalette::ButtonText);
QColor inactivebg = palette.color(QPalette::Dark);
QColor inactivefg = palette.color(QPalette::ButtonText);
QColor border = palette.color(QPalette::Mid);
setStyleSheet(
"QTabBar::tab { "
"background-color: " + inactivebg.name() + "; "
"color: " + inactivefg.name() + "; "
"border: 1px solid " + border.name() + "; "
"padding: 1px 0px; "
"margin-left: 0px; "
"margin-top: 1px; "
#ifndef __APPLE__
"width: 15em; "
"height: 1.5em; "
#else
"font: 11pt; "
"width: 19em; "
"height: 1.5em; "
#endif
"} "
"QTabBar::tab:selected { "
"background-color: " + activebg.name() + "; "
"color: " + activefg.name() + "; "
"border-bottom-style: none; "
"} "
"QTabWidget::pane { "
"border: 0; "
"} "
"QTabWidget::tab-bar {"
"alignment: left; "
"}"
);
}
void DockTabWidget::setFloatingBaseWidget(QWidget *widget)
{
floatingWidget = widget;
if (floatingEnabled && parentWidget() == 0)
setParent(widget);
}
void DockTabWidget::setFloatingEnabled(bool x)
{
floatingEnabled = x;
if (parent() == 0)
{
if (x)
setWindowFlags(Qt::Tool);
else
setWindowFlags(Qt::Window);
}
}
// Slot: go back to page and enable/disable toolbutton
void DockTabWidget::dockGoBackPage()
{
WebViewWindow *webviewPtr = NULL;
QWidget *tab = this->widget(this->currentIndex());
if (tab != NULL)
{
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
foreach( QWidget* widgetPtr, widgetList )
{
if (widgetPtr != NULL)
{
webviewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
if (webviewPtr != NULL)
webviewPtr->back();
}
}
}
}
// Slot: go forward to page and enable/disable toolbutton
void DockTabWidget::dockGoForwardPage()
{
WebViewWindow *webviewPtr = NULL;
QWidget *tab = this->widget(this->currentIndex());
if (tab != NULL)
{
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
foreach( QWidget* widgetPtr, widgetList )
{
if (widgetPtr != NULL)
{
webviewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
if (webviewPtr != NULL)
webviewPtr->forward();
}
}
}
}
// Close the tab and remove the memory of the given index tab
void DockTabWidget::dockClosetabs()
{
int totalTabs = 0;
QToolButton *btn = NULL;
QWidget *tab = NULL;
DockTabWidget *l_tab_widget = NULL;
QObject *senderPtr = QObject::sender();
if (senderPtr != NULL)
{
btn = dynamic_cast<QToolButton*>(senderPtr);
if (btn != NULL)
{
l_tab_widget = dynamic_cast<DockTabWidget*>(btn->parent()->parent());
int current_tab_index = 0;
if (l_tab_widget != NULL)
{
totalTabs = l_tab_widget->count();
for (int loopCount = l_tab_widget->count();loopCount >= 0;loopCount--)
{
QWidget *l_tab = l_tab_widget->tabBar()->tabButton(loopCount, QTabBar::RightSide);
if (l_tab != NULL)
{
QToolButton *nextBtnPtr = dynamic_cast<QToolButton*>(l_tab);
if (nextBtnPtr != NULL && btn != NULL && nextBtnPtr == btn)
current_tab_index = loopCount;
}
}
QList<QWidget*> widgetList = l_tab_widget->tabBar()->findChildren<QWidget*>();
foreach(QWidget* widgetPtr, widgetList)
{
if (widgetPtr != NULL)
{
QToolButton *toolBtnPtr = dynamic_cast<QToolButton*>(widgetPtr);
if (toolBtnPtr != NULL && toolBtnPtr == btn)
{
tab = l_tab_widget->widget(current_tab_index);
break;
}
}
}
}
if (tab != NULL)
tab->deleteLater();
// If user close the last tab then close the parent tab widget also.
if (totalTabs == 1 && l_tab_widget != NULL)
l_tab_widget->deleteLater();
}
}
if (tab != NULL)
{
WebViewWindow *webviewPtr = NULL;
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
foreach (QWidget* widgetPtr, widgetList)
{
if (widgetPtr != NULL)
{
webviewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
if (webviewPtr != NULL)
{
/* Trigger the action for tab window close so unload event will be called and
* resources will be freed properly.
* Trigger 'RequestClose' action from Qt5 onwards. Here we have triggerred the action
* 'ToggleVideoFullscreen + 1' because we do not know from which webkit
* version 'RequestClose' action was added so increment with previous enum value so that
* it will be backward webkit version compatible.
*/
#if QT_VERSION >= 0x050000
#ifndef PGADMIN4_USE_WEBENGINE
webviewPtr->page()->triggerAction(static_cast<QWebPage::WebAction>(QWebPage::ToggleVideoFullscreen + 1));
#endif
#endif
}
}
}
}
// Check if main pgAdmin4 application has only one tab then close tab bar.
// Here - check for count 2 because tab will be deleted later.
DockTabWidget *mainTab = DockTabWidget::getMainTabWidget();
if (mainTab != NULL && l_tab_widget != NULL && l_tab_widget == mainTab && mainTab->count() == 2)
mainTab->tabBar()->setVisible(false);
}
// This function is used to set back/forward/close buttons on new tabbar.
void DockTabWidget::setButtonsNewTabbar(int index)
{
QWidget *m_widget = new QWidget();
QToolButton *m_toolBtnBack = new QToolButton(m_widget);
m_toolBtnBack->setFixedHeight(PGA_BTN_SIZE);
m_toolBtnBack->setFixedWidth(PGA_BTN_SIZE);
m_toolBtnBack->setIcon(QIcon(":/back.png"));
m_toolBtnBack->setToolTip(tr("Go back"));
QToolButton *m_toolBtnForward = new QToolButton(m_widget);
m_toolBtnForward->setFixedHeight(PGA_BTN_SIZE);
m_toolBtnForward->setFixedWidth(PGA_BTN_SIZE);
m_toolBtnForward->setIcon(QIcon(":/forward.png"));
m_toolBtnForward->setToolTip(tr("Go forward"));
QToolButton *m_btnClose = new QToolButton(m_widget);
m_btnClose->setFixedHeight(PGA_BTN_SIZE);
m_btnClose->setFixedWidth(PGA_BTN_SIZE);
m_btnClose->setIcon(QIcon(":/close.png"));
m_btnClose->setToolTip(tr("Close tab"));
QHBoxLayout *m_horizontalLayout = new QHBoxLayout(m_widget);
m_horizontalLayout->setContentsMargins(0,1,0,0);
m_horizontalLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
m_horizontalLayout->setSpacing(1);
m_horizontalLayout->addWidget(m_toolBtnBack);
m_horizontalLayout->addWidget(m_toolBtnForward);
// Register the slot on toolbutton to show the previous history of web
connect(m_toolBtnBack, SIGNAL(clicked()), this, SLOT(dockGoBackPage()));
// Register the slot on toolbutton to show the next history of web
connect(m_toolBtnForward, SIGNAL(clicked()), this, SLOT(dockGoForwardPage()));
// Register the slot on close button , added manually
connect(m_btnClose, SIGNAL(clicked()), SLOT(dockClosetabs()));
// Register the slot on tab index change
connect(this, SIGNAL(currentChanged(int )), this,SLOT(tabIndexChanged(int )));
// Set the back and forward button on tab
this->tabBar()->setTabButton(index, QTabBar::LeftSide, m_widget);
this->tabBar()->setTabButton(index, QTabBar::RightSide, m_btnClose);
// find the webview and hide/show button depending on flag set with web view.
QWidget *tab = this->widget(index);
if (tab != NULL)
{
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
foreach( QWidget* widgetPtr, widgetList )
{
if (widgetPtr != NULL)
{
WebViewWindow *webViewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
if (webViewPtr != NULL)
{
// If user open any file in query tool then "Query -" name will not appear
// but it is still query tool so hide the tool button.
if (!webViewPtr->getBackForwardButtonHidden())
{
m_toolBtnBack->show();
m_toolBtnForward->show();
}
else
{
m_toolBtnBack->hide();
m_toolBtnForward->hide();
}
break;
}
}
}
}
}
// This function is used to move to old tab widget to new tab widget.
void DockTabWidget::moveTab(DockTabWidget *source, int sourceIndex, DockTabWidget *dest, int destIndex)
{
if (source == dest && sourceIndex < destIndex)
destIndex--;
QWidget *widget = source->widget(sourceIndex);
QString text = source->tabText(sourceIndex);
source->removeTab(sourceIndex);
dest->insertTab(destIndex, widget, text);
dest->setCurrentIndex(destIndex);
}
// This function is used to decode actual drop event on tab widget.
void DockTabWidget::decodeTabDropEvent(QDropEvent *event, DockTabWidget **p_tabWidget, int *p_index)
{
DockTabBar *tabBar = qobject_cast<DockTabBar *>(event->source());
if (!tabBar)
{
*p_tabWidget = NULL;
*p_index = 0;
return;
}
QByteArray data = event->mimeData()->data(MIMETYPE_TABINDEX);
QDataStream stream(&data, QIODevice::ReadOnly);
int index;
stream >> index;
*p_tabWidget = tabBar->tabWidget();
*p_index = index;
}
// This function is used to check event is actually drop event or not.
bool DockTabWidget::eventIsTabDrag(QDragEnterEvent *event)
{
return event->mimeData()->hasFormat(MIMETYPE_TABINDEX) && qobject_cast<DockTabBar *>(event->source());
}
// This function is used to delete tab widget when there is no tab inside.
void DockTabWidget::deleteIfEmpty()
{
if (count() == 0)
{
emit willBeAutomaticallyDeleted(this);
deleteLater();
}
}
// This is function is used to create another tab widget from parent window.
DockTabWidget *DockTabWidget::createAnotherTabWidget(QWidget *parent)
{
DockTabWidget *tab_widget = new DockTabWidget(this, parent);
tab_widget->tabBar()->setVisible(true);
return tab_widget;
}
// Check wether tab is insertable or not.
bool DockTabWidget::isInsertable(QWidget *widget)
{
Q_UNUSED(widget)
return true;
}
// Hide the close button of given index displayed on right side of tab
void DockTabWidget::enableDisableToolButton(const int &index)
{
QToolButton *toolBtnPtr = NULL;
WebViewWindow *tmpwebviewPtr = NULL;
WebViewWindow *webviewPtr = NULL;
// Enable/disable the toolbutton based on the history
QWidget *tab1 = this->widget(index);
if (tab1 != NULL)
{
QList<QWidget*> widgetList = tab1->findChildren<QWidget*>();
foreach( QWidget* widgetPtr, widgetList )
{
if (widgetPtr != NULL)
tmpwebviewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
if (tmpwebviewPtr != NULL)
webviewPtr = tmpwebviewPtr;
}
}
QWidget *tab = tabBar()->tabButton(index, QTabBar::LeftSide);
if (tab != NULL)
{
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
foreach( QWidget* widgetPtr, widgetList )
{
if (widgetPtr != NULL)
{
toolBtnPtr = dynamic_cast<QToolButton*>(widgetPtr);
if (webviewPtr != NULL && toolBtnPtr != NULL)
{
if (!QString::compare(toolBtnPtr->toolTip(), tr("Go back"), Qt::CaseInsensitive))
{
if (webviewPtr->page()->history()->canGoBack())
toolBtnPtr->setDisabled(false);
else
toolBtnPtr->setDisabled(true);
}
if (!QString::compare(toolBtnPtr->toolTip(), tr("Go forward"), Qt::CaseInsensitive))
{
if (webviewPtr->page()->history()->canGoForward())
toolBtnPtr->setDisabled(false);
else
toolBtnPtr->setDisabled(true);
}
}
}
}
}
}
// Slot: When the tab index change, hide/show the toolbutton displayed on tab
void DockTabWidget::tabIndexChanged(int index)
{
int tabCount = 0;
WebViewWindow *webViewPtr = NULL;
for (tabCount = 0; tabCount < this->count(); tabCount++)
{
// if main pgAdmin4 application tab then do nothing.
if (!QString::compare(this->tabText(tabCount), tr("pgAdmin 4"), Qt::CaseInsensitive))
continue;
QWidget *tab = this->widget(tabCount);
if (tab != NULL)
{
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
foreach( QWidget* widgetPtr, widgetList )
{
if (widgetPtr != NULL)
{
webViewPtr = dynamic_cast<WebViewWindow*>(widgetPtr);
if (webViewPtr != NULL)
break;
}
}
}
if (tabCount != index)
this->showHideToolButton(tabCount, 0);
else
{
if (!webViewPtr->getBackForwardButtonHidden())
this->showHideToolButton(tabCount, 1);
else
this->showHideToolButton(tabCount, 0);
}
}
// paint the tab text again as index of the tab widget changed.
this->tabBar()->update();
}
// Show and Hide the toolbutton once the tab is deselected depending on the option
// option 0: Hide the toolButton
// option 1: Show the toolButton
void DockTabWidget::showHideToolButton(const int &index, const int &option)
{
QToolButton *toolBtnPtr = NULL;
QWidget *tab = tabBar()->tabButton(index, QTabBar::LeftSide);
if (tab != NULL)
{
QList<QWidget*> widgetList = tab->findChildren<QWidget*>();
foreach( QWidget* widgetPtr, widgetList )
{
if (widgetPtr != NULL)
{
toolBtnPtr = dynamic_cast<QToolButton*>(widgetPtr);
if (toolBtnPtr != NULL)
{
if (!option)
toolBtnPtr->hide();
else
toolBtnPtr->show();
}
}
}
}
}
// Set the tab tool tip text
void DockTabWidget::setTabToolTipText(const int &index, const QString &toolTipString)
{
tabBar()->setTabToolTip(index, toolTipString);
}
// Implementation of custom tab bar for docking window.
DockTabBar::DockTabBar(DockTabWidget *tabWidget, QWidget *parent) :
QTabBar(parent),
tab_widget(tabWidget)
{
isStartingDrag = false;
setAcceptDrops(true);
}
// Insert new tab at specified index.
int DockTabBar::insertionIndexAt(const QPoint &pos)
{
int index = count();
for (int i = 0; i < count(); ++i)
{
QRect rect = tabRect(i);
QRect rect1(rect.x(), rect.y(), rect.width() / 2, rect.height());
QRect rect2(rect.x() + rect1.width(), rect.y(), rect.width() - rect1.width(), rect.height());
if (rect1.contains(pos))
{
index = i;
break;
}
if (rect2.contains(pos))
{
index = i + 1;
break;
}
}
return index;
}
// Mouse press event handler for tab drag.
void DockTabBar::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
dragStartPos = event->pos();
isStartingDrag = true;
}
QTabBar::mousePressEvent(event);
}
// Mouse move event handler for tab drag.
void DockTabBar::mouseMoveEvent(QMouseEvent *event)
{
if (!isStartingDrag)
return;
if ((!event->buttons()) && Qt::LeftButton)
return;
if ((event->pos() - dragStartPos).manhattanLength() < QApplication::startDragDistance())
return;
int index = tabAt(event->pos());
if (index < 0)
return;
// Don't allow to drag the pgAdmin4 main tab.
if (!QString::compare(tab_widget->tabText(index), tr("pgAdmin 4"), Qt::CaseInsensitive))
{
return;
}
// create data
QMimeData *mimeData = new QMimeData;
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << index;
mimeData->setData(MIMETYPE_TABINDEX, data);
// create pixmap
QRect rect = tabRect(index);
QPixmap pixmap(rect.size());
render(&pixmap, QPoint(), QRegion(rect));
// exec drag
QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setPixmap(pixmap);
QPoint offset = dragStartPos - rect.topLeft();
drag->setHotSpot(offset);
Qt::DropAction dropAction = drag->exec(Qt::MoveAction | Qt::IgnoreAction);
if (dropAction != Qt::MoveAction)
{
DockTabWidget *newTabWidget = tab_widget->createAnotherTabWidget();
if (!newTabWidget->isInsertable(tab_widget, index))
{
newTabWidget->deleteLater();
return;
}
DockTabWidget::moveTab(tab_widget, index, newTabWidget, 0);
newTabWidget->setButtonsNewTabbar(0);
newTabWidget->enableDisableToolButton(0);
QRect newGeometry = newTabWidget->geometry();
newGeometry.moveTopLeft(QCursor::pos() - offset);
newTabWidget->setGeometry(newGeometry);
newTabWidget->show();
// Check if main pgAdmin4 application has only one tab then close tab bar.
// Here - check for count 2 because tab will be deleted later.
DockTabWidget *mainTab = DockTabWidget::getMainTabWidget();
if (mainTab != NULL && tab_widget != NULL && tab_widget == mainTab && mainTab->count() == 1)
mainTab->tabBar()->setVisible(false);
}
tab_widget->deleteIfEmpty();
isStartingDrag = false;
}
// Actual tab drag started.
void DockTabBar::dragEnterEvent(QDragEnterEvent *event)
{
if (DockTabWidget::eventIsTabDrag(event))
event->acceptProposedAction();
}
// Drag event leave the actual area.
void DockTabBar::dragLeaveEvent(QDragLeaveEvent * event)
{
Q_UNUSED(event)
}
// Drop event handler for tabbar.
void DockTabBar::dropEvent(QDropEvent *event)
{
DockTabWidget *oldTabWidget = NULL;
int oldIndex;
DockTabWidget::decodeTabDropEvent(event, &oldTabWidget, &oldIndex);
if (oldTabWidget && tab_widget && tab_widget->isInsertable(oldTabWidget, oldIndex))
{
int newIndex = insertionIndexAt(event->pos());
DockTabWidget::moveTab(oldTabWidget, oldIndex, tab_widget, newIndex);
// create new back/forward/close buttons and register its events.
tab_widget->setButtonsNewTabbar(newIndex);
tab_widget->enableDisableToolButton(newIndex);
// Check if main pgAdmin4 application has only one tab then close tab bar.
// Here - check for count 2 because tab will be deleted later.
DockTabWidget *mainTab = DockTabWidget::getMainTabWidget();
if (mainTab != NULL && oldTabWidget != NULL && oldTabWidget == mainTab && mainTab->count() == 1)
mainTab->tabBar()->setVisible(false);
event->acceptProposedAction();
}
}

View File

@@ -1,188 +0,0 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// TabWindow.h - Declaration of the custom tab widget
//
//////////////////////////////////////////////////////////////////////////
#ifndef TABWINDOW_H
#define TABWINDOW_H
#include "pgAdmin4.h"
#include "WebViewWindow.h"
// Define button sizes
#ifdef _WIN32
const int PGA_BTN_SIZE = 18;
#else
const int PGA_BTN_SIZE = 16;
#endif
#include <QTabBar>
#include <QTabWidget>
#define MIMETYPE_TABINDEX "x-paintfield-tabindex"
class DockTabBar;
class DockTabWidget : public QTabWidget
{
Q_OBJECT
friend class DockTabBar;
public:
explicit DockTabWidget(QWidget *parent = 0);
DockTabWidget(DockTabWidget *other, QWidget *parent = 0);
// Drop event handlers of parent tab widget.
static void moveTab(DockTabWidget *source, int sourceIndex, DockTabWidget *dest, int destIndex);
static void decodeTabDropEvent(QDropEvent *event, DockTabWidget **p_tabWidget, int *p_index);
static bool eventIsTabDrag(QDragEnterEvent *event);
void setButtonsNewTabbar(int index);
static DockTabWidget *mainTabWidget;
static DockTabWidget* getMainTabWidget()
{
return mainTabWidget;
}
void setFloatingBaseWidget(QWidget *widget);
QWidget *floatingBaseWidget()
{
return floatingWidget;
}
void setFloatingEnabled(bool x);
bool isFloatingEnabled() const
{
return floatingEnabled;
}
virtual bool isInsertable(QWidget *widget);
bool isInsertable(DockTabWidget *other, int index)
{
return isInsertable(other->widget(index));
}
virtual DockTabWidget *createAnotherTabWidget(QWidget *parent = 0);
int getButtonIndex(QPushButton *btn);
void showHideToolButton(const int &index,const int &option);
void enableDisableToolButton(const int &index);
void setTabToolTipText(const int &index, const QString &toolTipString);
QTabBar *tabBar() const
{
return QTabWidget::tabBar();
}
signals:
void willBeAutomaticallyDeleted(DockTabWidget *widget);
public slots:
void deleteIfEmpty();
void dockClosetabs();
void dockGoBackPage();
void dockGoForwardPage();
void tabIndexChanged(int index);
private:
QWidget *floatingWidget;
bool floatingEnabled;
};
class DockTabBar : public QTabBar
{
Q_OBJECT
public:
DockTabBar(DockTabWidget *tabWidget, QWidget *parent = 0);
// return tab widget of respective tab bar widget.
DockTabWidget *tabWidget()
{
return tab_widget;
}
protected:
// re-implemnted mouse event to detect tab drag started or not.
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
// re-implemnted drag-drop event for docking of tabs.
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
void dragLeaveEvent(QDragLeaveEvent * event);
// re-implemented paint event to draw the text on tab bar of tab widget control.
void paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
bool isToolBtnVisible = false;
DockTabWidget *l_tab_widget = dynamic_cast<DockTabWidget*>(this->parent());
if (l_tab_widget != NULL)
{
int current_index = l_tab_widget->currentIndex();
QStylePainter painter(this);
for(int i = 0; i < l_tab_widget->count(); ++i)
{
QString str = l_tab_widget->tabText(i);
if (!str.startsWith("pgAdmin 4") && !str.startsWith("Query -") && !str.startsWith("Debugger"))
isToolBtnVisible = true;
QStyleOptionTab option;
initStyleOption(&option, i);
QString tempText = this->tabText(i);
if (tempText.length() > 28)
{
tempText = tempText.mid(0,27);
tempText += QString("...");
}
QRect rect(option.rect);
// If toolButton is visible then only draw text after tool button pixel area.
// If tool button is not visible - draw the text after margin of 10px.
if (isToolBtnVisible)
{
if ((current_index != -1) && i == current_index)
{
if (str.startsWith("Query -") || str.startsWith("Debugger"))
rect.setX(option.rect.x() + 10);
else
rect.setX(option.rect.x() + 45);
}
else
rect.setX(option.rect.x() + 10);
}
else
rect.setX(option.rect.x() + 10);
rect.setY(option.rect.y() + 7);
option.text = QString();
painter.drawControl(QStyle::CE_TabBarTab, option);
painter.drawItemText(rect, 0, palette(), 1, tempText);
}
}
}
#ifdef __APPLE__
QSize tabSizeHint(int) const
{
return QSize(250, 26);
}
#endif
private:
int insertionIndexAt(const QPoint &pos);
DockTabWidget *tab_widget;
bool isStartingDrag;
QPoint dragStartPos;
};
#endif // TABWINDOW_H

244
runtime/TrayIcon.cpp Normal file
View File

@@ -0,0 +1,244 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// TrayIcon.cpp - Manages the tray icon
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin4.h"
// QT headers
#include <QMessageBox>
// App headers
#include "ConfigWindow.h"
#include "LogWindow.h"
#include "TrayIcon.h"
TrayIcon::TrayIcon(QString logFile) :
m_logFile(logFile)
{
m_logWindow = NULL;
m_trayIcon = NULL;
m_trayIconMenu = NULL;
m_newAction = NULL;
m_configAction = NULL;
m_logAction = NULL;
m_quitAction = NULL;
}
TrayIcon::~TrayIcon()
{
}
bool TrayIcon::Init()
{
if (! isSystemTrayAvailable())
return false;
createTrayIcon();
if (m_trayIcon)
m_trayIcon->show();
return true;
}
void TrayIcon::setAppServerUrl(QString appServerUrl)
{
m_appServerUrl = appServerUrl;
}
// Check whether system tray exists
bool TrayIcon::isSystemTrayAvailable()
{
int timeout = 10; // 30 sec * 10 = 5 minutes, thus we timeout after 5 minutes
int iteration = 0;
bool trayFound = false;
while (iteration < timeout)
{
// Check we can find the system tray.
if (!QSystemTrayIcon::isSystemTrayAvailable())
{
// Wait for 30 seconds.
wait(3000);
trayFound = false;
}
else
{
trayFound = true;
break;
}
iteration++;
}
return trayFound;
}
// Make application wait for msec milliseconds
void TrayIcon::wait(int msec)
{
QMutex mutex;
QWaitCondition wc;
mutex.lock();
wc.wait(&mutex, msec);
mutex.unlock();
}
// Create the tray icon
void TrayIcon::createTrayIcon()
{
createActions();
if (m_trayIconMenu)
{
delete m_trayIconMenu;
m_trayIconMenu = NULL;
}
m_trayIconMenu = new QMenu(this);
m_trayIconMenu->addAction(m_newAction);
m_trayIconMenu->addSeparator();
m_trayIconMenu->addAction(m_configAction);
m_trayIconMenu->addAction(m_logAction);
m_trayIconMenu->addSeparator();
m_trayIconMenu->addAction(m_quitAction);
if (!m_trayIcon)
m_trayIcon = new QSystemTrayIcon(this);
m_trayIcon->setContextMenu(m_trayIconMenu);
// Setup the icon itself. For convenience, we'll also use it for the dialogue.
#ifdef Q_OS_MAC
QIcon icon(":pgAdmin4-mac.png");
#else
QIcon icon(":pgAdmin4.png");
#endif
m_trayIcon->setIcon(icon);
setWindowIcon(icon);
}
// Create the menu actions
void TrayIcon::createActions()
{
m_newAction = new QAction(QString(tr("&New %1 window...")).arg(PGA_APP_NAME), this);
connect(m_newAction, SIGNAL(triggered()), this, SLOT(onNew()));
m_configAction = new QAction(tr("&Configure..."), this);
connect(m_configAction, SIGNAL(triggered()), this, SLOT(onConfig()));
m_logAction = new QAction(tr("&View log..."), this);
connect(m_logAction, SIGNAL(triggered()), this, SLOT(onLog()));
m_quitAction = new QAction(tr("&Shutdown server"), this);
connect(m_quitAction, SIGNAL(triggered()), this, SLOT(onQuit()));
}
// Create a new application browser window on user request
void TrayIcon::onNew()
{
QSettings settings;
QString cmd = settings.value("BrowserCommand").toString();
if (!cmd.isEmpty())
{
cmd.replace("%URL%", m_appServerUrl);
QProcess::startDetached(cmd);
}
else
{
if (!QDesktopServices::openUrl(m_appServerUrl))
{
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);
}
}
}
// Show the config dialogue
void TrayIcon::onConfig()
{
QSettings settings;
bool ok;
ConfigWindow *dlg = new ConfigWindow();
dlg->setWindowTitle(QString(tr("%1 Configuration")).arg(PGA_APP_NAME));
dlg->setBrowserCommand(settings.value("BrowserCommand").toString());
dlg->setPythonPath(settings.value("PythonPath").toString());
dlg->setApplicationPath(settings.value("ApplicationPath").toString());
dlg->setModal(true);
ok = dlg->exec();
QString browsercommand = dlg->getBrowserCommand();
QString pythonpath = dlg->getPythonPath();
QString applicationpath = dlg->getApplicationPath();
if (ok)
{
bool needRestart = (settings.value("PythonPath").toString() != pythonpath ||
settings.value("ApplicationPath").toString() != applicationpath);
settings.setValue("BrowserCommand", browsercommand);
settings.setValue("PythonPath", pythonpath);
settings.setValue("ApplicationPath", applicationpath);
if (needRestart)
{
if (QMessageBox::Yes == QMessageBox::question(this, tr("Shutdown server?"), QString(tr("The %1 server must be restarted for changes to take effect. Do you want to shutdown the server now?")).arg(PGA_APP_NAME), QMessageBox::Yes | QMessageBox::No))
{
exit(0);
}
}
}
}
// Show the log window
void TrayIcon::onLog()
{
QSettings settings;
if (!m_logWindow)
{
m_logWindow = new LogWindow(NULL, m_logFile);
m_logWindow->setWindowTitle(QString(tr("%1 Log")).arg(PGA_APP_NAME));
}
m_logWindow->show();
m_logWindow->raise();
m_logWindow->activateWindow();
QCoreApplication::processEvents( QEventLoop::AllEvents, 100 );
m_logWindow->ReadLog();
}
// Exit
void TrayIcon::onQuit()
{
if (QMessageBox::Yes == QMessageBox::question(this, tr("Shutdown server?"), QString(tr("Are you sure you want to shutdown the %1 server?")).arg(PGA_APP_NAME), QMessageBox::Yes | QMessageBox::No))
{
exit(0);
}
}

61
runtime/TrayIcon.h Normal file
View File

@@ -0,0 +1,61 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// TrayIcon.h - Manages the tray icon
//
//////////////////////////////////////////////////////////////////////////
#ifndef TRAYICON_H
#define TRAYICON_H
#include "pgAdmin4.h"
// QT headers
#include <QWidget>
#include <QMessageBox>
// App headers
#include "LogWindow.h"
class TrayIcon : public QWidget
{
Q_OBJECT
public:
TrayIcon(QString logFile);
~TrayIcon();
bool Init();
void setAppServerUrl(QString appServerUrl);
private:
void createTrayIcon();
bool isSystemTrayAvailable();
void createActions();
void wait(int msec);
QAction *m_newAction;
QAction *m_configAction;
QAction *m_logAction;
QAction *m_quitAction;
QSystemTrayIcon *m_trayIcon;
QMenu *m_trayIconMenu;
QString m_appServerUrl, m_logFile;
LogWindow *m_logWindow;
private slots:
void onNew();
void onConfig();
void onLog();
void onQuit();
};
#endif // TRAYICON_H

View File

@@ -1,195 +0,0 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// WebViewWindow.cpp - Implementation of the custom web view widget
//
//////////////////////////////////////////////////////////////////////////
#include "pgAdmin4.h"
// App headers
#include "WebViewWindow.h"
#include "TabWindow.h"
#ifndef PGADMIN4_USE_WEBENGINE
#include <QWebPage>
#include <QNetworkRequest>
#endif
WebViewWindow *WebViewWindow::mainWebViewWindow = NULL;
// Override QWebEnginePage to handle link delegation
#ifdef PGADMIN4_USE_WEBENGINE
bool WebEnginePage::acceptNavigationRequest(const QUrl & url, NavigationType type, bool isMainFrame)
{
Q_UNUSED(type);
Q_UNUSED(url);
Q_UNUSED(isMainFrame);
return true;
}
QWebEnginePage *WebEnginePage::createWindow(QWebEnginePage::WebWindowType type)
{
if (type == QWebEnginePage::WebBrowserTab)
{
QWebEnginePage *_page = NULL;
emit createTabWindow(_page);
return _page;
}
return NULL;
}
#endif
WebViewWindow::WebViewWindow(QWidget *parent) :
#ifdef PGADMIN4_USE_WEBENGINE
QWebEngineView(parent)
#else
QWebView(parent)
#endif
{
m_url = QString("");
m_tabIndex = 0;
m_backForwardBtnHide = false;
// Accept drop event for only main pgAdmin4 application window.
if (mainWebViewWindow == NULL)
mainWebViewWindow = this;
setAcceptDrops(true);
}
// Actual tab drag started.
void WebViewWindow::dragEnterEvent(QDragEnterEvent *event)
{
//DockTabWidget *mainTabWidget = DockTabWidget::getMainTabWidget();
//if (this->parent()->parent()->parent() == mainTabWidget)
event->accept();
}
void WebViewWindow::dragMoveEvent(QDragMoveEvent *event)
{
event->acceptProposedAction();
}
// Drop event handler for tabbar.
void WebViewWindow::dropEvent(QDropEvent *event)
{
DockTabWidget *oldTabWidget;
int oldIndex;
DockTabWidget::decodeTabDropEvent(event, &oldTabWidget, &oldIndex);
//DockTabWidget *mainTabWidget = DockTabWidget::getMainTabWidget();
DockTabWidget *mainTabWidget = dynamic_cast<DockTabWidget*>(this->parent()->parent()->parent());
if (oldTabWidget && mainTabWidget && oldTabWidget != mainTabWidget)
//if (oldTabWidget && mainTabWidget)
{
mainTabWidget->tabBar()->setVisible(true);
QPoint pos = event->pos();
int index = mainTabWidget->tabBar()->count();
for (int i = 0; i < mainTabWidget->tabBar()->count(); ++i)
{
QRect rect = mainTabWidget->tabBar()->tabRect(i);
QRect rect1(rect.x(), rect.y(), rect.width() / 2, rect.height());
QRect rect2(rect.x() + rect1.width(), rect.y(), rect.width() - rect1.width(), rect.height());
if (rect1.contains(pos))
{
index = i;
break;
}
if (rect2.contains(pos))
{
index = i + 1;
break;
}
}
DockTabWidget::moveTab(oldTabWidget, oldIndex, mainTabWidget, index);
// create new back/forward/close buttons and register its events.
mainTabWidget->setButtonsNewTabbar(index);
mainTabWidget->enableDisableToolButton(index);
// Check if main pgAdmin4 application has only one tab then close tab bar.
// Here - check for count 2 because tab will be deleted later.
DockTabWidget *mainTab = DockTabWidget::getMainTabWidget();
if (mainTab != NULL && oldTabWidget != NULL && oldTabWidget == mainTab && mainTab->count() == 1)
mainTab->tabBar()->setVisible(false);
event->acceptProposedAction();
}
}
void WebViewWindow::setBackForwardButtonHidden(const bool hideButton)
{
m_backForwardBtnHide = hideButton;
}
bool WebViewWindow::getBackForwardButtonHidden() const
{
return m_backForwardBtnHide;
}
void WebViewWindow::setFirstLoadURL(const QString &url)
{
m_url = url;
}
QString WebViewWindow::getFirstLoadURL() const
{
return m_url;
}
void WebViewWindow::setTabIndex(const int &tabIndex)
{
m_tabIndex = tabIndex;
}
int WebViewWindow::getTabIndex() const
{
return m_tabIndex;
}
#ifndef PGADMIN4_USE_WEBENGINE
WebViewPage::WebViewPage(QObject *parent)
: QWebPage(parent)
{
}
bool WebViewPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type)
{
Q_UNUSED(type);
Q_UNUSED(request);
Q_UNUSED(frame);
return true;
}
QWebPage *WebViewPage::createWindow(QWebPage::WebWindowType type)
{
if (type == QWebPage::WebBrowserWindow)
{
QWebPage *_page = NULL;
emit createTabWindowKit(_page);
return _page;
}
return NULL;
}
bool WebViewPage::javaScriptConfirm(QWebFrame * frame, const QString & msg)
{
// If required, override the QDialog to give custom confirmation message to user.
Q_UNUSED(frame);
Q_UNUSED(msg);
return false;
}
WebViewPage::~WebViewPage()
{
}
#endif

View File

@@ -1,95 +0,0 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// WebViewWindow.h - Declaration of the custom web view widget
//
//////////////////////////////////////////////////////////////////////////
#ifndef WEBVIEWWINDOW_H
#define WEBVIEWWINDOW_H
#include "pgAdmin4.h"
#if QT_VERSION >= 0x050000
#ifdef PGADMIN4_USE_WEBENGINE
#include <QtWebEngineWidgets>
#else
#include <QtWebKitWidgets>
#endif
#else
#include <QWebView>
#endif
// Override QWebEnginePage to handle link delegation
#ifdef PGADMIN4_USE_WEBENGINE
class WebEnginePage : public QWebEnginePage
{
Q_OBJECT
protected:
virtual bool acceptNavigationRequest(const QUrl & url, NavigationType type, bool isMainFrame);
QWebEnginePage *createWindow(QWebEnginePage::WebWindowType type);
signals:
void createTabWindow(QWebEnginePage * &);
};
#endif
#ifdef PGADMIN4_USE_WEBENGINE
class WebViewWindow : public QWebEngineView
#else
class WebViewWindow : public QWebView
#endif
{
Q_OBJECT
public:
WebViewWindow(QWidget *parent = NULL);
void setFirstLoadURL(const QString &url);
QString getFirstLoadURL() const;
void setTabIndex(const int &tabIndex);
int getTabIndex() const;
void setBackForwardButtonHidden(const bool hideButton);
bool getBackForwardButtonHidden() const;
// Store main webview window of pgAdmin4 application.
static WebViewWindow *mainWebViewWindow;
static WebViewWindow* getMainWebViewWindow()
{
return mainWebViewWindow;
}
protected:
// re-implemnted drag-drop event for docking of tabs.
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
private:
QString m_url;
int m_tabIndex;
bool m_backForwardBtnHide;
};
#ifndef PGADMIN4_USE_WEBENGINE
class WebViewPage : public QWebPage
{
Q_OBJECT
public:
WebViewPage(QObject *parent = 0);
~WebViewPage();
protected:
virtual bool acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type);
QWebPage *createWindow(QWebPage::WebWindowType type);
bool javaScriptConfirm(QWebFrame * frame, const QString & msg);
signals:
void createTabWindowKit(QWebPage * &);
};
#endif
#endif // WEBVIEWWINDOW_H

BIN
runtime/pgAdmin4-mac.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -28,153 +28,22 @@
#include <QSplashScreen>
#include <QUuid>
#include <QNetworkProxyFactory>
#include <QSslConfiguration>
#endif
// App headers
#include "BrowserWindow.h"
#include "ConfigWindow.h"
#include "Server.h"
#include "TrayIcon.h"
#include <QTime>
// Implement support for system proxies for Qt 4.x on Linux
#if defined (Q_OS_LINUX) && QT_VERSION < 0x050000
#include "qnetworkproxy.h"
#include <QtCore/QByteArray>
#include <QtCore/QUrl>
#ifndef QT_NO_NETWORKPROXY
QT_BEGIN_NAMESPACE
static bool ignoreProxyFor(const QNetworkProxyQuery &query)
{
const QByteArray noProxy = qgetenv("no_proxy").trimmed();
if (noProxy.isEmpty())
return false;
const QList<QByteArray> noProxyTokens = noProxy.split(',');
foreach (const QByteArray &rawToken, noProxyTokens) {
QByteArray token = rawToken.trimmed();
QString peerHostName = query.peerHostName();
// Since we use suffix matching, "*" is our 'default' behaviour
if (token.startsWith("*"))
token = token.mid(1);
// Harmonize trailing dot notation
if (token.endsWith('.') && !peerHostName.endsWith('.'))
token = token.left(token.length()-1);
// We prepend a dot to both values, so that when we do a suffix match,
// we don't match "donotmatch.com" with "match.com"
if (!token.startsWith('.'))
token.prepend('.');
if (!peerHostName.startsWith('.'))
peerHostName.prepend('.');
if (peerHostName.endsWith(QString::fromLatin1(token)))
return true;
}
return false;
}
static QList<QNetworkProxy> pgAdminSystemProxyForQuery(const QNetworkProxyQuery &query)
{
QList<QNetworkProxy> proxyList;
if (ignoreProxyFor(query))
return proxyList << QNetworkProxy::NoProxy;
// No need to care about casing here, QUrl lowercases values already
const QString queryProtocol = query.protocolTag();
QByteArray proxy_env;
if (queryProtocol == QLatin1String("http"))
proxy_env = qgetenv("http_proxy");
else if (queryProtocol == QLatin1String("https"))
proxy_env = qgetenv("https_proxy");
else if (queryProtocol == QLatin1String("ftp"))
proxy_env = qgetenv("ftp_proxy");
else
proxy_env = qgetenv("all_proxy");
// Fallback to http_proxy is no protocol specific proxy was found
if (proxy_env.isEmpty())
proxy_env = qgetenv("http_proxy");
if (!proxy_env.isEmpty())
{
QUrl url = QUrl(QString::fromLocal8Bit(proxy_env));
if (url.scheme() == QLatin1String("socks5"))
{
QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, url.host(),
url.port() ? url.port() : 1080, url.userName(), url.password());
proxyList << proxy;
} else if (url.scheme() == QLatin1String("socks5h"))
{
QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, url.host(),
url.port() ? url.port() : 1080, url.userName(), url.password());
proxy.setCapabilities(QNetworkProxy::HostNameLookupCapability);
proxyList << proxy;
} else if ((url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https") || url.scheme().isEmpty())
&& query.queryType() != QNetworkProxyQuery::UdpSocket
&& query.queryType() != QNetworkProxyQuery::TcpServer)
{
QNetworkProxy proxy(QNetworkProxy::HttpProxy, url.host(),
url.port() ? url.port() : 8080, url.userName(), url.password());
proxyList << proxy;
}
}
if (proxyList.isEmpty())
proxyList << QNetworkProxy::NoProxy;
return proxyList;
}
class pgAdminSystemConfigurationProxyFactory : public QNetworkProxyFactory
{
public:
pgAdminSystemConfigurationProxyFactory() : QNetworkProxyFactory() {}
virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery& query)
{
QList<QNetworkProxy> proxies = pgAdminSystemProxyForQuery(query);
// Make sure NoProxy is in the list, so that QTcpServer can work:
// it searches for the first proxy that can has the ListeningCapability capability
// if none have (as is the case with HTTP proxies), it fails to bind.
// NoProxy allows it to fallback to the 'no proxy' case and bind.
proxies.append(QNetworkProxy::NoProxy);
return proxies;
}
};
QT_END_NAMESPACE
#endif // QT_NO_NETWORKINTERFACE
#endif
void delay( int milliseconds )
{
QTime endTime = QTime::currentTime().addMSecs( milliseconds );
while( QTime::currentTime() < endTime )
{
QCoreApplication::processEvents( QEventLoop::AllEvents, 100 );
}
}
QString logFileName;
QString addrFileName;
int main(int argc, char * argv[])
{
QSettings settings;
/*
* Before starting main application, need to set 'QT_X11_NO_MITSHM=1'
* to make the runtime work with IBM PPC machine.
@@ -186,6 +55,7 @@ int main(int argc, char * argv[])
// Create the QT application
QApplication app(argc, argv);
app.setQuitOnLastWindowClosed(false);
// Setup the settings management
QCoreApplication::setOrganizationName("pgadmin");
@@ -197,36 +67,84 @@ int main(int argc, char * argv[])
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
#ifdef _WIN32
// Set registry "HKEY_CLASSES_ROOT\.css\Content Type" to value "text/css" to avoid rendering issue in windows OS.
QString infoMsgStr("");
QSettings css_keys("HKEY_CLASSES_ROOT", QSettings::NativeFormat);
// Create a hash of the executable path so we can run copies side-by-side
QString homeDir = QDir::homePath();
unsigned long exeHash = sdbm((unsigned char *)argv[0]);
// If key already exists then check for existing value and it differs then only change it.
if (css_keys.childGroups().contains(".css", Qt::CaseInsensitive))
{
QSettings set("HKEY_CLASSES_ROOT\\.css", QSettings::NativeFormat);
if (set.value("Content Type").toString() != "text/css")
{
set.setValue("Content Type", "text/css");
// If error while setting registry then it should be issue with permissions.
if (set.status() == QSettings::NoError)
infoMsgStr = "pgAdmin 4 application has reset the registry key 'HKEY_CLASSES_ROOT\\css\\Content Type' to 'text/css' to fix a system misconfiguration that can lead to rendering problems.";
else
infoMsgStr = "Failed to reset the registry key 'HKEY_CLASSES_ROOT\\css\\Content Type' to 'text/css'. Try to run with Administrator privileges.";
}
}
// Create the address file, that will be used to store the appserver URL for this instance
addrFileName = homeDir + QString("/.%1.%2.addr").arg(PGA_APP_NAME).arg(exeHash);
addrFileName.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
/* In windows and linux, it is required to set application level proxy
* becuase 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.
*/
#ifndef PGADMIN4_USE_WEBENGINE
#if defined (Q_OS_WIN) && QT_VERSION <= 0x050800
QSharedMemory shmem(shmemName);
bool is_running;
if (shmem.attach())
{
is_running = true;
}
else
{
shmem.create(1);
is_running = false;
}
sema.release();
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;
@@ -241,18 +159,15 @@ int main(int argc, char * argv[])
QNetworkProxy::setApplicationProxy(QNetworkProxy());
}
}
#endif
#endif
#ifndef PGADMIN4_USE_WEBENGINE
#if defined (Q_OS_LINUX) && QT_VERSION <= 0x050800
#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
#endif
// Display the spash screen
@@ -288,19 +203,20 @@ int main(int argc, char * argv[])
QString key = QUuid::createUuid().toString();
key = key.mid(1, key.length() - 2);
#if defined (Q_OS_LINUX) && QT_VERSION < 0x050000
QNetworkProxyFactory::setApplicationProxyFactory(new pgAdminSystemConfigurationProxyFactory);
QSslConfiguration sslCfg = QSslConfiguration::defaultConfiguration();
QList<QSslCertificate> ca_list = sslCfg.caCertificates();
QList<QSslCertificate> ca_new = QSslCertificate::fromData("CaCertificates");
ca_list += ca_new;
// Generate the filename for the log
logFileName = homeDir + QString("/.%1.%2.log").arg(PGA_APP_NAME).arg(exeHash);
logFileName.remove(" ");
sslCfg.setCaCertificates(ca_list);
sslCfg.setProtocol(QSsl::AnyProtocol);
QSslConfiguration::setDefaultConfiguration(sslCfg);
#else
QNetworkProxyFactory::setUseSystemConfiguration(true);
#endif
// Start the tray service
TrayIcon *trayicon = new TrayIcon(logFileName);
if (!trayicon->Init())
{
QString error = QString(QWidget::tr("An error occurred initialising the tray icon"));
QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);
exit(1);
}
// Fire up the webserver
Server *server;
@@ -309,7 +225,7 @@ int main(int argc, char * argv[])
while (done != true)
{
server = new Server(port, key);
server = new Server(port, key, logFileName);
if (!server->Init())
{
@@ -325,6 +241,13 @@ int main(int argc, char * argv[])
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)
{
@@ -341,16 +264,19 @@ int main(int argc, char * argv[])
ConfigWindow *dlg = new ConfigWindow();
dlg->setWindowTitle(QWidget::tr("Configuration"));
dlg->setBrowserCommand(settings.value("BrowserCommand").toString());
dlg->setPythonPath(settings.value("PythonPath").toString());
dlg->setApplicationPath(settings.value("ApplicationPath").toString());
dlg->setModal(true);
ok = dlg->exec();
QString browsercommand = dlg->getBrowserCommand();
QString pythonpath = dlg->getPythonPath();
QString applicationpath = dlg->getApplicationPath();
if (ok)
{
settings.setValue("BrowserCommand", browsercommand);
settings.setValue("PythonPath", pythonpath);
settings.setValue("ApplicationPath", applicationpath);
settings.sync();
@@ -371,7 +297,6 @@ int main(int argc, char * argv[])
QString appServerUrl = QString("http://127.0.0.1:%1/?key=%2").arg(port).arg(key);
// Read the server connection timeout from the registry or set the default timeout.
QSettings settings;
int timeout = settings.value("ConnectionTimeout", 30).toInt();
// Now the server should be up, we'll attempt to connect and get a response.
@@ -393,7 +318,7 @@ int main(int argc, char * argv[])
}
// Attempt to connect one more time in case of a long network timeout while looping
if(!alive && !PingServer(QUrl(appServerUrl)))
if (!alive && !PingServer(QUrl(appServerUrl)))
{
splash->finish(NULL);
QString error(QWidget::tr("The application server could not be contacted."));
@@ -402,26 +327,35 @@ int main(int argc, char * argv[])
exit(1);
}
// Create & show the main window
BrowserWindow browserWindow(appServerUrl);
browserWindow.setWindowTitle(PGA_APP_NAME);
browserWindow.setWindowIcon(QIcon(":/pgAdmin4.ico"));
#ifdef _WIN32
browserWindow.setRegistryMessage(infoMsgStr);
#endif
browserWindow.show();
// Stash the URL for any duplicate processes to open
if (addrFile.open(QIODevice::WriteOnly))
{
QTextStream out(&addrFile);
out << appServerUrl << endl;
}
// Go!
splash->finish(NULL);
trayicon->setAppServerUrl(appServerUrl);
// Set global application stylesheet.
QFile file(":/qss/pgadmin4.qss");
if(file.open(QFile::ReadOnly))
QString cmd = settings.value("BrowserCommand").toString();
if (!cmd.isEmpty())
{
QString StyleSheet = QLatin1String(file.readAll());
qApp->setStyleSheet(StyleSheet);
file.close();
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(NULL, QString(QWidget::tr("Fatal Error")), error);
exit(1);
}
}
splash->finish(NULL);
return app.exec();
}
@@ -468,3 +402,36 @@ bool PingServer(QUrl url)
return true;
}
void delay(int milliseconds)
{
QTime endTime = QTime::currentTime().addMSecs(milliseconds);
while(QTime::currentTime() < endTime)
{
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
}
void cleanup()
{
// Remove the address file
QFile addrFile(addrFileName);
addrFile.remove();
// Remove the log file
QFile logFile(logFileName);
logFile.remove();
}
unsigned long sdbm(unsigned char *str)
{
unsigned long hash = 0;
int c;
while ((c = *str++))
hash = c + (hash << 6) + (hash << 16) - hash;
return hash;
}

View File

@@ -36,5 +36,8 @@ const QString PGA_APP_NAME = QString("pgAdmin 4");
// Global function prototypes
int main(int argc, char * argv[]);
bool PingServer(QUrl url);
void delay(int milliseconds);
void cleanup();
unsigned long sdbm(unsigned char *str);
#endif // PGADMIN4_H

BIN
runtime/pgAdmin4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -1,4 +1,4 @@
VERSION = 2.1.0.0
VERSION = 3.0.0.0
QMAKE_TARGET_COMPANY = "The pgAdmin Development Team"
QMAKE_TARGET_PRODUCT = "pgAdmin 4"
QMAKE_TARGET_DESCRIPTION = "pgAdmin 4 Desktop Runtime"
@@ -7,52 +7,17 @@ QMAKE_TARGET_COPYRIGHT = "Copyright 2013 - 2018, The pgAdmin Development Team"
# Configure QT modules for the appropriate version of QT
greaterThan(QT_MAJOR_VERSION, 4) {
message(Building for QT5+...)
# Users can force the use of WebKit in Qt5, e.g. qmake "DEFINES += PGADMIN4_USE_WEBKIT"
contains(DEFINES, PGADMIN4_USE_WEBKIT) {
message(Forcing use of QWebKit...)
message()
message(************************************** WARNING **************************************)
message(* It is strongly advised that Qt 5.5.0 or later is used to build the pgAdmin runtime.)
message(*************************************************************************************)
message()
QT += webkitwidgets network widgets
} else {
greaterThan(QT_MINOR_VERSION, 4) {
message(Using QWebEngine...)
DEFINES += PGADMIN4_USE_WEBENGINE
QT += webenginewidgets network widgets
} else {
message(Using QWebKit...)
message()
message(************************************** WARNING **************************************)
message(* It is strongly advised that Qt 5.5.0 or later is used to build the pgAdmin runtime.)
message(*************************************************************************************)
message()
DEFINES *= PGADMIN4_USE_WEBKIT
QT += webkitwidgets network widgets
}
}
message()
QT += network widgets
} else {
message(Building for QT4...)
message(Using QWebKit...)
message()
message(************************************** WARNING **************************************)
message(* It is strongly advised that Qt 5.5.0 or later is used to build the pgAdmin runtime.)
message(*************************************************************************************)
message()
DEFINES += PGADMIN4_USE_WEBKIT
QT += webkit network
QT += network
}
win32 {
RC_ICONS += pgAdmin4.ico
}
CONFIG(debug, debug|release) {
DEFINES += PGADMIN4_DEBUG
message(Configure pgAdmin4 to run in debug mode...)
}
# Environment settings for the build
QMAKE_CFLAGS += $$(PGADMIN_CFLAGS)
QMAKE_CXXFLAGS += $$(PGADMIN_CXXFLAGS)
@@ -122,23 +87,21 @@ else {
}
# Source code
HEADERS = BrowserWindow.h \
HEADERS = \
Server.h \
pgAdmin4.h \
TabWindow.h \
WebViewWindow.h \
ConfigWindow.h
ConfigWindow.h \
TrayIcon.h \
LogWindow.h
SOURCES = pgAdmin4.cpp \
BrowserWindow.cpp \
Server.cpp \
TabWindow.cpp \
WebViewWindow.cpp \
ConfigWindow.cpp
FORMS = BrowserWindow.ui \
ConfigWindow.ui
ConfigWindow.cpp \
TrayIcon.cpp \
LogWindow.cpp
FORMS = ConfigWindow.ui \
LogWindow.ui
ICON = pgAdmin4.icns
QMAKE_INFO_PLIST = Info.plist
RESOURCES += \
pgadmin4.qrc
RESOURCES += pgadmin4.qrc

View File

@@ -5,6 +5,7 @@
<file>forward.png</file>
<file>close.png</file>
<file>splash.png</file>
<file>qss/pgadmin4.qss</file>
<file>pgAdmin4.png</file>
<file>pgAdmin4-mac.png</file>
</qresource>
</RCC>

View File

@@ -1,3 +0,0 @@
QTabBar::tab {
background-color: #E8E8E8;
}