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 000000000..0fa06be14 Binary files /dev/null and b/runtime/pgAdmin4-mac.png differ diff --git a/runtime/pgAdmin4.cpp b/runtime/pgAdmin4.cpp index e121166b0..89e119e00 100644 --- a/runtime/pgAdmin4.cpp +++ b/runtime/pgAdmin4.cpp @@ -28,153 +28,22 @@ #include #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 000000000..83926278b Binary files /dev/null and b/runtime/pgAdmin4.png differ 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; -}