From 7192a2b67502b2afbad9e88cd3fc4618e7e64da1 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Mon, 5 Feb 2018 15:32:14 +0000 Subject: [PATCH] Rewrite the runtime as a tray-based server which can launch a web browser. Fixes #3086 --- .gitignore | 2 +- Make-MinGW.bat | 497 ----------- Make.bat | 241 +---- Makefile | 3 - README | 18 +- docs/en_US/desktop_deployment.rst | 12 +- docs/en_US/getting_started.rst | 10 +- docs/en_US/index.rst | 6 +- docs/en_US/keyboard_shortcuts.rst | 28 +- pkg/docker/build.sh | 4 +- pkg/mac/build.sh | 8 +- pkg/mac/complete-bundle.sh | 7 - runtime/BrowserWindow.cpp | 1377 ----------------------------- runtime/BrowserWindow.h | 170 ---- runtime/BrowserWindow.ui | 37 - runtime/ConfigWindow.cpp | 10 + runtime/ConfigWindow.h | 3 +- runtime/ConfigWindow.ui | 222 ++++- runtime/Info.plist | 2 + runtime/LogWindow.cpp | 88 ++ runtime/LogWindow.h | 40 + runtime/LogWindow.ui | 106 +++ runtime/Server.cpp | 9 +- runtime/Server.h | 9 +- runtime/TabWindow.cpp | 713 --------------- runtime/TabWindow.h | 188 ---- runtime/TrayIcon.cpp | 244 +++++ runtime/TrayIcon.h | 61 ++ runtime/WebViewWindow.cpp | 195 ---- runtime/WebViewWindow.h | 95 -- runtime/pgAdmin4-mac.png | Bin 0 -> 81551 bytes runtime/pgAdmin4.cpp | 361 ++++---- runtime/pgAdmin4.h | 3 + runtime/pgAdmin4.png | Bin 0 -> 99482 bytes runtime/pgAdmin4.pro | 65 +- runtime/pgAdmin4.qrc | 3 +- runtime/qss/pgadmin4.qss | 3 - 37 files changed, 1026 insertions(+), 3814 deletions(-) delete mode 100644 Make-MinGW.bat delete mode 100644 runtime/BrowserWindow.cpp delete mode 100644 runtime/BrowserWindow.h delete mode 100644 runtime/BrowserWindow.ui create mode 100644 runtime/LogWindow.cpp create mode 100644 runtime/LogWindow.h create mode 100644 runtime/LogWindow.ui delete mode 100644 runtime/TabWindow.cpp delete mode 100644 runtime/TabWindow.h create mode 100644 runtime/TrayIcon.cpp create mode 100644 runtime/TrayIcon.h delete mode 100644 runtime/WebViewWindow.cpp delete mode 100644 runtime/WebViewWindow.h create mode 100644 runtime/pgAdmin4-mac.png create mode 100644 runtime/pgAdmin4.png delete mode 100644 runtime/qss/pgadmin4.qss diff --git a/.gitignore b/.gitignore index 3396e4bac..90279beaf 100644 --- a/.gitignore +++ b/.gitignore @@ -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* diff --git a/Make-MinGW.bat b/Make-MinGW.bat deleted file mode 100644 index d130a888f..000000000 --- a/Make-MinGW.bat +++ /dev/null @@ -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=" - 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=" - 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=" - 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=" - ECHO SET "PYTHON_HOME=" - ECHO SET "PYTHON_DLL=" - 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=" - 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=" - 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=" - exit /B 1 - GOTO EXIT - -:err_handle_pg - ECHO %PGDIR% does not exist. - ECHO Please Install Postgres and SET enviroment Variable - ECHO SET "PGDIR=" - 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 " - ECHO. - exit /B 1 - GOTO EXIT - -:EXIT - diff --git a/Make.bat b/Make.bat index 98765c2fe..2949440fd 100644 --- a/Make.bat +++ b/Make.bat @@ -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%=" - ECHO SET "VCVAR%=" - ECHO SET "VCNMAKE%=" + ECHO SET "VCDIR=" 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%=" + ECHO SET "VCDIST=" 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=" 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=" exit /B 1 GOTO EXIT @@ -595,7 +448,7 @@ REM Main function Ends :USAGE ECHO Invalid command line options.... - ECHO Usage: "Make.bat " + ECHO Usage: "Make.bat " ECHO. exit /B 1 GOTO EXIT diff --git a/Makefile b/Makefile index 4c0f70f5b..02341f06f 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README b/README index af741ba1a..85645f3ed 100644 --- a/README +++ b/README @@ -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: diff --git a/docs/en_US/desktop_deployment.rst b/docs/en_US/desktop_deployment.rst index 49aafe0c4..7657896bc 100644 --- a/docs/en_US/desktop_deployment.rst +++ b/docs/en_US/desktop_deployment.rst @@ -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 diff --git a/docs/en_US/getting_started.rst b/docs/en_US/getting_started.rst index 7f1b17bad..e53e42601 100644 --- a/docs/en_US/getting_started.rst +++ b/docs/en_US/getting_started.rst @@ -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: diff --git a/docs/en_US/index.rst b/docs/en_US/index.rst index 46e4d4d76..a57823b56 100644 --- a/docs/en_US/index.rst +++ b/docs/en_US/index.rst @@ -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: diff --git a/docs/en_US/keyboard_shortcuts.rst b/docs/en_US/keyboard_shortcuts.rst index fe0737c26..d4a3a558f 100644 --- a/docs/en_US/keyboard_shortcuts.rst +++ b/docs/en_US/keyboard_shortcuts.rst @@ -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** diff --git a/pkg/docker/build.sh b/pkg/docker/build.sh index 61a490e34..373c99b7e 100755 --- a/pkg/docker/build.sh +++ b/pkg/docker/build.sh @@ -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 -) diff --git a/pkg/mac/build.sh b/pkg/mac/build.sh index 870216587..55ae581d4 100755 --- a/pkg/mac/build.sh +++ b/pkg/mac/build.sh @@ -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 "{}" \; diff --git a/pkg/mac/complete-bundle.sh b/pkg/mac/complete-bundle.sh index 31b92b358..bc8893792 100755 --- a/pkg/mac/complete-bundle.sh +++ b/pkg/mac/complete-bundle.sh @@ -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" diff --git a/runtime/BrowserWindow.cpp b/runtime/BrowserWindow.cpp deleted file mode 100644 index c200ac143..000000000 --- a/runtime/BrowserWindow.cpp +++ /dev/null @@ -1,1377 +0,0 @@ -////////////////////////////////////////////////////////////////////////// -// -// pgAdmin 4 - PostgreSQL Tools -// -// Copyright (C) 2013 - 2018, The pgAdmin Development Team -// This software is released under the PostgreSQL Licence -// -// BrowserWindow.cpp - Implementation of the main window class -// -////////////////////////////////////////////////////////////////////////// - -#include "pgAdmin4.h" - -#if QT_VERSION >= 0x050000 -#include -#ifdef PGADMIN4_USE_WEBENGINE -#include -#else -#include -#include -#endif -#else - -#include -#include -#include -#include -#include -#include -#endif -#include -#include -// App headers -#include "BrowserWindow.h" -#include "ConfigWindow.h" - -// Constructor -BrowserWindow::BrowserWindow(QString url) -{ - m_tabGridLayout = NULL; - m_mainGridLayout = NULL;; - m_tabWidget = NULL; - m_pgAdminMainTab = NULL; - m_addNewTab = NULL; - m_addNewGridLayout = NULL; - m_addNewWebView = NULL; - m_horizontalLayout = NULL; - m_widget = NULL; - m_toolBtnBack = NULL; - m_toolBtnForward = NULL; - m_downloadStarted = 0; - m_downloadCancelled = 0; - m_file = NULL; - m_downloadFilename = ""; - m_defaultFilename = ""; - m_progressDialog = NULL; - m_last_open_folder_path = ""; - m_dir = ""; - m_reply = NULL; - is_readyReadSignaled = false; - m_readBytes = 0; -#ifdef PGADMIN4_USE_WEBENGINE - m_download = NULL; -#endif - - m_appServerUrl = url; - -#ifdef _WIN32 - m_regMessage = ""; -#endif - - // Setup the shortcuts - createActions(); - - m_tabWidget = new DockTabWidget(this); - m_tabWidget->tabBar()->setVisible(false); - - m_mainGridLayout = new QGridLayout(m_tabWidget); - m_mainGridLayout->setContentsMargins(0, 0, 0, 0); - m_pgAdminMainTab = new QWidget(m_tabWidget); - m_tabGridLayout = new QGridLayout(m_pgAdminMainTab); - m_tabGridLayout->setContentsMargins(0, 0, 0, 0); - m_mainWebView = new WebViewWindow(m_pgAdminMainTab); - -#ifdef PGADMIN4_USE_WEBENGINE - m_mainWebView->setPage(new WebEnginePage()); -#else - m_cookieJar = new QNetworkCookieJar(); - m_netAccessMan = new QNetworkAccessManager(); - m_netAccessMan->setCookieJar(m_cookieJar); - m_mainWebView->setPage(new WebViewPage()); - m_mainWebView->page()->setNetworkAccessManager(m_netAccessMan); - m_mainWebView->settings()->setAttribute(QWebSettings::JavascriptEnabled, true); - m_mainWebView->settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true); -#endif - -#ifdef PGADMIN4_DEBUG - // If pgAdmin4 is run in debug mode, then we should enable the - // "Inspect" option, when the user right clicks on QWebView widget. - // This option is useful to debug the pgAdmin4 desktop application and open the developer tools. -#ifdef PGADMIN4_USE_WEBENGINE - // With QWebEngine, run with QTWEBENGINE_REMOTE_DEBUGGING= and then point Google - // Chrome at 127.0.0.1: to debug the runtime's web engine -#else - QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); -#endif -#endif - -#ifdef __APPLE__ - m_mainWebView->setStyle(QStyleFactory::create("Fusion")); -#endif - - m_tabGridLayout->addWidget(m_mainWebView, 0, 0, 1, 1); - m_tabWidget->addTab(m_pgAdminMainTab, QString()); - m_tabWidget->setCurrentIndex(0); - m_tabWidget->setTabText(0, PGA_APP_NAME); - m_tabWidget->setTabToolTipText(0, PGA_APP_NAME) ; - - setCentralWidget(m_tabWidget); - -#ifdef PGADMIN4_USE_WEBENGINE - // Register the slot when click on the URL link for QWebEnginePage - connect(m_mainWebView->page(), SIGNAL(createTabWindow(QWebEnginePage * &)),SLOT(createNewTabWindow(QWebEnginePage * &))); -#else - // Register the slot when click on the URL link form main menu bar - connect(m_mainWebView, SIGNAL(linkClicked(const QUrl &)),SLOT(urlLinkClicked(const QUrl &))); - // Register the slot when click on the URL link for QWebPage - connect(m_mainWebView->page(), SIGNAL(createTabWindowKit(QWebPage * &)),SLOT(createNewTabWindowKit(QWebPage * &))); -#endif - - // Register the slot on tab index change - connect(m_tabWidget,SIGNAL(currentChanged(int )), m_tabWidget,SLOT(tabIndexChanged(int ))); - - // Listen for download file request from the web page -#ifdef PGADMIN4_USE_WEBENGINE - // Register downloadRequested signal of QWenEngineProfile to start download file to client side. - connect(m_mainWebView->page()->profile(),SIGNAL(downloadRequested(QWebEngineDownloadItem*)),this,SLOT(downloadRequested(QWebEngineDownloadItem*))); -#else - m_mainWebView->page()->setForwardUnsupportedContent(true); - connect(m_mainWebView->page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(download(const QNetworkRequest &))); - connect(m_mainWebView->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*))); - m_mainWebView->page()->setForwardUnsupportedContent(true); - m_mainWebView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); -#endif - - // Register the signal when base URL loading is finished. - connect(m_mainWebView, SIGNAL(loadFinished(bool )),this, SLOT(urlLoadingFinished(bool ))); - - // Restore the geometry, or set a nice default - QSettings settings; - - QSize availableSize = qApp->desktop()->availableGeometry().size(); - QSize defaultSize(availableSize.width() * 0.9, availableSize.height() * 0.9); - - QRect defaultGeometry = QStyle::alignedRect( - Qt::LeftToRight, - Qt::AlignCenter, - defaultSize, - qApp->desktop()->availableGeometry() - ); - - restoreGeometry(settings.value("Browser/Geometry", defaultGeometry).toByteArray()); - restoreState(settings.value("Browser/WindowState").toByteArray()); - - // Set the initial zoom - qreal zoom = settings.value("Browser/Zoom", m_mainWebView->zoomFactor()).toReal(); - m_mainWebView->setZoomFactor(zoom); - - // The last save location - m_last_open_folder_path = settings.value("Browser/LastSaveLocation", QDir::homePath()).toString(); - - // Display the app - m_mainWebView->setUrl(m_appServerUrl); -} - -// Destructor -BrowserWindow::~BrowserWindow() -{ - if (m_tabWidget) - delete m_tabWidget; -} - -// Save the window geometry on close -void BrowserWindow::closeEvent(QCloseEvent *event) -{ - if (QMessageBox::Yes == QMessageBox::question(this, "Exit the application?", "Are you sure you want to exit the application?", QMessageBox::Yes | QMessageBox::No)) - { - QSettings settings; - settings.setValue("Browser/Geometry", saveGeometry()); - settings.setValue("Browser/WindowState", saveState()); - QMainWindow::closeEvent(event); - event->accept(); - } - else - { - event->ignore(); - } -} - -#ifdef _WIN32 -// Set the message when change in registry value. -void BrowserWindow::setRegistryMessage(const QString &msg) -{ - m_regMessage = msg; -} - -QString BrowserWindow::getRegistryMessage() -{ - return m_regMessage; -} -#endif - -void BrowserWindow::urlLoadingFinished(bool res) -{ - if (res) - { -#ifdef _WIN32 - // Check if registry value is set by application then display information message to user. - // If message is empty string means no value set by application in registry. - QString message = getRegistryMessage(); - if (message != QString("")) - QMessageBox::information(this, tr("Registry change"), message); -#endif - } -} - -// Create the actions for the window -void BrowserWindow::createActions() -{ - // Open an arbitrary URL - openUrlShortcut = new QShortcut(QKeySequence(Qt::ALT + Qt::SHIFT + Qt::Key_U), this); - openUrlShortcut->setContext(Qt::ApplicationShortcut); - connect(openUrlShortcut, SIGNAL(activated()), this, SLOT(openUrl())); - - // Set the Python Path - preferencesShortcut = new QShortcut(QKeySequence(Qt::ALT + Qt::SHIFT + Qt::Key_P), this); - preferencesShortcut->setContext(Qt::ApplicationShortcut); - connect(preferencesShortcut, SIGNAL(activated()), this, SLOT(preferences())); - - // Exit the app - exitShortcut = new QShortcut(QKeySequence::Quit, this); - exitShortcut->setContext(Qt::ApplicationShortcut); - connect(exitShortcut, SIGNAL(activated()), this, SLOT(close())); - - signalMapper = new QSignalMapper(this); - - // About box - aboutShortcut = new QShortcut(QKeySequence(Qt::ALT + Qt::SHIFT + Qt::Key_A), this); - aboutShortcut->setContext(Qt::ApplicationShortcut); - connect(aboutShortcut, SIGNAL(activated()), this, SLOT(about())); - - // Zoom in - zoomInShortcut = new QShortcut(QKeySequence(QKeySequence::ZoomIn), this); - zoomInShortcut->setContext(Qt::ApplicationShortcut); - signalMapper->setMapping(zoomInShortcut, 1); - connect(zoomInShortcut, SIGNAL(activated()), signalMapper, SLOT(map())); - - // Zoom out - zoomOutShortcut = new QShortcut(QKeySequence(QKeySequence::ZoomOut), this); - zoomOutShortcut->setContext(Qt::ApplicationShortcut); - signalMapper->setMapping(zoomOutShortcut, -1); - connect(zoomOutShortcut, SIGNAL(activated()), signalMapper, SLOT(map())); - - // Reset Zoom - zoomResetShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_0), this); - zoomResetShortcut->setContext(Qt::ApplicationShortcut); - signalMapper->setMapping(zoomResetShortcut, 0); - connect(zoomResetShortcut, SIGNAL(activated()), signalMapper, SLOT(map())); - - connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setZoomLevel(int))); - - -#ifdef __APPLE__ - #ifdef PGADMIN4_USE_WEBENGINE - QShortcut *cut_shortcut = new QShortcut(QKeySequence("Ctrl+X"), this); - QObject::connect(cut_shortcut, SIGNAL(activated()), this, SLOT(onMacCut())); - - QShortcut *copy_shortcut = new QShortcut(QKeySequence("Ctrl+C"), this); - QObject::connect(copy_shortcut, SIGNAL(activated()), this, SLOT(onMacCopy())); - - QShortcut *paste_shortcut = new QShortcut(QKeySequence("Ctrl+V"), this); - QObject::connect(paste_shortcut, SIGNAL(activated()), this, SLOT(onMacPaste())); - #endif -#endif - -} - -#ifdef __APPLE__ - #ifdef PGADMIN4_USE_WEBENGINE -// Find current tab widget's webview widget and trigger the respective events of web page -void BrowserWindow::triggerWebViewWindowEvents(QWebEnginePage::WebAction action) -{ - WebViewWindow *webviewPtr = NULL; - - // Find current selected index from the view and set the cut/copy/paste events. - int index = m_tabWidget->currentIndex(); - - // If main web view window is pgAdmin then we should return from here after triggering events - if (index == 0) - { - m_mainWebView->triggerPageAction(action); - return; - } - - // if multiple webviews are opened then trigger cut/copy/paste events to respective webviews. - QWidget *tab = m_tabWidget->widget(index); - if (tab != NULL) - { - QList widgetList = tab->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - { - webviewPtr = dynamic_cast(widgetPtr); - - if (webviewPtr != NULL) - webviewPtr->triggerPageAction(action); - } - } - } -} - -// Trigger web page's cut event -void BrowserWindow::onMacCut() -{ - triggerWebViewWindowEvents(QWebEnginePage::Cut); -} - -// Trigger web page's copy event -void BrowserWindow::onMacCopy() -{ - triggerWebViewWindowEvents(QWebEnginePage::Copy); -} - -// Trigger web page's paste event -void BrowserWindow::onMacPaste() -{ - triggerWebViewWindowEvents(QWebEnginePage::Paste); -} - #endif -#endif - -// Check if Tab is already open with given URL name -int BrowserWindow::findURLTab(const QUrl &name) -{ - int tabCount = 0; - WebViewWindow *webviewPtr = NULL; - - for (tabCount = 1; tabCount < m_tabWidget->count(); tabCount++) - { - QWidget *tab = m_tabWidget->widget(tabCount); - if (tab != NULL) - { - QList widgetList = tab->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - { - webviewPtr = dynamic_cast(widgetPtr); - - if (webviewPtr != NULL && !QString::compare(webviewPtr->getFirstLoadURL(),name.host(), Qt::CaseInsensitive)) - { - m_tabWidget->setCurrentIndex(tabCount); - return 1; - } - } - } - } - } - - return 0; -} - -#ifdef PGADMIN4_USE_WEBENGINE -// Below slot will be called when user start download (Only for QWebEngine. Qt version >= 5.5) -void BrowserWindow::downloadRequested(QWebEngineDownloadItem *download) -{ - // Save the web engine download item state. it require when user cancel the download. - if (download != NULL) - m_download = download; - - // Extract filename and query from encoded URL - QUrlQuery query_data(download->url()); - QString file_name = query_data.queryItemValue("filename"); - QString query = query_data.queryItemValue("query"); - - if (m_downloadStarted) - { - // Inform user that download is already started - QMessageBox::information(this, tr("Download warning"), tr("File download already in progress: %1").arg(m_defaultFilename)); - return; - } - - // If encoded URL contains 'filename' attribute then use that filename in file dialog. - if (file_name.isEmpty() && query.isEmpty()) - m_defaultFilename = QFileInfo(download->url().toString()).fileName(); - else - m_defaultFilename = file_name; - - QFileDialog save_dialog(this); - save_dialog.setAcceptMode(QFileDialog::AcceptSave); - save_dialog.setWindowTitle(tr("Save file")); - save_dialog.setDirectory(m_last_open_folder_path); - save_dialog.selectFile(m_defaultFilename); - - QObject::connect(&save_dialog, SIGNAL(directoryEntered(const QString &)), this, SLOT(current_dir_path(const QString &))); - m_dir = m_last_open_folder_path; - QString fileName = ""; - QString f_name = ""; - - if (save_dialog.exec() == QDialog::Accepted) { - fileName = save_dialog.selectedFiles().first(); - f_name = fileName.replace(m_dir, ""); - // Remove the first character(/) from fiename - f_name.remove(0,1); - m_defaultFilename = f_name; - } - else - return; - - fileName = m_dir + fileName; - // Clear last open folder path - m_dir.clear(); - -#ifdef __APPLE__ - // Check that user has given valid file name or not - forward slash is not allowed in file name - // In Mac OSX, forward slash is converted to colon(:) by Qt so we need to check for colon. - if (f_name.indexOf(":") != -1) - { - QMessageBox::information(this, tr("File name error"), tr("Invalid file name")); - return; - } -#else - // Check that user has given valid file name or not - forward slash is not allowed in file name - if (f_name.indexOf("/") != -1) - { - QMessageBox::information(this, tr("File name error"), tr("Invalid file name")); - return; - } -#endif - - if (fileName.isEmpty()) - return; - else - { - m_downloadFilename = fileName; - if (download != NULL) - { - m_downloadStarted = 1; - m_downloadCancelled = 0; - connect(download, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadEngineFileProgress(qint64,qint64))); - connect(download, SIGNAL(finished()), this, SLOT(downloadEngineFinished())); - download->setPath(m_downloadFilename); - download->accept(); - } - } -} -#endif - -// Below slot will be called when user right click on download link and select "Save Link..." option from context menu -void BrowserWindow::download(const QNetworkRequest &request) -{ - // Check that request contains data for download at client side - QUrl name; - - if (m_downloadStarted) - { - // Inform user that a download is already started - QMessageBox::information(this, tr("Download warning"), tr("File download already in progress: %1").arg(m_defaultFilename)); - return; - } - - m_defaultFilename = QFileInfo(request.url().toString()).fileName(); - - // Open the dialog to save file - QFileDialog save_dialog(this); - save_dialog.setAcceptMode(QFileDialog::AcceptSave); - save_dialog.setWindowTitle(tr("Save file")); - save_dialog.setDirectory(m_last_open_folder_path); - save_dialog.selectFile(m_defaultFilename); - - // Register the slot for directory travesing when file dialog is opened and save the last open directory - QObject::connect(&save_dialog, SIGNAL(directoryEntered(const QString &)), this, SLOT(current_dir_path(const QString &))); - m_dir = m_last_open_folder_path; - QString fileName = ""; - QString f_name = ""; - - if (save_dialog.exec() == QDialog::Accepted) { - fileName = save_dialog.selectedFiles().first(); - f_name = fileName.replace(m_dir, ""); - - // Remove the first character(/) from fiename - f_name.remove(0,1); - m_defaultFilename = f_name; - } - else - return; - - fileName = m_dir + fileName; - // Clear the last open directory path - m_dir.clear(); - -#ifdef __APPLE__ - // Check that user has given valid file name or not - forward slash is not allowed in file name - // In Mac OSX, forward slash is converted to colon(:) by Qt so we need to check for colon. - if (f_name.indexOf(":") != -1) - { - QMessageBox::information(this, tr("File name error"), tr("Invalid file name")); - return; - } -#else - // Check that user has given valid file name or not - forward slash is not allowed in file name - if (f_name.indexOf("/") != -1) - { - QMessageBox::information(this, tr("File name error"), tr("Invalid file name")); - return; - } -#endif - - if (fileName.isEmpty()) - return; - else - { - m_downloadFilename = fileName; - - QNetworkRequest newRequest = request; - newRequest.setAttribute(QNetworkRequest::User, fileName); - - QObject *obj_web_page = QObject::sender(); - if (obj_web_page != NULL) - { -#ifdef PGADMIN4_USE_WEBENGINE - WebEnginePage *sender_web_page = dynamic_cast(obj_web_page); -#else - QWebPage *sender_web_page = dynamic_cast(obj_web_page); -#endif - if (sender_web_page != NULL) - { -#ifdef PGADMIN4_USE_WEBKIT - QNetworkAccessManager *networkManager = sender_web_page->networkAccessManager(); - QNetworkReply *reply = networkManager->get(newRequest); - if (reply != NULL) - { - m_downloadStarted = 1; - m_downloadCancelled = 0; - - // Download is started so open the file - if (!m_file) - { - if (!m_downloadFilename.isEmpty()) - { - m_file = new QFile(m_downloadFilename); - if (!m_file->open(QIODevice::WriteOnly)) - { - qDebug() << "Error opening file: " << m_downloadFilename; - m_downloadFilename.clear(); - m_defaultFilename.clear(); - m_downloadStarted = 0; - return; - } - - // Create progress bar dialog - m_progressDialog = new QProgressDialog (tr("Downloading file: %1 ").arg(m_defaultFilename), "Cancel", 0, 100, this); - m_progressDialog->setWindowModality(Qt::WindowModal); - m_progressDialog->setWindowTitle(tr("Download progress")); - m_progressDialog->setMinimumWidth(450); - m_progressDialog->setMinimumHeight(80); - m_progressDialog->setWindowFlags(Qt::Window | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint); - - // Register slot for file download cancel request - QObject::connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(progressCanceled())); - m_reply = reply; - - // Show downloading progress bar - m_progressDialog->show(); - } - } - - // Connect the signals for downloadProgress and downloadFinished - connect( reply, SIGNAL(readyRead()), this, SLOT(replyReady())); - connect( reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadFileProgress(qint64, qint64)) ); - connect( reply, SIGNAL(finished()), this, SLOT(downloadFinished())); - } -#endif - } - } - } -} - -#ifdef PGADMIN4_USE_WEBENGINE -// Below slot will be called when file download is in progress ( Only for QWebEngine Qt version >= 5.5 ) -void BrowserWindow::downloadEngineFileProgress(qint64 readData, qint64 totalData) -{ - // Check if progress dialog is already opened then only update the progress bar status - if (!m_progressDialog) - { - // Create progress bar dialog - m_progressDialog = new QProgressDialog (tr("Downloading file: %1 ").arg(m_defaultFilename), "Cancel", 0, totalData, this); - m_progressDialog->setWindowModality(Qt::WindowModal); - m_progressDialog->setWindowTitle(tr("Download progress")); - m_progressDialog->setMinimumWidth(450); - m_progressDialog->setMinimumHeight(80); - m_progressDialog->setWindowFlags(Qt::Window | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint); - - // Register slot for file download cancel request - QObject::connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(progressCanceled())); - - // Show downloading progress bar - m_progressDialog->show(); - } - else - m_progressDialog->setValue(readData); -} -#endif - -// Below slot will be called when data are available for download. -void BrowserWindow::replyReady() -{ - is_readyReadSignaled = true; - // When download is canceled by user then no need to write data to file - if (m_downloadCancelled) - return; - - // Write the data received from network to file. - if (m_reply != NULL && m_file != NULL) - { - QByteArray data= m_reply->readAll(); - int l_size = data.size(); - m_readBytes += (qint64)(l_size); - m_file->write(data); - // Calculate size in MB to be displayed in progress bar dialog. - if (m_progressDialog) { - qreal k_bytes = (((qreal)m_readBytes) / 1024); - qreal m_bytes = (k_bytes / 1024); - QString f_str = QString::number(m_bytes, 'f', 1); - QString set_str = QString("Downloaded ") + f_str + QString(" MB"); - m_progressDialog->setLabelText(set_str); - } - } -} - -// Below slot will be called when file download is in progress -void BrowserWindow::downloadFileProgress(qint64 readData, qint64 totalData) -{ - // When download is canceled by user then no need to write data to file - if (m_downloadCancelled) - return; - - if(m_reply != NULL && m_reply->error() != QNetworkReply::NoError) - { - qDebug() << "Network error occurred whilst downloading: " << m_defaultFilename; - return; - } - - if (m_file) - { - // Only update the status in progress bar as percentage. - if (!is_readyReadSignaled) - { - m_progressDialog->setRange(0, totalData); - m_progressDialog->setValue(readData); - } - - // Check if download is finished without readyRead signal then write the data. - if(m_reply && m_reply->isFinished() && !is_readyReadSignaled) - { - // Write data to file - m_file->write(m_reply->read(readData)); - is_readyReadSignaled = false; - - // As downloading is finished so remove progress bar dialog - if (m_progressDialog) - { - m_progressDialog->deleteLater(); - m_progressDialog = NULL; - } - - m_downloadStarted = 0; - m_downloadFilename.clear(); - m_defaultFilename.clear(); - m_downloadCancelled = 0; - - if (m_file) - { - m_file->close(); - delete m_file; - m_file = NULL; - } - - if (m_reply) - m_reply = NULL; - } - - if(m_reply && m_reply->isFinished() && readData == totalData) - m_readBytes = 0; - } -} - -// Below slot will be called when user cancel the downloading file which is in progress. -void BrowserWindow::progressCanceled() -{ - m_downloadCancelled = 1; - - if (m_progressDialog) - { - m_progressDialog->deleteLater(); - m_progressDialog = NULL; - } - -#ifdef PGADMIN4_USE_WEBKIT - if (m_file) - { - m_file->close(); - // Remove the file from file system as downloading is canceled by user - m_file->remove(); - delete m_file; - m_file = NULL; - } - - if (m_reply) - { - m_reply->abort(); - m_reply = NULL; - } -#else - m_download->cancel(); -#endif - - m_downloadFilename.clear(); - m_defaultFilename.clear(); - m_downloadStarted = 0; - is_readyReadSignaled = false; - m_readBytes = 0; -} - -#ifdef PGADMIN4_USE_WEBENGINE -// Below slot will called when file download is finished (Only for QWebEngine) -void BrowserWindow::downloadEngineFinished() -{ - // Check download finished state. - if (m_download) - { - QWebEngineDownloadItem::DownloadState state = m_download->state(); - - switch (state) - { - case QWebEngineDownloadItem::DownloadRequested: - case QWebEngineDownloadItem::DownloadInProgress: - Q_UNREACHABLE(); - break; - case QWebEngineDownloadItem::DownloadCompleted: - case QWebEngineDownloadItem::DownloadCancelled: - case QWebEngineDownloadItem::DownloadInterrupted: - m_download = NULL; - break; - } - } - - if (m_progressDialog) - { - m_progressDialog->deleteLater(); - m_progressDialog = NULL; - } - - m_downloadFilename.clear(); - m_defaultFilename.clear(); - m_downloadStarted = 0; - m_downloadCancelled = 0; -} -#endif - -// Below slot will called when file download is finished -void BrowserWindow::downloadFinished() -{ - if (m_progressDialog) - { - m_progressDialog->deleteLater(); - m_progressDialog = NULL; - } - - m_downloadFilename.clear(); - m_defaultFilename.clear(); - m_downloadStarted = 0; - m_downloadCancelled = 0; - is_readyReadSignaled = false; - m_readBytes = 0; - - if (m_file) - { - m_file->close(); - delete m_file; - m_file = NULL; - } - - if (m_reply) - m_reply = NULL; -} - -// Below slot will be called when user directly click on any download link -void BrowserWindow::unsupportedContent(QNetworkReply * reply) -{ -#if QT_VERSION >= 0x050000 - // Extract filename and query from encoded URL - QUrlQuery query_data(reply->url()); - QString file_name = query_data.queryItemValue("filename"); - QString query = query_data.queryItemValue("query"); -#else - QUrl url(reply->url()); - QString file_name = url.queryItemValue("filename"); - QString query = url.queryItemValue("query"); -#endif - - if (m_downloadStarted) - { - // Inform user that download is already started - QMessageBox::information(this, tr("Download warning"), tr("File download already in progress: %1").arg(m_defaultFilename)); - return; - } - - // If encoded URL contains 'filename' attribute then use that filename in file dialog. - if (file_name.isEmpty() && query.isEmpty()) - m_defaultFilename = QFileInfo(reply->url().toString()).fileName(); - else - m_defaultFilename = file_name; - - QFileDialog save_dialog(this); - save_dialog.setAcceptMode(QFileDialog::AcceptSave); - save_dialog.setWindowTitle(tr("Save file")); - save_dialog.setDirectory(m_last_open_folder_path); - save_dialog.selectFile(m_defaultFilename); - - QObject::connect(&save_dialog, SIGNAL(directoryEntered(const QString &)), this, SLOT(current_dir_path(const QString &))); - m_dir = m_last_open_folder_path; - QString fileName = ""; - QString f_name = ""; - - if (save_dialog.exec() == QDialog::Accepted) { - fileName = save_dialog.selectedFiles().first(); - f_name = fileName.replace(m_dir, ""); - // Remove the first character(/) from fiename - f_name.remove(0,1); - m_defaultFilename = f_name; - } - else - return; - - fileName = m_dir + fileName; - // Clear last open folder path - m_dir.clear(); - -#ifdef __APPLE__ - // Check that user has given valid file name or not - forward slash is not allowed in file name - // In Mac OSX, forward slash is converted to colon(:) by Qt so we need to check for colon. - if (f_name.indexOf(":") != -1) - { - QMessageBox::information(this, tr("File name error"), tr("Invalid file name")); - return; - } -#else - // Check that user has given valid file name or not - forward slash is not allowed in file name - if (f_name.indexOf("/") != -1) - { - QMessageBox::information(this, tr("File name error"), tr("Invalid file name")); - return; - } -#endif - - if (fileName.isEmpty()) - return; - else - { - m_downloadFilename = fileName; - if (reply != NULL) - { - m_downloadStarted = 1; - m_downloadCancelled = 0; - - // Download is started so open the file - if (!m_file) - { - if (!m_downloadFilename.isEmpty()) - { - m_file = new QFile(m_downloadFilename); - if (!m_file->open(QIODevice::WriteOnly)) - { - qDebug() << "Error opening file: " << m_downloadFilename; - m_downloadFilename.clear(); - m_defaultFilename.clear(); - m_downloadStarted = 0; - return; - } - - // Create progress bar dialog - m_progressDialog = new QProgressDialog (tr("Downloading file: %1 ").arg(m_defaultFilename), "Cancel", 0, 100, this); - m_progressDialog->setWindowModality(Qt::WindowModal); - m_progressDialog->setWindowTitle(tr("Download progress")); - m_progressDialog->setMinimumWidth(450); - m_progressDialog->setMinimumHeight(80); - m_progressDialog->setWindowFlags(Qt::Window | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint); - - // Register slot for file download cancel request - QObject::connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(progressCanceled())); - m_reply = reply; - - // Show downloading progress bar - m_progressDialog->show(); - } - } - - connect( reply, SIGNAL(readyRead()), this, SLOT(replyReady())); - connect( reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadFileProgress(qint64, qint64))); - connect( reply, SIGNAL(finished()), this, SLOT(downloadFinished())); - } - } -} - -// Slot: set the title of tab when the new tab created or existing tab contents changed -void BrowserWindow::tabTitleChanged(const QString &str) -{ - WebViewWindow *nextWebViewPtr = NULL; - bool flagTabText = false; - QToolButton *backToolButton = NULL; - QToolButton *forwardToolButton = NULL; - - if (!str.isEmpty()) - { - QObject *senderPtr = QObject::sender(); - WebViewWindow *webViewPtr = NULL; - if (senderPtr != NULL) - { - webViewPtr = dynamic_cast(senderPtr); - if (webViewPtr != NULL) - { - DockTabWidget *dock_tab_widget = dynamic_cast(webViewPtr->parent()->parent()->parent()); - if (dock_tab_widget != NULL) - { - for (int loopCount = dock_tab_widget->count();loopCount >= 0;loopCount--) - { - QWidget *tab = dock_tab_widget->widget(loopCount); - if (tab != NULL) - { - QList widgetList = tab->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - nextWebViewPtr = dynamic_cast(widgetPtr); - - if (nextWebViewPtr != NULL && nextWebViewPtr == webViewPtr) - { - // If tab title is for Query tool then we should hide tool buttons. - QWidget *tab = dock_tab_widget->tabBar()->tabButton(loopCount, QTabBar::LeftSide); - if (tab != NULL) - { - QList widgetList = tab->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - { - QToolButton *toolBtnPtr = dynamic_cast(widgetPtr); - if (toolBtnPtr != NULL) - { - if (!QString::compare(toolBtnPtr->toolTip(), tr("Go back"), Qt::CaseInsensitive)) - backToolButton = toolBtnPtr; - if (!QString::compare(toolBtnPtr->toolTip(), tr("Go forward"), Qt::CaseInsensitive)) - forwardToolButton = toolBtnPtr; - } - } - } - } - - if (backToolButton != NULL && forwardToolButton != NULL) - { - if (!str.startsWith("Query -")) - { - if (str.startsWith("Debugger")) - { - backToolButton->hide(); - forwardToolButton->hide(); - webViewPtr->setBackForwardButtonHidden(true); - } - // 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. - else if (!webViewPtr->getBackForwardButtonHidden()) - { - backToolButton->show(); - forwardToolButton->show(); - webViewPtr->setBackForwardButtonHidden(false); - } - } - else - { - backToolButton->hide(); - forwardToolButton->hide(); - webViewPtr->setBackForwardButtonHidden(true); - } - } - - dock_tab_widget->setTabText(loopCount, str); - dock_tab_widget->setTabToolTipText(loopCount, str); - dock_tab_widget->enableDisableToolButton(loopCount); - flagTabText = true; - break; - } - } - } - - if (flagTabText) - break; - } - - if (!flagTabText) - { - dock_tab_widget->setTabText(dock_tab_widget->currentIndex(), str); - dock_tab_widget->setTabToolTipText(dock_tab_widget->currentIndex(), str); - dock_tab_widget->enableDisableToolButton(dock_tab_widget->currentIndex()); - } - } - } - } - } -} - - -void BrowserWindow::current_dir_path(const QString &dir) -{ - m_dir = dir; - m_last_open_folder_path = dir; - - QSettings settings; - settings.setValue("Browser/LastSaveLocation", m_last_open_folder_path); -} - -#ifndef PGADMIN4_USE_WEBENGINE -void BrowserWindow::createNewTabWindowKit(QWebPage * &p) -{ - m_addNewTab = new QWidget(m_tabWidget); - - m_addNewGridLayout = new QGridLayout(m_addNewTab); - m_addNewGridLayout->setContentsMargins(0, 0, 0, 0); - - m_addNewWebView = new WebViewWindow(m_addNewTab); - m_addNewWebView->setPage(new WebViewPage()); - m_addNewWebView->setZoomFactor(m_mainWebView->zoomFactor()); - - // Register the slot when click on the URL link form main menu bar - connect(m_addNewWebView, SIGNAL(linkClicked(const QUrl &)),SLOT(urlLinkClicked(const QUrl &))); - // Register the slot when click on the URL link for QWebPage - connect(m_addNewWebView->page(), SIGNAL(createTabWindowKit(QWebPage * &)),SLOT(createNewTabWindowKit(QWebPage * &))); - - m_addNewWebView->page()->setForwardUnsupportedContent(true); - connect(m_addNewWebView->page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(download(const QNetworkRequest &))); - connect(m_addNewWebView->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*))); - m_addNewWebView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); - - m_addNewWebView->settings()->setAttribute(QWebSettings::JavascriptEnabled, true); - m_addNewWebView->settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true); - - m_widget = new QWidget(m_addNewTab); - - 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")); - m_toolBtnBack->hide(); - - 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")); - m_toolBtnForward->hide(); - - 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")); - - 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 titleChange so set the tab text accordingly - connect(m_addNewWebView, SIGNAL(titleChanged(const QString &)), SLOT(tabTitleChanged(const QString &))); - - // Register the slot on toolbutton to show the previous history of web - connect(m_toolBtnBack, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoBackPage())); - - // Register the slot on toolbutton to show the next history of web - connect(m_toolBtnForward, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoForwardPage())); - - // Register the slot on close button , added manually - connect(m_btnClose, SIGNAL(clicked()), m_tabWidget, SLOT(dockClosetabs())); - - m_addNewGridLayout->addWidget(m_addNewWebView, 0, 0, 1, 1); - m_tabWidget->addTab(m_addNewTab, QString()); - m_tabWidget->tabBar()->setVisible(true); - m_tabWidget->setCurrentIndex((m_tabWidget->count() - 1)); - - // Set the back and forward button on tab - m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::LeftSide, m_widget); - m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::RightSide, m_btnClose); - - m_addNewWebView->setTabIndex((m_tabWidget->count() - 1)); - m_addNewWebView->page()->setNetworkAccessManager(m_netAccessMan); - p = m_addNewWebView->page(); -} -#endif - -#ifdef PGADMIN4_USE_WEBENGINE -// Below slot will be called when link is required to open in new tab. -void BrowserWindow::createNewTabWindow(QWebEnginePage * &p) -{ - m_addNewTab = new QWidget(m_tabWidget); - - m_addNewGridLayout = new QGridLayout(m_addNewTab); - m_addNewGridLayout->setContentsMargins(0, 0, 0, 0); - - m_addNewWebView = new WebViewWindow(); - m_addNewWebView->setPage(new WebEnginePage()); - m_addNewWebView->setZoomFactor(m_mainWebView->zoomFactor()); - - m_widget = new QWidget(m_addNewTab); - - 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")); - m_toolBtnBack->hide(); - - 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")); - m_toolBtnForward->hide(); - - m_toolBtnClose = new QToolButton(m_widget); - m_toolBtnClose->setFixedHeight(PGA_BTN_SIZE); - m_toolBtnClose->setFixedWidth(PGA_BTN_SIZE); - m_toolBtnClose->setIcon(QIcon(":/close.png")); - m_toolBtnClose->setToolTip(tr("Close tab")); - - 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 titleChange so set the tab text accordingly - connect(m_addNewWebView, SIGNAL(titleChanged(const QString &)), SLOT(tabTitleChanged(const QString &))); - - // Register the slot on toolbutton to show the previous history of web - connect(m_toolBtnBack, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoBackPage())); - - // Register the slot on toolbutton to show the next history of web - connect(m_toolBtnForward, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoForwardPage())); - - // Register the slot on close button , added manually - connect(m_toolBtnClose, SIGNAL(clicked()), m_tabWidget, SLOT(dockClosetabs())); - - m_addNewGridLayout->addWidget(m_addNewWebView, 0, 0, 1, 1); - m_tabWidget->addTab(m_addNewTab, QString()); - m_tabWidget->tabBar()->setVisible(true); - m_tabWidget->setCurrentIndex((m_tabWidget->count() - 1)); - - // Set the back and forward button on tab - m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::LeftSide, m_widget); - m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::RightSide, m_toolBtnClose); - - m_addNewWebView->setTabIndex((m_tabWidget->count() - 1)); - p = m_addNewWebView->page(); -} -#endif - -// Slot: Link is open from pgAdmin mainwindow -void BrowserWindow::urlLinkClicked(const QUrl &name) -{ - // Check that request contains the data download at client side - QNetworkRequest request; - - // First check is there any tab opened with same URL then open it again. - int tabFound = findURLTab(name); - - if (!tabFound) - { - m_addNewTab = new QWidget(m_tabWidget); - - m_addNewGridLayout = new QGridLayout(m_addNewTab); - m_addNewGridLayout->setContentsMargins(0, 0, 0, 0); - - m_addNewWebView = new WebViewWindow(); - m_addNewWebView->setZoomFactor(m_mainWebView->zoomFactor()); - - // Listen for the download request from the web page -#ifdef PGADMIN4_USE_WEBENGINE - m_addNewWebView->setPage(new WebEnginePage()); - connect(m_addNewWebView->page()->profile(),SIGNAL(downloadRequested(QWebEngineDownloadItem*)),this,SLOT(downloadRequested(QWebEngineDownloadItem*))); -#else - m_addNewWebView->setPage(new WebViewPage()); - m_addNewWebView->page()->setForwardUnsupportedContent(true); - connect(m_addNewWebView->page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(download(const QNetworkRequest &))); - connect(m_addNewWebView->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*))); - - // Register the slot when click on the URL link form main menu bar - connect(m_addNewWebView, SIGNAL(linkClicked(const QUrl &)),SLOT(urlLinkClicked(const QUrl &))); - // Register the slot when click on the URL link for QWebPage - connect(m_addNewWebView->page(), SIGNAL(createTabWindowKit(QWebPage * &)),SLOT(createNewTabWindowKit(QWebPage * &))); - - m_addNewWebView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); -#endif - - m_widget = new QWidget(m_addNewTab); - - 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")); - m_toolBtnBack->hide(); - - 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")); - m_toolBtnForward->hide(); - - m_toolBtnClose = new QToolButton(m_widget); - m_toolBtnClose->setFixedHeight(PGA_BTN_SIZE); - m_toolBtnClose->setFixedWidth(PGA_BTN_SIZE); - m_toolBtnClose->setIcon(QIcon(":/close.png")); - m_toolBtnClose->setToolTip(tr("Close tab")); - - 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 titleChange so set the tab text accordingly - connect(m_addNewWebView, SIGNAL(titleChanged(const QString &)), SLOT(tabTitleChanged(const QString &))); - - // Register the slot on toolbutton to show the previous history of web - connect(m_toolBtnBack, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoBackPage())); - - // Register the slot on toolbutton to show the next history of web - connect(m_toolBtnForward, SIGNAL(clicked()), m_tabWidget, SLOT(dockGoForwardPage())); - - // Register the slot on close button , added manually - connect(m_toolBtnClose, SIGNAL(clicked()), m_tabWidget, SLOT(dockClosetabs())); - - m_addNewGridLayout->addWidget(m_addNewWebView, 0, 0, 1, 1); - m_tabWidget->addTab(m_addNewTab, QString()); - m_tabWidget->tabBar()->setVisible(true); - m_tabWidget->setCurrentIndex((m_tabWidget->count() - 1)); - - // Set the back and forward button on tab - m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::LeftSide, m_widget); - m_tabWidget->tabBar()->setTabButton((m_tabWidget->count() - 1), QTabBar::RightSide, m_toolBtnClose); - - m_addNewWebView->setFirstLoadURL(name.host()); - m_addNewWebView->setTabIndex((m_tabWidget->count() - 1)); - m_addNewWebView->setUrl(name); - } -} - -// Pause for n seconds, without freezing the UI. -void BrowserWindow::pause(int seconds) -{ - QTime dieTime = QTime::currentTime().addSecs(seconds); - - while (QTime::currentTime() < dieTime) - QCoreApplication::processEvents(QEventLoop::AllEvents, 100); -} - -// Display the about box -void BrowserWindow::about() -{ - QMessageBox::about(this, tr("About %1").arg(PGA_APP_NAME), tr("%1 - PostgreSQL Tools").arg(PGA_APP_NAME)); -} - - -// Set Zoom Level -void BrowserWindow::setZoomLevel(int zoomFlag) -{ - int tabCount = 0; - WebViewWindow *webviewPtr = NULL; - - // Loop through all the tabs - for (tabCount = 0; tabCount < m_tabWidget->count(); tabCount++) - { - QWidget *tab = m_tabWidget->widget(tabCount); - if (tab != NULL) - { - // Find and loop through any child controls - QList widgetList = tab->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - { - // If it's a web view control, set the zoom level based on the main view - webviewPtr = dynamic_cast(widgetPtr); - - if (webviewPtr != NULL) - { - if (zoomFlag == 1) { - webviewPtr->setZoomFactor(m_mainWebView->zoomFactor() + 0.1); - } - else if (zoomFlag == -1) { - webviewPtr->setZoomFactor(m_mainWebView->zoomFactor() - 0.1); - } - else if(zoomFlag == 0) { - webviewPtr->setZoomFactor(1.0); - } - } - } - } - } - } - - // Set the zoom value for the next time - QSettings settings; - settings.setValue("Browser/Zoom", m_mainWebView->zoomFactor()); - -} - -// Open an arbitrary URL -void BrowserWindow::openUrl() -{ - bool ok; - - QInputDialog *dlg = new QInputDialog(); - dlg->setInputMode(QInputDialog::TextInput); - dlg->setWindowTitle(QWidget::tr("Open URL")); - dlg->setLabelText(QWidget::tr("Enter a URL")); - dlg->setTextValue("http://"); - dlg->resize(600,100); - - ok = dlg->exec(); - - QString url = dlg->textValue(); - - if (ok && !url.isEmpty()) - urlLinkClicked(QUrl(url)); -} - -// Edit the app configuration -void BrowserWindow::preferences() -{ - QSettings settings; - bool ok; - - ConfigWindow *dlg = new ConfigWindow(); - dlg->setWindowTitle(QWidget::tr("Configuration")); - dlg->setPythonPath(settings.value("PythonPath").toString()); - dlg->setApplicationPath(settings.value("ApplicationPath").toString()); - dlg->setModal(true); - ok = dlg->exec(); - - QString pythonpath = dlg->getPythonPath(); - QString applicationpath = dlg->getApplicationPath(); - - if (ok) - { - settings.setValue("PythonPath", pythonpath); - settings.setValue("ApplicationPath", applicationpath); - } -} - diff --git a/runtime/BrowserWindow.h b/runtime/BrowserWindow.h deleted file mode 100644 index 60dfb8d15..000000000 --- a/runtime/BrowserWindow.h +++ /dev/null @@ -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 - - #ifdef PGADMIN4_USE_WEBENGINE - #include - #else - #include - #include - #include - #endif -#else - #include - - #ifdef PGADMIN4_USE_WEBENGINE - #include - #else - #include - #include - #include - #endif -#endif - -#ifdef PGADMIN4_USE_WEBENGINE - #include -#else - #include -#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 diff --git a/runtime/BrowserWindow.ui b/runtime/BrowserWindow.ui deleted file mode 100644 index 65172f18a..000000000 --- a/runtime/BrowserWindow.ui +++ /dev/null @@ -1,37 +0,0 @@ - - - Form - - - - 0 - 0 - 911 - 688 - - - - Form - - - - - - - about:blank - - - - - - - - - QWebView - QWidget -
QtWebKitWidgets/QWebView
-
-
- - -
diff --git a/runtime/ConfigWindow.cpp b/runtime/ConfigWindow.cpp index e0c39a672..d423d1fea 100644 --- a/runtime/ConfigWindow.cpp +++ b/runtime/ConfigWindow.cpp @@ -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); diff --git a/runtime/ConfigWindow.h b/runtime/ConfigWindow.h index 00503d092..2e5251771 100644 --- a/runtime/ConfigWindow.h +++ b/runtime/ConfigWindow.h @@ -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 diff --git a/runtime/ConfigWindow.ui b/runtime/ConfigWindow.ui index 40d87be43..db180b210 100644 --- a/runtime/ConfigWindow.ui +++ b/runtime/ConfigWindow.ui @@ -6,49 +6,211 @@ 0 0 - 608 - 118 + 625 + 300 - + 0 0 + + + 625 + 300 + + Dialog - - - QLayout::SetDefaultConstraint + + + 0 - - QFormLayout::ExpandingFieldsGrow - - - - - Python Path - - - - - - - - - - Application Path - - - - - - - + + + Runtime + + + + + + QLayout::SetMaximumSize + + + + + + 0 + 0 + + + + + 589 + 16777215 + + + + 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. + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + QLayout::SetMaximumSize + + + + + + 0 + 0 + + + + Browser Command + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Python + + + + + + + + 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! + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + Enter the path to the directory containing pgAdmin.py if desired. + + + + + + + Python Path + + + + + + + + + + Enter a PYTHONPATH if desired. Path elements should be semi-colon delimited. + + + true + + + + + + + Application Path + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/runtime/Info.plist b/runtime/Info.plist index 8d57f884c..b14f5ef74 100644 --- a/runtime/Info.plist +++ b/runtime/Info.plist @@ -29,5 +29,7 @@ org.pgadmin.@EXECUTABLE@ NSPrincipalClass NSApplication + LSUIElement + 1 diff --git a/runtime/LogWindow.cpp b/runtime/LogWindow.cpp new file mode 100644 index 000000000..09f0b3cf0 --- /dev/null +++ b/runtime/LogWindow.cpp @@ -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 + + +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(); +} diff --git a/runtime/LogWindow.h b/runtime/LogWindow.h new file mode 100644 index 000000000..2df552400 --- /dev/null +++ b/runtime/LogWindow.h @@ -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 + +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 diff --git a/runtime/LogWindow.ui b/runtime/LogWindow.ui new file mode 100644 index 000000000..e5ae71e78 --- /dev/null +++ b/runtime/LogWindow.ui @@ -0,0 +1,106 @@ + + + LogWindow + + + + 0 + 0 + 800 + 500 + + + + Dialog + + + + + + + Courier + + + + true + + + + + + false + + + + + + + + + Reload + + + + + + + + 0 + 0 + + + + + + + + + + + Close + + + + + + + + + + + btnReload + clicked() + LogWindow + reload() + + + 53 + 471 + + + 399 + 249 + + + + + btnClose + clicked() + LogWindow + close() + + + 731 + 471 + + + 399 + 249 + + + + + + reload() + + diff --git a/runtime/Server.cpp b/runtime/Server.cpp index 30550b323..5fb922e9a 100644 --- a/runtime/Server.cpp +++ b/runtime/Server.cpp @@ -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); } + diff --git a/runtime/Server.h b/runtime/Server.h index f89ac028e..97e5ca3a3 100644 --- a/runtime/Server.h +++ b/runtime/Server.h @@ -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; diff --git a/runtime/TabWindow.cpp b/runtime/TabWindow.cpp deleted file mode 100644 index 3ada71fc7..000000000 --- a/runtime/TabWindow.cpp +++ /dev/null @@ -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 -#else -#include -#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 widgetList = tab->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - { - webviewPtr = dynamic_cast(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 widgetList = tab->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - { - webviewPtr = dynamic_cast(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(senderPtr); - if (btn != NULL) - { - l_tab_widget = dynamic_cast(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(l_tab); - if (nextBtnPtr != NULL && btn != NULL && nextBtnPtr == btn) - current_tab_index = loopCount; - } - } - - QList widgetList = l_tab_widget->tabBar()->findChildren(); - foreach(QWidget* widgetPtr, widgetList) - { - if (widgetPtr != NULL) - { - QToolButton *toolBtnPtr = dynamic_cast(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 widgetList = tab->findChildren(); - foreach (QWidget* widgetPtr, widgetList) - { - if (widgetPtr != NULL) - { - webviewPtr = dynamic_cast(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::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 widgetList = tab->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - { - WebViewWindow *webViewPtr = dynamic_cast(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(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(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 widgetList = tab1->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - tmpwebviewPtr = dynamic_cast(widgetPtr); - - if (tmpwebviewPtr != NULL) - webviewPtr = tmpwebviewPtr; - } - } - - QWidget *tab = tabBar()->tabButton(index, QTabBar::LeftSide); - if (tab != NULL) - { - QList widgetList = tab->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - { - toolBtnPtr = dynamic_cast(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 widgetList = tab->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - { - webViewPtr = dynamic_cast(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 widgetList = tab->findChildren(); - foreach( QWidget* widgetPtr, widgetList ) - { - if (widgetPtr != NULL) - { - toolBtnPtr = dynamic_cast(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(); - } -} diff --git a/runtime/TabWindow.h b/runtime/TabWindow.h deleted file mode 100644 index 60bf841e6..000000000 --- a/runtime/TabWindow.h +++ /dev/null @@ -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 -#include - -#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(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 diff --git a/runtime/TrayIcon.cpp b/runtime/TrayIcon.cpp new file mode 100644 index 000000000..aefc58125 --- /dev/null +++ b/runtime/TrayIcon.cpp @@ -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 + +// 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); + } +} diff --git a/runtime/TrayIcon.h b/runtime/TrayIcon.h new file mode 100644 index 000000000..f6538123d --- /dev/null +++ b/runtime/TrayIcon.h @@ -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 +#include + +// 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 diff --git a/runtime/WebViewWindow.cpp b/runtime/WebViewWindow.cpp deleted file mode 100644 index 3b56a5de3..000000000 --- a/runtime/WebViewWindow.cpp +++ /dev/null @@ -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 -#include -#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(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 diff --git a/runtime/WebViewWindow.h b/runtime/WebViewWindow.h deleted file mode 100644 index 6014e6337..000000000 --- a/runtime/WebViewWindow.h +++ /dev/null @@ -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 -#else -#include -#endif -#else -#include -#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 diff --git a/runtime/pgAdmin4-mac.png b/runtime/pgAdmin4-mac.png new file mode 100644 index 0000000000000000000000000000000000000000..0fa06be1409600931ba6ed5480c84a8036785520 GIT binary patch literal 81551 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�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 #include #include -#include #endif // App headers -#include "BrowserWindow.h" #include "ConfigWindow.h" #include "Server.h" +#include "TrayIcon.h" #include -// Implement support for system proxies for Qt 4.x on Linux -#if defined (Q_OS_LINUX) && QT_VERSION < 0x050000 - -#include "qnetworkproxy.h" - -#include -#include - -#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 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 pgAdminSystemProxyForQuery(const QNetworkProxyQuery &query) -{ - QList 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 queryProxy(const QNetworkProxyQuery& query) - { - QList 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 ca_list = sslCfg.caCertificates(); - QList 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; +} diff --git a/runtime/pgAdmin4.h b/runtime/pgAdmin4.h index 8a601f8be..9d7135f26 100644 --- a/runtime/pgAdmin4.h +++ b/runtime/pgAdmin4.h @@ -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 diff --git a/runtime/pgAdmin4.png b/runtime/pgAdmin4.png new file mode 100644 index 0000000000000000000000000000000000000000..83926278bf4a6d438d0e6a5b858c7386ea9ccce0 GIT binary patch literal 99482 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

8|0Vi2snfbu%>@RF8=z<8k#@56GV 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 literal 0 HcmV?d00001 diff --git a/runtime/pgAdmin4.pro b/runtime/pgAdmin4.pro index e3ecf4d66..9284077bf 100644 --- a/runtime/pgAdmin4.pro +++ b/runtime/pgAdmin4.pro @@ -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 diff --git a/runtime/pgAdmin4.qrc b/runtime/pgAdmin4.qrc index 4471059b9..19d5ccc9b 100644 --- a/runtime/pgAdmin4.qrc +++ b/runtime/pgAdmin4.qrc @@ -5,6 +5,7 @@ forward.png close.png splash.png - qss/pgadmin4.qss + pgAdmin4.png + pgAdmin4-mac.png diff --git a/runtime/qss/pgadmin4.qss b/runtime/qss/pgadmin4.qss deleted file mode 100644 index fae5c0330..000000000 --- a/runtime/qss/pgadmin4.qss +++ /dev/null @@ -1,3 +0,0 @@ -QTabBar::tab { - background-color: #E8E8E8; -}