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

2
.gitignore vendored
View File

@ -28,7 +28,7 @@ runtime/.qmake.stash
runtime/Makefile
runtime/Makefile.Debug
runtime/Makefile.Release
runtime/moc_BrowserWindow.cpp
runtime/moc_TrayIcon.cpp
runtime/moc_Server.cpp
runtime/pgAdmin4.app/
runtime/pgAdmin4.pro.user*

View File

@ -1,497 +0,0 @@
@ECHO off
SETLOCAL
REM
REM ****************************************************************
SET WD=%CD%
SET "PGBUILDPATH=%WD%\win-build"
SET CMDOPTION=""
IF "%1" == "clean" SET CMDOPTION="VALID"
IF "%1" == "x86" SET CMDOPTION="VALID"
IF NOT %CMDOPTION%=="VALID" ( GOTO USAGE )
SET ARCHITECTURE=%1
IF "%ARCHITECTURE%"=="clean" (
GOTO CLEAN_RELEASE
GOTO EXIT
)
REM Main Functions
CALL :SET_PGADMIN4_ENVIRONMENT
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL :VALIDATE_ENVIRONMENT
CALL :CLEAN_RELEASE
CALL :CREATE_VIRTUAL_ENV
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL :CREATE_RUNTIME_ENV
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL :CREATE_PYTHON_ENV
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL :CLEANUP_ENV
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL :CREATE_INSTALLER
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL :SIGN_INSTALLER
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CD %WD%
GOTO EXIT
REM Main function Ends
:CLEAN_RELEASE
ECHO Calling clean target...
IF EXIST "%PGBUILDPATH%" rd "%PGBUILDPATH%" /S /Q
FOR /R "%WD%" %%f in (*.pyc *.pyo) do DEL /q "%%f" > nul
IF EXIST "%WD%\pkg\win32\Output" rd "%WD%\pkg\win32\Output" /S /Q
IF EXIST DEL /q "%WD%\pkg\win32\installer.iss" > nul
CD %WD%
GOTO:eof
:SET_PGADMIN4_ENVIRONMENT
IF "%PYTHON_HOME%" == "" SET "PYTHON_HOME=C:\Python27"
IF "%PYTHON_DLL%" == "" SET "PYTHON_DLL=C:\Windows\SysWOW64\python27.dll"
IF "%QTDIR%" == "" SET "QTDIR=C:\Qt\5.9.1\mingw53_32"
IF "%PGDIR%" == "" SET "PGDIR=C:\Program Files (x86)\PostgreSQL\9.6"
IF "%INNOTOOL%" == "" SET "INNOTOOL=C:\Program Files (x86)\Inno Setup 5"
IF "%YARNDIR%" == "" SET "YARNDIR=C:\Program Files\Yarn"
IF "%NODEJSDIR%" == "" SET "NODEJSDIR=C:\Program Files\nodejs"
IF "%VCDIR%" == "" SET "VCDIR=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC"
IF "%VCREDIST%" == "" SET "VCREDIST=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\redist\1033\vcredist_x86.exe"
IF "%SIGNTOOL%" == "" SET "SIGNTOOL=C:\Program Files\Microsoft SDKs\Windows\v7.1A\Bin\signtool.exe"
SET "VCREDISTNAME=vcredist_x86.exe"
GOTO:eof
:VALIDATE_ENVIRONMENT
REM SET the variables IF not availalbe in windows environment
SET "QMAKE=%QTDIR%\bin\qmake.exe"
SET "VIRTUALENV=venv"
SET "TARGETINSTALLER=%WD%\dist"
SET "VCREDIST=%VCDIR%\redist\1033\%VCREDISTNAME%"
FOR /F "tokens=3" %%a IN ('findstr /C:"APP_RELEASE =" %WD%\web\config.py') DO SET APP_RELEASE=%%a
FOR /F "tokens=3" %%a IN ('findstr /C:"APP_REVISION =" %WD%\web\config.py') DO SET APP_REVISION_VERSION=%%a
FOR /F "tokens=3" %%a IN ('findstr /C:"APP_SUFFIX =" %WD%\web\config.py') DO SET APP_SUFFIX_VERSION=%%a
REM remove single quote from the string
SET APP_SUFFIX_VERSION=%APP_SUFFIX_VERSION:'=%
SET APP_NAME=""
FOR /F "tokens=2* DELims='" %%a IN ('findstr /C:"APP_NAME =" web\config.py') DO SET APP_NAME=%%a
FOR /f "tokens=1 DELims=." %%G IN ('%PYTHON_HOME%/python.exe -c "print('%APP_NAME%'.lower().replace(' ', ''))"') DO SET APP_SHORTNAME=%%G
FOR /F "tokens=4,5 delims=. " %%a IN ('%QMAKE% -v ^| findstr /B /C:"Using Qt version "') DO SET QT_VERSION=%%a.%%b
SET INSTALLERNAME=%APP_SHORTNAME%-%APP_RELEASE%.%APP_REVISION_VERSION%-%APP_SUFFIX_VERSION%-%ARCHITECTURE%.exe
IF "%APP_SUFFIX_VERSION%" == "" SET INSTALLERNAME=%APP_SHORTNAME%-%APP_RELEASE%.%APP_REVISION_VERSION%-%ARCHITECTURE%.exe
SET PGADMIN4_VERSION=v%APP_RELEASE%
SET PGADMIN4_APP_VERSION=%APP_RELEASE%.%APP_REVISION_VERSION%
ECHO ****************************************************************
ECHO S U M M A R Y
ECHO ****************************************************************
ECHO Target mode = x86
ECHO INNOTOOL = %INNOTOOL%
ECHO VCDIR = %VCDIR%
ECHO VCDIST = %VCREDIST%
ECHO SIGNTOOL = %SIGNTOOL%
ECHO QTDIR = %QTDIR%
ECHO QMAKE = %QMAKE%
ECHO QT_VERSION = %QT_VERSION%
ECHO YARNDIR = %YARNDIR%
ECHO NODEJSDIR = %NODEJSDIR%
ECHO BROWSER = QtWebKit
ECHO PYTHON_HOME = %PYTHON_HOME%
ECHO PYTHON_DLL = %PYTHON_DLL%
ECHO PGDIR = %PGDIR%
ECHO ****************************************************************
REM Check IF path SET in enviroments really exist or not ?
IF NOT EXIST "%INNOTOOL%" GOTO err_handle_inno
IF NOT EXIST "%VCDIR%" GOTO err_handle_visualstudio
IF NOT EXIST "%VCREDIST%" GOTO err_handle_visualstudio_dist
IF NOT EXIST "%QTDIR%" GOTO err_handle_qt
IF NOT EXIST "%QMAKE%" GOTO err_handle_qt
IF NOT EXIST "%PYTHON_HOME%" GOTO err_handle_python
IF NOT EXIST "%PYTHON_DLL%" GOTO err_handle_python
IF NOT EXIST "%PGDIR%" GOTO err_handle_pg
IF NOT EXIST "%YARNDIR%" GOTO err_handle_yarn
IF NOT EXIST "%NODEJSDIR%" GOTO err_handle_nodejs
REM get Python version ex. 2.7.1 will get as 27
FOR /f "tokens=1 DELims=." %%G IN ('%PYTHON_HOME%/python.exe -c "import sys; print(sys.version.split(' ')[0])"') DO SET PYTHON_MAJOR=%%G
FOR /f "tokens=2 DELims=." %%G IN ('%PYTHON_HOME%/python.exe -c "import sys; print(sys.version.split(' ')[0])"') DO SET PYTHON_MINOR=%%G
SET "PYTHON_VERSION=%PYTHON_MAJOR%%PYTHON_MINOR%"
IF NOT EXIST "%PYTHON_HOME%\Scripts\virtualenv.exe" GOTO err_handle_pythonvirtualenv
SET PATH=%PGDIR%;%PGDIR%\bin;%QTDIR%\..\..\Tools\mingw530_32\bin;%NODEJSDIR%;%YARNDIR%\bin;%PATH%;
GOTO:eof
:CREATE_VIRTUAL_ENV
ECHO Creating Virtual Enviroment...
IF NOT EXIST "%PGBUILDPATH%" MKDIR "%PGBUILDPATH%"
CD "%PGBUILDPATH%"
"%PYTHON_HOME%\Scripts\virtualenv.exe" "%VIRTUALENV%"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
ECHO Activating Virtual Enviroment - %PGBUILDPATH%\%VIRTUALENV%\Scripts\activate...
CALL "%PGBUILDPATH%\%VIRTUALENV%\Scripts\activate"
SET PATH=%PGDIR%\bin;%PATH%
ECHO Installing dependencies...
pip install -r "%WD%\requirements.txt"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
pip install sphinx
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
ECHO Virtual Environment created successfully.
ECHO Deactivating Virtual Enviroment - %PGBUILDPATH%\%VIRTUALENV%\Scripts\deactivate...
CALL "%PGBUILDPATH%\%VIRTUALENV%\Scripts\deactivate"
CD %WD%
GOTO:eof
:CREATE_RUNTIME_ENV
ECHO Compiling source code...
MKDIR "%PGBUILDPATH%\runtime" > nul
REM --- Processing WEB ---
CD "%WD%\web"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
ECHO Install Javascript dependencies
call yarn install
ECHO Bundle all Javascript
call yarn run bundle
XCOPY /S /I /E /H /Y "%WD%\web" "%PGBUILDPATH%\web" > nul
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
REM Clean up .pyc, .pyo, pgadmin4.db, config_local.py
ECHO Cleaning up unnecessary files...
FOR /R "%PGBUILDPATH%\web" %%f in (*.pyc *.pyo) do DEL /q "%%f"
FOR /R "%PGBUILDPATH%\web" %%f in (tests feature_tests __pycache__ node_modules) do RD /Q /S "%%f"
RD /Q /S "%PGBUILDPATH%\web\regression"
RD /Q /S "%PGBUILDPATH%\web\tools"
DEL /q "%PGBUILDPATH%\web\pgadmin4.db"
DEL /q "%PGBUILDPATH%\web\config_local.py"
ECHO Creating config_distro.py
ECHO SERVER_MODE = False > "%PGBUILDPATH%\web\config_distro.py"
ECHO HELP_PATH = '../../../docs/en_US/html/' >> "%PGBUILDPATH%\web\config_distro.py"
ECHO DEFAULT_BINARY_PATHS = { >> "%PGBUILDPATH%\web\config_distro.py"
ECHO 'pg': '$DIR/../runtime', >> "%PGBUILDPATH%\web\config_distro.py"
ECHO 'ppas': '' >> "%PGBUILDPATH%\web\config_distro.py"
ECHO } >> "%PGBUILDPATH%\web\config_distro.py"
ECHO Activating Virtual Enviroment - %PGBUILDPATH%\%VIRTUALENV%\Scripts\activate...
CALL "%PGBUILDPATH%\%VIRTUALENV%\Scripts\activate"
ECHO Building docs...
MKDIR "%PGBUILDPATH%\docs\en_US\html"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CD "%WD%\docs\en_US"
"%PGBUILDPATH%\%VIRTUALENV%\Scripts\python.exe" build_code_snippet.py
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
"%PGBUILDPATH%\%VIRTUALENV%\Scripts\sphinx-build.exe" "%WD%\docs\en_US" "%PGBUILDPATH%\docs\en_US\html"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
ECHO Removing Sphinx
pip uninstall -y sphinx Pygments alabaster colorama docutils imagesize requests snowballstemmer
ECHO Fixing backports.csv - adding missing __init__.py
type nul >> "%PGBUILDPATH%\%VIRTUALENV%\Lib\site-packages\backports\__init__.py"
ECHO Assembling runtime environment...
CD "%WD%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL "%QMAKE%" "DEFINES+=PGADMIN4_USE_WEBKIT"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL mingw32-make.exe clean
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL mingw32-make.exe
IF ERRORLEVEL 1 GOTO ERR_HANDLER
REM Copy binary to Release Folder
copy "%WD%\runtime\release\pgAdmin4.exe" "%PGBUILDPATH%\runtime"
IF ERRORLEVEL 1 GOTO ERR_HANDLER
REM Copy QT dependences
copy "%QTDIR%\bin\Qt5Core.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Sql.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Gui.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Qml.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5OpenGL.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Quick.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Sensors.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Widgets.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Network.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Multimedia.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5WebChannel.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Positioning.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5PrintSupport.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5MultimediaWidgets.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
IF %QT_VERSION% GEQ 5.9 (
copy "%QTDIR%\bin\Qt5WebKit.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5WebKitWidgets.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
) ELSE (
copy "%QTDIR%\bin\libQt5WebKit.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\libQt5WebKitWidgets.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
)
copy "%QTDIR%\bin\icudt57.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\icuin57.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\icuuc57.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\libgcc_s_dw2-1.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\libstdc++-6.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\libwinpthread-1.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\libxml2-2.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\libxslt-1.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
MKDIR "%PGBUILDPATH%\runtime\platforms"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\plugins\platforms\qwindows.dll" "%PGBUILDPATH%\runtime\platforms" > nul
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
ECHO [Paths] > "%PGBUILDPATH%\runtime\qt.conf"
ECHO Plugins=plugins >> "%PGBUILDPATH%\runtime\qt.conf"
copy "%PGDIR%\bin\libpq.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%PGDIR%\bin\ssleay32.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%PGDIR%\bin\libeay32.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%PGDIR%\bin\libintl-*.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%PGDIR%\bin\libiconv-*.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%PGDIR%\bin\zlib1.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%PGDIR%\bin\pg_dump.exe" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%PGDIR%\bin\pg_dumpall.exe" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%PGDIR%\bin\pg_restore.exe" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%PGDIR%\bin\psql.exe" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
MKDIR "%PGBUILDPATH%\installer"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%VCREDIST%" "%PGBUILDPATH%\installer" > nul
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
ECHO Runtime built successfully.
CD %WD%
GOTO:eof
:CREATE_PYTHON_ENV
copy %PYTHON_DLL% "%PGBUILDPATH%\runtime" > nul
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
REM Copy Python interpretor as it's needed to run background processes
copy %PYTHON_HOME%\python.exe "%PGBUILDPATH%\runtime" > nul
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy %PYTHON_HOME%\pythonw.exe "%PGBUILDPATH%\runtime" > nul
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
XCOPY /S /I /E /H /Y "%PYTHON_HOME%\DLLs" "%PGBUILDPATH%\%VIRTUALENV%\DLLs" > nul
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
XCOPY /S /I /E /H /Y "%PYTHON_HOME%\Lib" "%PGBUILDPATH%\%VIRTUALENV%\Lib" > nul
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
ECHO Cleaning up unnecessary files...
FOR /R "%PGBUILDPATH%\%VIRTUALENV%" %%f in (*.pyc *.pyo) do DEL /q "%%f"
FOR /R "%PGBUILDPATH%\%VIRTUALENV%\Lib" %%f in (test tests) do RD /Q /S "%%f"
RD /Q /S "%PGBUILDPATH%\%VIRTUALENV%\tcl"
CD %WD%
GOTO:eof
:CREATE_INSTALLER
ECHO Preparing for creation of windows installer...
IF NOT EXIST "%TARGETINSTALLER%" MKDIR "%TARGETINSTALLER%"
copy "%WD%\pkg\win32\Resources\pgAdmin4.ico" "%PGBUILDPATH%"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CD "%WD%"
CD pkg
CD win32
"%PYTHON_HOME%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in" "-o" "%WD%\pkg\win32\installer.iss.in_stage1" "-s" MYAPP_NAME -r """%APP_NAME%"""
"%PYTHON_HOME%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in_stage1" "-o" "%WD%\pkg\win32\installer.iss.in_stage2" "-s" MYAPP_FULLVERSION -r """%PGADMIN4_APP_VERSION%"""
"%PYTHON_HOME%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in_stage2" "-o" "%WD%\pkg\win32\installer.iss.in_stage3" "-s" MYAPP_VERSION -r """%PGADMIN4_VERSION%"""
set ARCMODE=
IF "%ARCHITECTURE%"=="amd64" (
set ARCMODE="x64"
)
"%PYTHON_HOME%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in_stage3" "-o" "%WD%\pkg\win32\installer.iss.in_stage4" "-s" MYAPP_ARCHITECTURESMODE -r """%ARCMODE%"""
"%PYTHON_HOME%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in_stage4" "-o" "%WD%\pkg\win32\installer.iss" "-s" MYAPP_VCDIST -r """%VCREDISTNAME%"""
DEL /s "%WD%\pkg\win32\installer.iss.in_stage*" > nul
ECHO Creating windows installer... using INNO tool
CALL "%INNOTOOL%\ISCC.exe" /q "%WD%\pkg\win32\installer.iss"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
MOVE "%WD%\pkg\win32\Output\Setup.exe" "%TARGETINSTALLER%\%INSTALLERNAME%"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
ECHO "Location - %TARGETINSTALLER%\%INSTALLERNAME%"
ECHO Installer generated successfully.
CD %WD%
GOTO:eof
:SIGN_INSTALLER
ECHO Attempting to sign the installer...
"%SIGNTOOL%" sign /t http://timestamp.verisign.com/scripts/timstamp.dll "%TARGETINSTALLER%\%INSTALLERNAME%"
IF %ERRORLEVEL% NEQ 0 (
ECHO
ECHO ************************************************************
ECHO * Failed to sign the installer
ECHO ************************************************************
PAUSE
)
CD %WD%
GOTO:eof
:CLEANUP_ENV
ECHO Cleaning up private environment...
rd "%PGBUILDPATH%\%VIRTUALENV%\Include" /S /Q
DEL /s "%PGBUILDPATH%\%VIRTUALENV%\pip-selfcheck.json"
ECHO Cleaned up private environment successfully.
CD %WD%
GOTO:eof
:err_handle_inno
ECHO %INNOTOOL% does not exist
ECHO Please Install Innotool and SET INNOTOOL enviroment Variable.
ECHO SET "INNOTOOL=<PATH>"
exit /B 1
GOTO EXIT
:err_handle_visualstudio
ECHO %VCDIR% does not exist
ECHO Please Install Microsoft Visual studio and SET the VCDIR enviroment Variable.
ECHO SET "VCDIR=<PATH>"
exit /B 1
GOTO EXIT
:err_handle_visualstudio_dist
ECHO %VCREDIST% does not exist
ECHO Please Install Microsoft Visual studio and SET the VCDIST enviroment Variable.
ECHO SET "VCDIST=<PATH>"
exit /B 1
GOTO EXIT
:err_handle_python
ECHO %PYTHON_HOME% does not exist, or
ECHO PYTHON_VERSION is not SET, or
ECHO %PYTHON_DLL% does not exist.
ECHO Please install Python and SET the PYTHON_HOME enviroment Variable.
ECHO SET "PYTHON_VERSION=<VERSION NUMBER>"
ECHO SET "PYTHON_HOME=<PATH>"
ECHO SET "PYTHON_DLL=<PATH>"
exit /B 1
GOTO EXIT
:err_handle_qt
ECHO %QTDIR% does not exist.
ECHO Please Install QT SDK and SET the QTDIR enviroment variable.
ECHO SET "QTDIR=<PATH>"
exit /B 1
GOTO EXIT
:err_handle_yarn
ECHO %YARNDIR% does not exist.
ECHO Please Install YARN and SET the YARNDIR enviroment variable.
ECHO SET "YARNDIR=<YARN PATH>"
exit /B 1
GOTO EXIT
:err_handle_nodejs
ECHO %NODEJSDIR% does not exist.
ECHO Please Install NodeJs and SET the NODEJSDIR enviroment variable.
ECHO SET "NODEJSDIR=<NODEJS PATH>"
exit /B 1
GOTO EXIT
:err_handle_pg
ECHO %PGDIR% does not exist.
ECHO Please Install Postgres and SET enviroment Variable
ECHO SET "PGDIR=<PATH>"
exit /B 1
GOTO EXIT
:err_handle_pythonvirtualenv
ECHO Python virtualenv is missing @ location - %PYTHON_HOME%\Scripts\virtualenv.exe
exit /B 1
GOTO EXIT
:ERR_HANDLER
ECHO.
ECHO Aborting build!
CD %WD%
exit /B 1
GOTO EXIT
:USAGE
ECHO Invalid command line options....
ECHO Usage: "Make.bat <x86 | clean>"
ECHO.
exit /B 1
GOTO EXIT
:EXIT

241
Make.bat
View File

@ -7,7 +7,6 @@ SET "PGBUILDPATH=%WD%\win-build"
SET CMDOPTION=""
IF "%1" == "clean" SET CMDOPTION="VALID"
IF "%1" == "x86" SET CMDOPTION="VALID"
IF "%1" == "amd64" SET CMDOPTION="VALID"
IF NOT %CMDOPTION%=="VALID" ( GOTO USAGE )
SET ARCHITECTURE=%1
@ -59,65 +58,22 @@ REM Main function Ends
GOTO:eof
:SET_PGADMIN4_ENVIRONMENT
REM Check os architecture x86 or amd64
SET RegQry=HKLM\Hardware\Description\System\CentralProcessor\0
REG.exe Query %RegQry% > checkOS.txt
Find /i "x86" < CheckOS.txt > StringCheck.txt
SET OSTYPE=""
IF %ERRORLEVEL% == 0 (
SET OSTYPE=x86
) else (
SET OSTYPE=amd64
)
DEL CheckOS.txt StringCheck.txt
SET OSVALUE=""
IF "%OSTYPE%"=="x86" (
IF "%ARCHITECTURE%"=="amd64" (
ECHO ARCHITECTURE - %ARCHITECTURE% cannot be run on 32 bit machine
GOTO EXIT
)
SET OSVALUE=%OSTYPE%
)
IF "%PYTHON_HOME%" == "" SET "PYTHON_HOME=C:\Python27"
IF "%PYTHON_DLL%" == "" SET "PYTHON_DLL=C:\Windows\SysWOW64\python27.dll"
IF "%QTDIR%" == "" SET "QTDIR=C:\Qt\5.9.1\mingw53_32"
IF "%PGDIR%" == "" SET "PGDIR=C:\Program Files (x86)\PostgreSQL\10"
IF "%INNOTOOL%" == "" SET "INNOTOOL=C:\Program Files (x86)\Inno Setup 5"
IF "%YARNDIR%" == "" SET "YARNDIR=C:\Program Files\Yarn"
IF "%NODEJSDIR%" == "" SET "NODEJSDIR=C:\Program Files\nodejs"
IF "%VCDIR%" == "" SET "VCDIR=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC"
IF "%VCREDIST%" == "" SET "VCREDIST=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\redist\1033\vcredist_x86.exe"
IF "%SIGNTOOL%" == "" SET "SIGNTOOL=C:\Program Files\Microsoft SDKs\Windows\v7.1A\Bin\signtool.exe"
SET "VCREDISTNAME=vcredist_x86.exe"
REM Check IF its is windows 32 bit machine and selected architecture is x86
IF %OSVALUE%=="x86" (
IF "%PYTHON_HOME%" == "" SET "PYTHON_HOME=C:\Python27"
IF "%PYTHON_DLL%" == "" SET "PYTHON_DLL=C:\Windows\System32\python27.dll"
IF "%QTDIR%" == "" SET "QTDIR=C:\Qt\5.7\msvc2013"
IF "%PGDIR%" == "" SET "PGDIR=C:\Program Files\PostgreSQL\9.6"
IF "%INNOTOOL%" == "" SET "INNOTOOL=C:\Program Files\Inno Setup 5"
IF "%VCDIR%" == "" SET "VCDIR=C:\Program Files\Microsoft Visual Studio 12.0\VC"
SET "VCREDISTNAME=vcredist_x86.exe"
)
REM Check IF its is windows 64 bit machine and selected architecture is x86 or amd64
IF "%ARCHITECTURE%"=="x86" (
IF "%PYTHON_HOME%" == "" SET "PYTHON_HOME=C:\Python27"
IF "%PYTHON_DLL%" == "" SET "PYTHON_DLL=C:\Windows\SysWOW64\python27.dll"
IF "%QTDIR%" == "" SET "QTDIR=C:\Qt\5.7\msvc2013"
IF "%PGDIR%" == "" SET "PGDIR=C:\Program Files (x86)\PostgreSQL\9.6"
IF "%INNOTOOL%" == "" SET "INNOTOOL=C:\Program Files (x86)\Inno Setup 5"
IF "%VCDIR%" == "" SET "VCDIR=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC"
IF "%VCREDIST%" == "" SET "VCREDIST=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\redist\1033\vcredist_x86.exe"
SET "VCREDISTNAME=vcredist_x86.exe"
)
IF "%ARCHITECTURE%"=="amd64" (
IF "%PYTHON_HOME%" == "" SET "PYTHON_HOME=C:\Python27-x64"
IF "%PYTHON_DLL%" == "" SET "PYTHON_DLL=C:\Windows\System32\python27.dll"
IF "%QTDIR%" == "" SET "QTDIR=C:\Qt\5.7\msvc2013"
IF "%PGDIR%" == "" SET "PGDIR=C:\Program Files\PostgreSQL\9.6"
IF "%INNOTOOL%" == "" SET "INNOTOOL=C:\Program Files\Inno Setup 5"
IF "%VCDIR%" == "" SET "VCDIR=C:\Program Files\Microsoft Visual Studio 12.0\VC"
IF "%VCREDIST%" == "" SET "VCREDIST=C:\Program Files\Microsoft Visual Studio 12.0\VC\redist\1033\vcredist_x64.exe"
SET "VCREDISTNAME=vcredist_x64.exe"
)
GOTO:eof
:VALIDATE_ENVIRONMENT
REM SET the variables IF not availalbe in windows environment
SET "VCVAR=%VCDIR%\vcvarsall.bat"
SET "VCNMAKE=%VCDIR%\bin\nmake.exe"
REM SET the variables IF not available in windows environment
SET "QMAKE=%QTDIR%\bin\qmake.exe"
SET "VIRTUALENV=venv"
SET "TARGETINSTALLER=%WD%\dist"
@ -142,19 +98,16 @@ REM Main function Ends
ECHO ****************************************************************
ECHO S U M M A R Y
ECHO ****************************************************************
ECHO Target mode = %ARCHITECTURE%
ECHO Target mode = x86
ECHO INNOTOOL = %INNOTOOL%
ECHO VCDIR = %VCDIR%
ECHO VCDIST = %VCREDIST%
ECHO NMAKE = %VCNMAKE%
ECHO SIGNTOOL = %SIGNTOOL%
ECHO QTDIR = %QTDIR%
ECHO QMAKE = %QMAKE%
ECHO QT_VERSION = %QT_VERSION%
IF %QT_VERSION% GEQ 5.5 (
ECHO BROWSER = QtWebEngine
) ELSE (
ECHO BROWSER = QtWebKit
)
ECHO YARNDIR = %YARNDIR%
ECHO NODEJSDIR = %NODEJSDIR%
ECHO PYTHON_HOME = %PYTHON_HOME%
ECHO PYTHON_DLL = %PYTHON_DLL%
ECHO PGDIR = %PGDIR%
@ -164,35 +117,13 @@ REM Main function Ends
IF NOT EXIST "%INNOTOOL%" GOTO err_handle_inno
IF NOT EXIST "%VCDIR%" GOTO err_handle_visualstudio
IF NOT EXIST "%VCREDIST%" GOTO err_handle_visualstudio_dist
IF NOT EXIST "%VCVAR%" GOTO err_handle_visualstudio
IF NOT EXIST "%VCNMAKE%" GOTO err_handle_visualstudio
IF NOT EXIST "%QTDIR%" GOTO err_handle_qt
IF NOT EXIST "%QMAKE%" GOTO err_handle_qt
IF NOT EXIST "%PYTHON_HOME%" GOTO err_handle_python
IF NOT EXIST "%PYTHON_DLL%" GOTO err_handle_python
IF NOT EXIST "%PGDIR%" GOTO err_handle_pg
REM Check for QT and VC dependences
FOR /L %%G IN (15,1,19) DO "%VCDIR%\bin\cl.exe" /? 2>&1 | findstr /C:"Version %%G" > nul && SET MSVC_MAJOR_VERSION=%%G
IF %MSVC_MAJOR_VERSION%==19 SET QT_MSVC_PATH=msvc2015
IF %MSVC_MAJOR_VERSION%==18 SET QT_MSVC_PATH=msvc2013
IF %MSVC_MAJOR_VERSION%==17 SET QT_MSVC_PATH=msvc2012
IF %MSVC_MAJOR_VERSION%==16 SET QT_MSVC_PATH=msvc2010
IF %MSVC_MAJOR_VERSION%==15 SET QT_MSVC_PATH=msvc2008
REM on 64 bit machine if x86 is selected and QTDIR is set to 64 bit is should not allow
IF "%OSTYPE%"=="amd64" (
IF "%ARCHITECTURE%"=="x86" (
echo "%QTDIR%" | findstr /C:"_64" > nul && ( GOTO err_handle_qt_compactissue )
)
)
IF "%ARCHITECTURE%"=="amd64" (
SET QT_MSVC_PATH=%QT_MSVC_PATH%_64
)
IF NOT EXIST "%QTDIR%\..\%QT_MSVC_PATH%" GOTO err_handle_qt_mismatch
IF NOT EXIST "%YARNDIR%" GOTO err_handle_yarn
IF NOT EXIST "%NODEJSDIR%" GOTO err_handle_nodejs
REM get Python version ex. 2.7.1 will get as 27
FOR /f "tokens=1 DELims=." %%G IN ('%PYTHON_HOME%/python.exe -c "import sys; print(sys.version.split(' ')[0])"') DO SET PYTHON_MAJOR=%%G
@ -201,8 +132,7 @@ REM Main function Ends
IF NOT EXIST "%PYTHON_HOME%\Scripts\virtualenv.exe" GOTO err_handle_pythonvirtualenv
SET PATH=%PGDIR%;%PGDIR%\bin;%PATH%
SET PATH=%PGDIR%;%PGDIR%\bin;%QTDIR%\..\..\Tools\mingw530_32\bin;%NODEJSDIR%;%YARNDIR%\bin;%PATH%;
GOTO:eof
:CREATE_VIRTUAL_ENV
@ -244,6 +174,9 @@ REM Main function Ends
ECHO Bundle all Javascript
call yarn run bundle
REM Remove any cache
RD /Q /S "%WD%\web\pgadmin\static\js\generated\.cache"
XCOPY /S /I /E /H /Y "%WD%\web" "%PGBUILDPATH%\web" > nul
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
@ -288,17 +221,15 @@ REM Main function Ends
CD "%WD%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL "%VCVAR%" %ARCHITECTURE%
CALL "%QMAKE%" "DEFINES+=PGADMIN4_USE_WEBKIT"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL "%QMAKE%"
CALL mingw32-make.exe clean
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL "%VCNMAKE%" clean
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
CALL "%VCNMAKE%"
CALL mingw32-make.exe
IF ERRORLEVEL 1 GOTO ERR_HANDLER
REM Copy binary to Release Folder
copy "%WD%\runtime\release\pgAdmin4.exe" "%PGBUILDPATH%\runtime"
IF ERRORLEVEL 1 GOTO ERR_HANDLER
@ -306,97 +237,24 @@ REM Main function Ends
REM Copy QT dependences
copy "%QTDIR%\bin\Qt5Core.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Sql.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Gui.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Qml.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5OpenGL.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Quick.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Sensors.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Widgets.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Network.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Multimedia.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5WebChannel.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5Positioning.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5PrintSupport.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5MultimediaWidgets.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
REM Install the appropriate browser components. We use QtWebEngine with Qt 5.5+
IF %QT_VERSION% GEQ 5.7 (
copy "%QTDIR%\bin\Qt5QuickWidgets.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
)
IF %QT_VERSION% GEQ 5.7 (
copy "%QTDIR%\resources\icudtl.dat" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\resources\qtwebengine_resources.pak" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\resources\qtwebengine_devtools_resources.pak" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\resources\qtwebengine_resources_100p.pak" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\resources\qtwebengine_resources_200p.pak" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5WebEngine.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5WebEngineCore.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5WebEngineWidgets.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\QtWebEngineProcess.exe" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\opengl32sw.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
) ELSE (
IF %QT_VERSION% GEQ 5.5 (
copy "%QTDIR%\bin\icudt54.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\icuin54.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\icuuc54.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\icudtl.dat" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\qtwebengine_resources.pak" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\qtwebengine_resources_100p.pak" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\qtwebengine_resources_200p.pak" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5WebEngine.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5WebEngineCore.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5WebEngineWidgets.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\QtWebEngineProcess.exe" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
) ELSE (
copy "%QTDIR%\bin\Qt5WebKit.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\Qt5WebKitWidgets.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
)
)
copy "%QTDIR%\bin\libgcc_s_dw2-1.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\libstdc++-6.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\bin\libwinpthread-1.dll" "%PGBUILDPATH%\runtime"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
MKDIR "%PGBUILDPATH%\runtime\platforms"
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
copy "%QTDIR%\plugins\platforms\qwindows.dll" "%PGBUILDPATH%\runtime\platforms" > nul
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
ECHO [Paths] > "%PGBUILDPATH%\runtime\qt.conf"
ECHO Plugins=plugins >> "%PGBUILDPATH%\runtime\qt.conf"
@ -494,13 +352,13 @@ REM Main function Ends
:SIGN_INSTALLER
ECHO Attempting to sign the installer...
signtool sign /t http://timestamp.verisign.com/scripts/timstamp.dll "%TARGETINSTALLER%\%INSTALLERNAME%"
"%SIGNTOOL%" sign /t http://timestamp.verisign.com/scripts/timstamp.dll "%TARGETINSTALLER%\%INSTALLERNAME%"
IF %ERRORLEVEL% NEQ 0 (
ECHO
ECHO ************************************************************
ECHO * Failed to sign the installer
ECHO ************************************************************
SLEEP 5
PAUSE
)
CD %WD%
@ -523,20 +381,16 @@ REM Main function Ends
GOTO EXIT
:err_handle_visualstudio
ECHO %VCDIR% does not exist, or
ECHO %VCVAR% does not exist, or
ECHO %VCNMAKE% does not exist.
ECHO %VCDIR% does not exist
ECHO Please Install Microsoft Visual studio and SET the VCDIR enviroment Variable.
ECHO SET "VCDIR%=<PATH>"
ECHO SET "VCVAR%=<PATH>"
ECHO SET "VCNMAKE%=<PATH>"
ECHO SET "VCDIR=<PATH>"
exit /B 1
GOTO EXIT
:err_handle_visualstudio_dist
ECHO %VCREDIST% does not exist
ECHO Please Install Microsoft Visual studio and SET the VCDIST enviroment Variable.
ECHO SET "VCDIST%=<PATH>"
ECHO SET "VCDIST=<PATH>"
exit /B 1
GOTO EXIT
@ -559,18 +413,17 @@ REM Main function Ends
exit /B 1
GOTO EXIT
:err_handle_qt_mismatch
ECHO %QTDIR%\..\%QT_MSVC_PATH%" does not match with your current Visual Studio, version %QT_MSVC_PATH%
ECHO Your current QT installation willraise a linking error with an MSVC version mismatch.
ECHO Please use a valid QT installation with a folder %QT_MSVC_PATH%. You can use the Qt Maintenance
ECHO Tool to add or remove compiler kits.
:err_handle_yarn
ECHO %YARNDIR% does not exist.
ECHO Please Install YARN and SET the YARNDIR enviroment variable.
ECHO SET "YARNDIR=<YARN PATH>"
exit /B 1
GOTO EXIT
:err_handle_qt_compactissue
ECHO %QTDIR%" does support the current architecture selected %ARCHITECTURE%
ECHO Please use a valid QT installation with a folder %QT_MSVC_PATH%. You can use the Qt Maintenance
ECHO Tool to add or remove compiler kits.
:err_handle_nodejs
ECHO %NODEJSDIR% does not exist.
ECHO Please Install NodeJs and SET the NODEJSDIR enviroment variable.
ECHO SET "NODEJSDIR=<NODEJS PATH>"
exit /B 1
GOTO EXIT
@ -595,7 +448,7 @@ REM Main function Ends
:USAGE
ECHO Invalid command line options....
ECHO Usage: "Make.bat <x86 | amd64 | clean>"
ECHO Usage: "Make.bat <x86 | clean>"
ECHO.
exit /B 1
GOTO EXIT

View File

@ -19,9 +19,6 @@ all: docs pip src
appbundle: docs
./pkg/mac/build.sh
appbundle-webkit: docs
PGADMIN4_USE_WEBKIT=1 ./pkg/mac/build.sh
install-node:
cd web && yarn install

18
README
View File

@ -18,18 +18,16 @@ utilised.
Although developed using web technologies, pgAdmin 4 can be deployed either on
a web server using a browser, or standalone on a workstation. The runtime/
subdirectory contains a QT based runtime application intended to allow this -
it is essentially a browser and Python interpreter in one package which is
capable of hosting the Python application and presenting it to the user as a
desktop application.
it is essentially a Python application server that runs in the system tray
and allows the user to connect to the application using their web browser.
Building the Runtime
--------------------
To build the runtime, the following packages must be installed:
- QT 4.6 or above, up to 5.5 (older versions may work, but haven't been tested,
newer versions are not yet supported as Qt Webkit has been deprecated).
- Python 2.6, 2.7 or 3.3+
- QT 4.6 or above
- Python 2.6, 2.7 or 3.3+ (2.7 only on Windows)
Assuming both qmake and python-config are in the path:
@ -230,8 +228,8 @@ a number of known locations for the pgAdmin4.py file needed to run pgAdmin
an alternate path if needed.
If either a working environment or pgAdmin4.py cannot be found at startup, the
runtime will prompt for the locations. Alternatively, you can use Alt+Shift+P
to open the path configuration dialogue.
runtime will prompt for the locations. Alternatively, you can click the try
icon and select the Configuration option to open the configuration dialogue.
On a Linux/Mac system, the Python Path will typically consist of a single path
to the virtual environment's site-packages directory, e.g.
@ -308,11 +306,11 @@ Qt 5.5.1, Python 2.7 and Visual Studio 2013. The examples below are for a
similar 32 bit system:
INNOTOOL=C:\Program Files\Inno Setup 5
PGDIR=C:\Program Files\PostgreSQL\9.6
PGDIR=C:\Program Files\PostgreSQL\10
PYTHON_DLL=C:\Windows\System32\Python27.dll
PYTHON_HOME=C:\Python27
PYTHON_VERSION=27
QTDIR=C:\Qt\5.5\msvc2013
QTDIR=C:\Qt\5.9\msvc2013
VCDIR=C:\Program Files\Microsoft Visual Studio 12.0\VC
To build the installer:

View File

@ -5,8 +5,16 @@ Desktop Deployment
******************
pgAdmin may be deployed as a desktop application by configuring the application
to run in desktop mode and then utilising the desktop runtime to host and
display the program on a supported Windows, Mac OS X or Linux installation.
to run in desktop mode and then utilising the desktop runtime to host the
program on a supported Windows, Mac OS X or Linux installation.
The desktop runtime is a system-tray application that when launched, runs the
pgAdmin server and launches a web browser to render the user interface. If
additional instances of pgAdmin are launched, a new browser tab will be opened
and be served by the existing instance of the server in order to minimise system
resource utilisation. Clicking the icon in the system tray will present a menu
offering options to open a new pgAdmin window, configure the runtime, view the
server log and shutdown the server.
.. note:: Pre-compiled and configured installation packages are available for
a number of platforms. These packages should be used by end-users whereever

View File

@ -28,11 +28,11 @@ Contents:
change_user_password
In a Desktop Deployment, the pgAdmin application is configured to use the
desktop runtime environment to host and display the program on a supported
platform. Typically, users will install a pre-built package to run pgAdmin
in desktop mode, but a manual desktop deployment can be installed and though
it is more difficult to setup, it may be useful for developers interested
in understanding how pgAdmin works.
desktop runtime environment to host the program on a supported platform.
Typically, users will install a pre-built package to run pgAdmin in desktop
mode, but a manual desktop deployment can be installed and though it is more
difficult to setup, it may be useful for developers interested in understanding
how pgAdmin works.
Contents:

View File

@ -6,7 +6,11 @@ pgAdmin 4
:align: right
:alt: pgAdmin Logo
Welcome to pgAdmin 4. pgAdmin is the leading Open Source management tool for Postgres, the world's most advanced Open Source database. pgAdmin 4 is designed to meet the needs of both novice and experienced Postgres users alike, providing a powerful graphical interface that simplifies the creation, maintenance and use of database objects.
Welcome to pgAdmin 4. pgAdmin is the leading Open Source management tool for
Postgres, the world's most advanced Open Source database. pgAdmin 4 is designed
to meet the needs of both novice and experienced Postgres users alike, providing
a powerful graphical interface that simplifies the creation, maintenance and use
of database objects.
Contents:

View File

@ -2,31 +2,9 @@
Keyboard Shortcuts
******************
Keyboard shortcuts are provided in pgAdmin to allow easy access to specific functions.
The shortcuts can be configured through File > Preferences dialogue as per the need.
**Desktop Runtime**
When running in the Desktop Runtime, the following keyboard shortcuts are available:
+--------------------------+----------------+---------------------------------------+
| Shortcut (Windows/Linux) | Shortcut (Mac) | Function |
+==========================+================+=======================================+
| Alt+Shift+A | Option+Shift+A | Display the runtime's About box |
+--------------------------+----------------+---------------------------------------+
| Alt+Shift+P | Option+Shift+U | Open the runtime preferences dialogue |
+--------------------------+----------------+---------------------------------------+
| Alt+Shift+U | Option+Shift+U | Open an arbitrary URL |
+--------------------------+----------------+---------------------------------------+
| Ctrl+Q | Cmd+Q | Quit |
+--------------------------+----------------+---------------------------------------+
| Ctrl+Plus | Cmd+Plus | Zoom in |
+--------------------------+----------------+---------------------------------------+
| Ctrl+Minus | Cmd+Minus | Zoom out |
+--------------------------+----------------+---------------------------------------+
| Ctrl+0 | Cmd+0 | Reset the zoom level |
+--------------------------+----------------+---------------------------------------+
Keyboard shortcuts are provided in pgAdmin to allow easy access to specific
functions. Alternate shortcuts can be configured through File > Preferences if
desired.
**Main Browser Window**

View File

@ -60,7 +60,9 @@ pushd web
yarn install
yarn run bundle
for FILE in `ls -d pgAdmin/static/js/generated/*`
rm -rf pgadmin/static/js/generated/.cache
for FILE in `ls -d pgadmin/static/js/generated/*`
do
echo Adding $FILE
tar cf - $FILE | (cd ../docker-build/web; tar xf -)

View File

@ -137,11 +137,7 @@ _build_runtime() {
_create_python_virtualenv || exit 1
cd $SOURCEDIR/runtime
make clean
if [ "$PGADMIN4_USE_WEBKIT" == "1" ]; then
$QMAKE DEFINES+=PGADMIN4_USE_WEBKIT || { echo qmake failed; exit 1; }
else
$QMAKE || { echo qmake failed; exit 1; }
fi
$QMAKE || { echo qmake failed; exit 1; }
make || { echo make failed; exit 1; }
cp -r pgAdmin4.app "$BUILDROOT/$APP_BUNDLE_NAME"
}
@ -187,7 +183,7 @@ _complete_bundle() {
cp -r $SOURCEDIR/web "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/" || exit 1
cd "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/web"
rm -f pgadmin4.db config_local.*
rm -rf karma.conf.js package.json node_modules/ regression/ tools/
rm -rf karma.conf.js package.json node_modules/ regression/ tools/ pgadmin/static/js/generated/.cache
find . -name "tests" -type d -exec rm -rf "{}" \;
find . -name "feature_tests" -type d -exec rm -rf "{}" \;
find . -name ".DS_Store" -exec rm -f "{}" \;

View File

@ -86,13 +86,6 @@ function CompleteSingleApp() {
test -d $lib_loc || mkdir -p $lib_loc
echo Copying -R $QTDIR/lib/$qtfw_path/$lib_bn to $lib_loc/
cp $QTDIR/lib/$qtfw_path/$lib_bn $lib_loc/
if [ "$lib_bn" = "QtWebEngineCore" ]; then
# QtWebEngineCore has some required resources
cp -R $QTDIR/lib/$qtfw_path/Resources $lib_loc/
cp -R $QTDIR/lib/$qtfw_path/Helpers $lib_loc/
ln -s Versions/Current/Helpers "$bundle/Contents/Frameworks/QtWebEngineCore.Framework/Helpers"
fi
elif echo $lib | grep Python > /dev/null ; then
test -d $lib_loc || mkdir -p $lib_loc
cp -R "$lib" "$lib_loc/$lib_bn"

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;
}