From 8e348455f9a26159bc403f16a1d781047c595e26 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 24 Sep 2015 22:17:10 +0200 Subject: [PATCH 01/49] Added appveyor script --- CMakeLists.txt | 6 ++-- appveyor.yml | 78 +++++++++++++++++++++++++++++++++++++++++++ oracle/CMakeLists.txt | 5 ++- 3 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 appveyor.yml diff --git a/CMakeLists.txt b/CMakeLists.txt index c4d239fe..3937321e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ endif() IF(MSVC) # Visual Studio: # Maximum optimization - set(CMAKE_CXX_FLAGS_RELEASE "/Ox") + set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD") # Generate complete debugging information #set(CMAKE_CXX_FLAGS_DEBUG "/Zi") ELSEIF (CMAKE_COMPILER_IS_GNUCXX) @@ -104,7 +104,7 @@ ENDIF() # Default is Qt5 unless WITH_QT4 option is enabled option(WITH_QT4 "Force the use of Qt4 libraries" OFF) OPTION(UPDATE_TRANSLATIONS "Update translations on compile" OFF) -MESSAGE("UPDATE TRANSLATIONS: ${UPDATE_TRANSLATIONS}") +MESSAGE(STATUS "UPDATE TRANSLATIONS: ${UPDATE_TRANSLATIONS}") IF(NOT WITH_QT4) # First known not-broken Qt5 version (5.0.2 available on old ubuntus is buggy). @@ -197,7 +197,7 @@ if(UNIX) endif() elseif(WIN32) set(CPACK_GENERATOR NSIS ${CPACK_GENERATOR}) - set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_win32_git-${PROJECT_VERSION_SAFE}") + set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_git-${PROJECT_VERSION_SAFE}") # Configure file with custom definitions for NSIS. configure_file( diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..c6d3608f --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,78 @@ +version: 0.0.0.0.1-branch-{branch}-build-{build} +cache: + - c:\protobuf + - c:\protoc + - c:\zlib +environment: + matrix: + - vc_arch: amd64 + choco_arch: + nuget_arch: x64 + target_arch: x86_64 + qt_ver: 5.5\msvc2013_64 + - vc_arch: amd64_x86 # cross-compile from amd64 to x86 + choco_arch: --x86 + nuget_arch: Win32 + target_arch: x86 + qt_ver: 5.5\msvc2013 +install: + - systeminfo + # upgrade cmake in order to have c++11 support + - choco install cmake -y + - choco install nsis -y + - choco install jom -y + - ps: | + if (Test-Path c:\protoc) { + echo "using protoc from cache" + } else { + Invoke-WebRequest "https://github.com/google/protobuf/releases/download/v2.6.1/protoc-2.6.1-win32.zip" -OutFile c:\protoc-2.6.1-win32.zip + c:\cygwin\bin\bash -lc "cd /cygdrive/c; 7z x -y protoc-2.6.1-win32.zip -oc:\protoc" + } + - ps: | + if (Test-Path c:\protobuf) { + echo "using protobuf from cache" + } else { + nuget install protobuf-v120 -OutputDirectory c:\protobuf + } + - ps: | + if (Test-Path c:\zlib) { + echo "using zlib from cache" + } else { + nuget install zlib -OutputDirectory c:\zlib + } +build_script: + - mkdir build + - cd build + - '"c:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/vcvarsall" %vc_arch%' + - path + - ps: | + $zlibinc = c:\cygwin\bin\find /cygdrive/c/zlib/ -path '*v120*/zlib.h' + $zlibinc = c:\cygwin\bin\dirname $zlibinc + $zlibinc = c:\cygwin\bin\cygpath -m $zlibinc + $zliblib = c:\cygwin\bin\find /cygdrive/c/zlib/ -path "*v120*/$env:nuget_arch/Release/zlib.lib" + $zliblib = c:\cygwin\bin\cygpath -m $zliblib + $protoinc = c:\cygwin\bin\find /cygdrive/c/protobuf/ -name 'google' + $protoinc = c:\cygwin\bin\dirname $protoinc + $protoinc = c:\cygwin\bin\cygpath -m $protoinc + $protolib = c:\cygwin\bin\find /cygdrive/c/protobuf/ -path "*/lib/$env:nuget_arch/v120/Release/libprotobuf.lib" + $protolib = c:\cygwin\bin\cygpath -m $protolib + $protoc = c:\cygwin\bin\find /cygdrive/c/protoc/ -name "protoc.exe" + $protoc = c:\cygwin\bin\cygpath -m $protoc + Write-Output "ZLIBINC = $zlibinc" + Write-Output "ZLIBLIB = $zliblib" + Write-Output "PROTOINC = $protoinc" + Write-Output "PROTOLIB = $protolib" + Write-Output "PROTOC = $protoc" + cmake .. "-GNMake Makefiles JOM" "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_PREFIX_PATH=c:/Qt/$env:qt_ver" "-DWITH_SERVER=1" "-DZLIB_INCLUDE_DIR=$zlibinc" "-DZLIB_LIBRARY=$zliblib" "-DPROTOBUF_INCLUDE_DIR=$protoinc" "-DPROTOBUF_LIBRARIES=$protolib" "-DPROTOBUF_LIBRARIES=$protolib" "-DPROTOBUF_LIBRARY=$protolib" "-DPROTOBUF_PROTOC_EXECUTABLE=$protoc" + - jom package + - c:\cygwin\bin\ls -l + - ps: | + $exe = dir -name *.exe + $new_name = $exe.Replace(".exe", "-$env:target_arch_qt5.exe") + Push-AppveyorArtifact $exe -FileName $new_name + $cmake_name = $exe.Replace(".exe", "-$env:target_arch_qt5.cmake.txt") + Push-AppveyorArtifact CMakeCache.txt -FileName $cmake_name + $json = New-Object PSObject + (New-Object PSObject | Add-Member -PassThru NoteProperty bin $new_name | Add-Member -PassThru NoteProperty cmake $cmake_name | Add-Member -PassThru NoteProperty commit $env:APPVEYOR_REPO_COMMIT) | ConvertTo-JSON | Out-File -FilePath "latest-$env:target_arch" -Encoding ASCII + Push-AppveyorArtifact "latest-$env:target_arch" +test: off diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 05c8a2e4..e927bda5 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -214,10 +214,13 @@ IF(WIN32) set(qtconf_dest_dir .) list(APPEND libSearchDirs ${QT_LIBRARY_DIR}) IF(ZLIB_FOUND) + # look for dll in the bin/ directory (gnuwin32 package) get_filename_component(ZLIB_DLL_DIR "${ZLIB_INCLUDE_DIRS}/../bin/" REALPATH) list(APPEND libSearchDirs ${ZLIB_DLL_DIR}) + # look for dll in the lib/ directory (nuget package) + get_filename_component(ZLIB_DLL_DIR "${ZLIB_LIBRARY}" DIRECTORY) + list(APPEND libSearchDirs ${ZLIB_DLL_DIR}) ENDIF() - MESSAGE(STATUS "Oracle: ZLIB dll found at ${ZLIB_DLL_DIR}") # qt4: codecs, iconengines, imageformats # qt5: iconengines, imageformats, platforms From a9f2cd73de5ed42925ffc3a146e63a4e1c35efe6 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Mon, 28 Sep 2015 12:24:22 +0200 Subject: [PATCH 02/49] Fix volume control fix #1528 --- cockatrice/src/soundengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/soundengine.cpp b/cockatrice/src/soundengine.cpp index 9686ecef..b3cd3c6a 100644 --- a/cockatrice/src/soundengine.cpp +++ b/cockatrice/src/soundengine.cpp @@ -89,7 +89,7 @@ void SoundEngine::playSound(QString fileName) inputBuffer->setData(audioData[fileName]); inputBuffer->open(QIODevice::ReadOnly); #if QT_VERSION >= 0x050000 - player->setVolume(settingsCache->getMasterVolume()); + player->setVolume(settingsCache->getMasterVolume() / 100.0); #endif player->stop(); player->start(inputBuffer); From 35815a894a9ff287bcad7e047bcac03a0140289a Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Tue, 29 Sep 2015 22:31:06 +0200 Subject: [PATCH 03/49] Make travis build a package --- .travis.yml | 8 +++----- CMakeLists.txt | 1 + travis-compile.sh | 10 ++++++++-- travis-dependencies.sh | 6 +++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 11cff198..31a722e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,11 @@ language: cpp env: - - QT4=1 - - QT4=0 + - QT4=1 BUILDTYPE=Debug + - QT4=0 BUILDTYPE=Debug + - QT4=0 BUILDTYPE=Release os: - linux - osx -compiler: - - gcc - - clang script: ./travis-compile.sh install: ./travis-dependencies.sh cache: apt diff --git a/CMakeLists.txt b/CMakeLists.txt index c4d239fe..d29239c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,6 +194,7 @@ if(UNIX) set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}/${PROJECT_VERSION_SAFE}") set(CPACK_STRIP_FILES "bin/${PROJECT_NAME}") set(CPACK_SOURCE_STRIP_FILES "") + set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_git-${PROJECT_VERSION_SAFE}") endif() elseif(WIN32) set(CPACK_GENERATOR NSIS ${CPACK_GENERATOR}) diff --git a/travis-compile.sh b/travis-compile.sh index 65f48b2c..23345d84 100755 --- a/travis-compile.sh +++ b/travis-compile.sh @@ -11,5 +11,11 @@ fi if [[ $TRAVIS_OS_NAME == "linux" && $QT4 == 0 ]]; then prefix="-DCMAKE_PREFIX_PATH=/opt/qt52/lib/cmake/" fi -cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=Debug -DWITH_QT4=$QT4 $prefix -make -j2 +cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE -DWITH_QT4=$QT4 $prefix + +if [[ $BUILDTYPE == "Debug" ]]; then + make -j2 +else + make package -j2 +fi + diff --git a/travis-dependencies.sh b/travis-dependencies.sh index 63fdc24c..37e529ec 100755 --- a/travis-dependencies.sh +++ b/travis-dependencies.sh @@ -1,11 +1,11 @@ #!/bin/bash if [[ $TRAVIS_OS_NAME == "osx" ]] ; then - brew update + brew update > /dev/null if (( QT4 )); then - brew install qt protobuf libgcrypt + brew install qt protobuf libgcrypt > /dev/null else - brew install qt5 protobuf libgcrypt + brew install qt5 protobuf libgcrypt > /dev/null fi else if (( QT4 )); then From ca9bfc9ff275a9184a334052ab8abd92e71ceff3 Mon Sep 17 00:00:00 2001 From: Psithief Date: Thu, 1 Oct 2015 12:00:30 +0800 Subject: [PATCH 04/49] Revert focus policy change in tab_game Fixes #1529. Completely untested, because I don't think this could possibly interfere with anything. It's a straight revert. --- cockatrice/src/tab_game.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index 86b6308d..5b5c3eda 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -452,7 +452,6 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_client scene = new GameScene(phasesToolbar, this); gameView = new GameView(scene); gameView->hide(); - gameView->setFocusPolicy(Qt::ClickFocus); cardInfo = new CardFrame(); playerListWidget = new PlayerListWidget(tabSupervisor, clients.first(), this); From 3e9610a08490a3f13d95458a9400ebe307ffb13a Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 1 Oct 2015 10:14:50 +0200 Subject: [PATCH 05/49] Fix artifact names --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c6d3608f..474ef02a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -68,9 +68,9 @@ build_script: - c:\cygwin\bin\ls -l - ps: | $exe = dir -name *.exe - $new_name = $exe.Replace(".exe", "-$env:target_arch_qt5.exe") + $new_name = $exe.Replace(".exe", "-${env:target_arch}_qt5.exe") Push-AppveyorArtifact $exe -FileName $new_name - $cmake_name = $exe.Replace(".exe", "-$env:target_arch_qt5.cmake.txt") + $cmake_name = $exe.Replace(".exe", "-${env:target_arch}_qt5.cmake.txt") Push-AppveyorArtifact CMakeCache.txt -FileName $cmake_name $json = New-Object PSObject (New-Object PSObject | Add-Member -PassThru NoteProperty bin $new_name | Add-Member -PassThru NoteProperty cmake $cmake_name | Add-Member -PassThru NoteProperty commit $env:APPVEYOR_REPO_COMMIT) | ConvertTo-JSON | Out-File -FilePath "latest-$env:target_arch" -Encoding ASCII From ab2a6867b5f46265eb3489055b217b6fb557ac87 Mon Sep 17 00:00:00 2001 From: tooomm Date: Thu, 1 Oct 2015 18:37:11 +0200 Subject: [PATCH 06/49] added appveyor badge badge for master branch --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 551f80e0..2a50748a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ --- -**Table of Contents**    [Cockatrice](#cockatrice) | [Get Involved] (#get-involved-) | [Community](#community-resources) | [Translation](#translation-status-) | [Building](#building-) | [Running](#running) | [License](#license-) +**Table of Contents**    [Cockatrice](#cockatrice) | [Get Involved] (#get-involved-) | [Community](#community-resources) | [Translation](#translation-status-) | [Building](#building--) | [Running](#running) | [License](#license-) --- @@ -37,7 +37,7 @@ Language statistics for `Cockatrice` *(on the left)* and `Oracle` *(on the right Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information!
-# Building [![Build Status](https://travis-ci.org/Cockatrice/Cockatrice.svg?branch=master)](https://travis-ci.org/Cockatrice/Cockatrice) +# Building [![Travis Build Status - master](https://travis-ci.org/Cockatrice/Cockatrice.svg?branch=master)](https://travis-ci.org/Cockatrice/Cockatrice) [![Appveyor Build Status - master](https://ci.appveyor.com/api/projects/status/lp5h0dhk4mhmeps7/branch/master?svg=true)](https://ci.appveyor.com/project/Daenyth/cockatrice/branch/master) **Detailed compiling instructions are on the Cockatrice wiki under [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)** From 74c58cc2b08d2c8e5acd8c3171d8c97a6ac0dbaa Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 1 Oct 2015 23:16:46 +0200 Subject: [PATCH 07/49] fix qt libraries bundling on osx fix #1547 --- cockatrice/CMakeLists.txt | 1 + oracle/CMakeLists.txt | 1 + servatrice/CMakeLists.txt | 1 + 3 files changed, 3 insertions(+) diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index f9657ced..2a600e1b 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -281,6 +281,7 @@ if(APPLE) # these needs to be relative to CMAKE_INSTALL_PREFIX set(plugin_dest_dir cockatrice.app/Contents/Plugins) set(qtconf_dest_dir cockatrice.app/Contents/Resources) + get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE) # qt4: codecs, iconengines, imageformats # qt5: audio, iconengines, imageformats, platforms, printsupport diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index e927bda5..1440ee24 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -185,6 +185,7 @@ if(APPLE) # these needs to be relative to CMAKE_INSTALL_PREFIX set(plugin_dest_dir oracle.app/Contents/Plugins) set(qtconf_dest_dir oracle.app/Contents/Resources) + get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE) # qt4: codecs, iconengines, imageformats # qt5: iconengines, platforms diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index 047a3f53..eb30b011 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -146,6 +146,7 @@ if(APPLE) # these needs to be relative to CMAKE_INSTALL_PREFIX set(plugin_dest_dir servatrice.app/Contents/Plugins) set(qtconf_dest_dir servatrice.app/Contents/Resources) + get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE) # qt4: codecs, sqldrivers # qt5: platforms, sqldrivers From 784148e1586a2782f095fc20da01e3ddd8995dc8 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Fri, 2 Oct 2015 18:14:54 +0200 Subject: [PATCH 08/49] try fixing appveyor dropping jom and using plain nmake --- appveyor.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 474ef02a..4c4b81f1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,7 +20,6 @@ install: # upgrade cmake in order to have c++11 support - choco install cmake -y - choco install nsis -y - - choco install jom -y - ps: | if (Test-Path c:\protoc) { echo "using protoc from cache" @@ -63,8 +62,8 @@ build_script: Write-Output "PROTOINC = $protoinc" Write-Output "PROTOLIB = $protolib" Write-Output "PROTOC = $protoc" - cmake .. "-GNMake Makefiles JOM" "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_PREFIX_PATH=c:/Qt/$env:qt_ver" "-DWITH_SERVER=1" "-DZLIB_INCLUDE_DIR=$zlibinc" "-DZLIB_LIBRARY=$zliblib" "-DPROTOBUF_INCLUDE_DIR=$protoinc" "-DPROTOBUF_LIBRARIES=$protolib" "-DPROTOBUF_LIBRARIES=$protolib" "-DPROTOBUF_LIBRARY=$protolib" "-DPROTOBUF_PROTOC_EXECUTABLE=$protoc" - - jom package + cmake .. "-GNMake Makefiles" "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_PREFIX_PATH=c:/Qt/$env:qt_ver" "-DWITH_SERVER=1" "-DZLIB_INCLUDE_DIR=$zlibinc" "-DZLIB_LIBRARY=$zliblib" "-DPROTOBUF_INCLUDE_DIR=$protoinc" "-DPROTOBUF_LIBRARIES=$protolib" "-DPROTOBUF_LIBRARIES=$protolib" "-DPROTOBUF_LIBRARY=$protolib" "-DPROTOBUF_PROTOC_EXECUTABLE=$protoc" + - nmake package - c:\cygwin\bin\ls -l - ps: | $exe = dir -name *.exe From 3ebe42c4006e5a754fd609c5f7370b09affa4ecd Mon Sep 17 00:00:00 2001 From: Gavin Bisesi Date: Sun, 27 Sep 2015 23:39:02 -0400 Subject: [PATCH 09/49] Add dummy googletest Uses the built in cmake -Dtest=ON It should download googletest on the fly if needed. Adds a new make target, `make test` Ref #167 --- CMakeLists.txt | 25 +++++++++++++++++++++++++ cockatrice/CMakeLists.txt | 8 ++++++++ cockatrice/test/dummy_test.cpp | 16 ++++++++++++++++ gtest-CMakeLists.txt | 15 +++++++++++++++ travis-compile.sh | 7 ++++--- travis-dependencies.sh | 10 +++++++--- 6 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 cockatrice/test/dummy_test.cpp create mode 100644 gtest-CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bc9e4b9..74b2f98e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -232,3 +232,28 @@ if(WITH_ORACLE) add_subdirectory(oracle) SET(CPACK_INSTALL_CMAKE_PROJECTS "release/oracle.app;oracle;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) endif() + +if(test) + find_package(Gtest REQUIRED) + if(GTEST_FOUND) + include_directories(${GTEST_INCLUDE_DIRS}) + else() + message(STATUS "Downloading googletest") + configure_file(gtest-CMakeLists.txt gtest-download/CMakeLists.txt) + execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download ) + execute_process(COMMAND ${CMAKE_COMMAND} --build . + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download ) + + # Add gtest directly to our build + add_subdirectory(${CMAKE_BINARY_DIR}/gtest-src + ${CMAKE_BINARY_DIR}/gtest-build + EXCLUDE_FROM_ALL ) + + # Add the gtest include directory, since gtest + # doesn't add that dependency to its gtest target + target_include_directories(gtest INTERFACE + "${CMAKE_BINARY_DIR}/gtest-src/include" ) + endif() + enable_testing() +endif() diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 2a600e1b..a2870bae 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -341,3 +341,11 @@ option(PORTABLE "portable build" OFF) IF(PORTABLE) add_definitions(-DPORTABLE_BUILD) endif() + +if(test) + enable_testing() + message(STATUS "Adding tests") + add_executable(cockatrice_test test/dummy_test.cpp) + target_link_libraries(cockatrice_test gtest_main) + add_test(NAME cockatrice_test COMMAND cockatrice_test) +endif() \ No newline at end of file diff --git a/cockatrice/test/dummy_test.cpp b/cockatrice/test/dummy_test.cpp new file mode 100644 index 00000000..a5416d09 --- /dev/null +++ b/cockatrice/test/dummy_test.cpp @@ -0,0 +1,16 @@ +#include "gtest/gtest.h" + +namespace { + class FooTest : public ::testing::Test { + + }; + + TEST(DummyTest, Works) { + ASSERT_EQ(1, 1) << "One is not equal to one"; + } +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/gtest-CMakeLists.txt b/gtest-CMakeLists.txt new file mode 100644 index 00000000..f5e07d30 --- /dev/null +++ b/gtest-CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.2) + +project(gtest-download LANGUAGES NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + URL https://googletest.googlecode.com/files/gtest-1.7.0.zip + URL_HASH SHA1=f85f6d2481e2c6c4a18539e391aa4ea8ab0394af + SOURCE_DIR "${CMAKE_BINARY_DIR}/gtest-src" + BINARY_DIR "${CMAKE_BINARY_DIR}/gtest-build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) \ No newline at end of file diff --git a/travis-compile.sh b/travis-compile.sh index 23345d84..86836437 100755 --- a/travis-compile.sh +++ b/travis-compile.sh @@ -11,11 +11,12 @@ fi if [[ $TRAVIS_OS_NAME == "linux" && $QT4 == 0 ]]; then prefix="-DCMAKE_PREFIX_PATH=/opt/qt52/lib/cmake/" fi -cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE -DWITH_QT4=$QT4 $prefix if [[ $BUILDTYPE == "Debug" ]]; then + cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE -Dtest=ON -DWITH_QT4=$QT4 $prefix make -j2 + make test else + cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE -DWITH_QT4=$QT4 $prefix make package -j2 -fi - +fi \ No newline at end of file diff --git a/travis-dependencies.sh b/travis-dependencies.sh index 37e529ec..15048ef8 100755 --- a/travis-dependencies.sh +++ b/travis-dependencies.sh @@ -7,15 +7,19 @@ if [[ $TRAVIS_OS_NAME == "osx" ]] ; then else brew install qt5 protobuf libgcrypt > /dev/null fi + brew unlink cmake + brew upgrade cmake else if (( QT4 )); then sudo apt-get update -qq - sudo apt-get install -y qtmobility-dev libprotobuf-dev protobuf-compiler libqt4-dev + sudo apt-get install -y qtmobility-dev libqt4-dev else + sudo add-apt-repository -y ppa:george-edison55/precise-backports sudo add-apt-repository -y ppa:beineri/opt-qt521 - sudo add-apt-repository -y ppa:kalakris/cmake sudo apt-get update -qq - sudo apt-get install -y libprotobuf-dev protobuf-compiler cmake libsqlite3-dev\ + sudo apt-get install -y libsqlite3-dev\ qt52base qt52webkit qt52tools qt52svg qt52multimedia fi + sudo apt-get install -y cmake libgtest-dev libprotobuf-dev protobuf-compiler + cd /usr/src/gtest && sudo cmake . && sudo cmake --build . && sudo mv libg* /usr/local/lib/ ; cd - fi From 7fe60279de45cbb46f20f4ea33346736a455759f Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 8 Oct 2015 21:33:24 +0200 Subject: [PATCH 10/49] Fix PR #1600 --- CMakeLists.txt | 28 +++----------- .../gtest-CMakeLists.txt.in | 0 cockatrice/CMakeLists.txt | 8 ---- tests/CMakeLists.txt | 38 +++++++++++++++++++ {cockatrice/test => tests}/dummy_test.cpp | 0 travis-compile.sh | 4 +- travis-dependencies.sh | 16 +++++--- 7 files changed, 56 insertions(+), 38 deletions(-) rename gtest-CMakeLists.txt => cmake/gtest-CMakeLists.txt.in (100%) create mode 100644 tests/CMakeLists.txt rename {cockatrice/test => tests}/dummy_test.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74b2f98e..ef9474e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,27 +233,9 @@ if(WITH_ORACLE) SET(CPACK_INSTALL_CMAKE_PROJECTS "release/oracle.app;oracle;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) endif() -if(test) - find_package(Gtest REQUIRED) - if(GTEST_FOUND) - include_directories(${GTEST_INCLUDE_DIRS}) - else() - message(STATUS "Downloading googletest") - configure_file(gtest-CMakeLists.txt gtest-download/CMakeLists.txt) - execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download ) - execute_process(COMMAND ${CMAKE_COMMAND} --build . - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download ) - - # Add gtest directly to our build - add_subdirectory(${CMAKE_BINARY_DIR}/gtest-src - ${CMAKE_BINARY_DIR}/gtest-build - EXCLUDE_FROM_ALL ) - - # Add the gtest include directory, since gtest - # doesn't add that dependency to its gtest target - target_include_directories(gtest INTERFACE - "${CMAKE_BINARY_DIR}/gtest-src/include" ) - endif() - enable_testing() +# Compile tests (default off) +option(WITH_TESTS "build tests" OFF) +if(WITH_TESTS) + include(CTest) + add_subdirectory(tests) endif() diff --git a/gtest-CMakeLists.txt b/cmake/gtest-CMakeLists.txt.in similarity index 100% rename from gtest-CMakeLists.txt rename to cmake/gtest-CMakeLists.txt.in diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index a2870bae..ca9342a4 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -340,12 +340,4 @@ endif() option(PORTABLE "portable build" OFF) IF(PORTABLE) add_definitions(-DPORTABLE_BUILD) -endif() - -if(test) - enable_testing() - message(STATUS "Adding tests") - add_executable(cockatrice_test test/dummy_test.cpp) - target_link_libraries(cockatrice_test gtest_main) - add_test(NAME cockatrice_test COMMAND cockatrice_test) endif() \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..93deb7df --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,38 @@ +enable_testing() +add_test(NAME dummy_test COMMAND dummy_test) + +# Find GTest + +add_executable(dummy_test dummy_test.cpp) + +find_package(GTest) + +if(NOT GTEST_FOUND) + IF(NOT EXISTS "${CMAKE_BINARY_DIR}/gtest-build") + message(STATUS "Downloading googletest") + configure_file("${CMAKE_SOURCE_DIR}/cmake/gtest-CMakeLists.txt.in" "${CMAKE_BINARY_DIR}/gtest-download/CMakeLists.txt") + execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download ) + execute_process(COMMAND ${CMAKE_COMMAND} --build . + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download ) + ELSE() + message(STATUS "GoogleTest directory exists") + ENDIF() + + # Add gtest directly to our build + add_subdirectory(${CMAKE_BINARY_DIR}/gtest-src + ${CMAKE_BINARY_DIR}/gtest-build + EXCLUDE_FROM_ALL ) + + # Add the gtest include directory, since gtest + # doesn't add that dependency to its gtest target + target_include_directories(gtest INTERFACE + "${CMAKE_BINARY_DIR}/gtest-src/include" ) + + SET(GTEST_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/gtest-src/include") + SET(GTEST_BOTH_LIBRARIES gtest) + add_dependencies(dummy_test gtest) +endif() + +include_directories(${GTEST_INCLUDE_DIRS}) +target_link_libraries(dummy_test ${GTEST_BOTH_LIBRARIES}) \ No newline at end of file diff --git a/cockatrice/test/dummy_test.cpp b/tests/dummy_test.cpp similarity index 100% rename from cockatrice/test/dummy_test.cpp rename to tests/dummy_test.cpp diff --git a/travis-compile.sh b/travis-compile.sh index 86836437..f3624ebb 100755 --- a/travis-compile.sh +++ b/travis-compile.sh @@ -9,11 +9,11 @@ if [[ $TRAVIS_OS_NAME == "osx" && $QT4 == 0 ]]; then prefix="-DCMAKE_PREFIX_PATH=`echo /usr/local/Cellar/qt5/5.*/`" fi if [[ $TRAVIS_OS_NAME == "linux" && $QT4 == 0 ]]; then - prefix="-DCMAKE_PREFIX_PATH=/opt/qt52/lib/cmake/" + prefix="-DCMAKE_PREFIX_PATH=`echo /opt/qt5*/lib/cmake/`" fi if [[ $BUILDTYPE == "Debug" ]]; then - cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE -Dtest=ON -DWITH_QT4=$QT4 $prefix + cmake .. -DWITH_SERVER=1 -DWITH_TESTS=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE -DWITH_QT4=$QT4 $prefix make -j2 make test else diff --git a/travis-dependencies.sh b/travis-dependencies.sh index 15048ef8..dcb65e29 100755 --- a/travis-dependencies.sh +++ b/travis-dependencies.sh @@ -10,16 +10,22 @@ if [[ $TRAVIS_OS_NAME == "osx" ]] ; then brew unlink cmake brew upgrade cmake else + sudo add-apt-repository -y ppa:george-edison55/precise-backports if (( QT4 )); then sudo apt-get update -qq sudo apt-get install -y qtmobility-dev libqt4-dev else - sudo add-apt-repository -y ppa:george-edison55/precise-backports - sudo add-apt-repository -y ppa:beineri/opt-qt521 + sudo add-apt-repository -y ppa:beineri/opt-qt541 sudo apt-get update -qq sudo apt-get install -y libsqlite3-dev\ - qt52base qt52webkit qt52tools qt52svg qt52multimedia + qt54base qt54webkit qt54tools qt54svg qt54multimedia fi - sudo apt-get install -y cmake libgtest-dev libprotobuf-dev protobuf-compiler - cd /usr/src/gtest && sudo cmake . && sudo cmake --build . && sudo mv libg* /usr/local/lib/ ; cd - + sudo apt-get install -y cmake cmake-data libgtest-dev libprotobuf-dev protobuf-compiler + sudo mkdir /usr/src/gtest/build + cd /usr/src/gtest/build + sudo cmake .. -DBUILD_SHARED_LIBS=1 + sudo make -j2 + sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so + sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so + cd - fi From b63475829b1f7b2855fdb82f38c92078a2327d4c Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Fri, 9 Oct 2015 15:47:34 +0200 Subject: [PATCH 11/49] Revert option from WITH_TESTS to TEST --- CMakeLists.txt | 4 ++-- travis-compile.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef9474e1..e735488e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,8 +234,8 @@ if(WITH_ORACLE) endif() # Compile tests (default off) -option(WITH_TESTS "build tests" OFF) -if(WITH_TESTS) +option(TEST "build tests" OFF) +if(TEST) include(CTest) add_subdirectory(tests) endif() diff --git a/travis-compile.sh b/travis-compile.sh index f3624ebb..37467226 100755 --- a/travis-compile.sh +++ b/travis-compile.sh @@ -13,7 +13,7 @@ if [[ $TRAVIS_OS_NAME == "linux" && $QT4 == 0 ]]; then fi if [[ $BUILDTYPE == "Debug" ]]; then - cmake .. -DWITH_SERVER=1 -DWITH_TESTS=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE -DWITH_QT4=$QT4 $prefix + cmake .. -DWITH_SERVER=1 -DTEST=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE -DWITH_QT4=$QT4 $prefix make -j2 make test else From 5fed8d04d66ab252a881ff7e99aefeb79e892410 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Fri, 9 Oct 2015 18:51:30 +0200 Subject: [PATCH 12/49] Updated docs --- README.md | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2a50748a..6802aea2 100644 --- a/README.md +++ b/README.md @@ -59,17 +59,28 @@ To compile: cd build cmake .. make + +You can then run + make install +to get a cockatrice installation inside the `release` folder, or: + + make package + +to create a system-specific installation package. + The following flags can be passed to `cmake`: -- `-DWITH_SERVER=1` Build the server. -- `-DWITH_CLIENT=0` Do not build the client. -- `-DWITH_ORACLE=0` Do not build oracle. -- `-DPORTABLE=1` Build portable versions of client & oracle. -- `-DWITH_QT4=1` Force compilation to use Qt4 instead of Qt5. -- `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings. -- `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files. +- `-DWITH_SERVER=1` Whether to build the server (default 0 = no). +- `-DWITH_CLIENT=0` Whether to build the client (default 1 = yes). +- `-DWITH_ORACLE=0` Whether to build oracle (default 1 = yes). +- `-DPORTABLE=1` Build portable versions of client & oracle (default 0 = no). +- `-DWITH_QT4=1` Force compilation to use Qt4 instead of Qt5 (default 0 = no). +- `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings (default `Release`). +- `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files (default 0 = no). +- `-DTEST=1` Enable regression tests (default 0 = no). Note: needs googletest, will be downloaded on the fly if unavailable. To run tests: ```make test```. + #### Building servatrice Docker container `docker build -t servatrice .`
From bab4a51ebb1a62539821d76a10629c68e9bd30a7 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Tue, 13 Oct 2015 19:25:42 +0200 Subject: [PATCH 13/49] CPack: Debian package --- CMakeLists.txt | 18 +++++++----------- cmake/createversionfile.cmake | 2 +- cmake/getversion.cmake | 30 +++++++++++++++++++++++++----- cockatrice/CMakeLists.txt | 4 ---- cockatrice/src/main.cpp | 14 +++++--------- oracle/CMakeLists.txt | 4 ---- oracle/src/main.cpp | 14 +++++--------- travis-compile.sh | 1 + 8 files changed, 44 insertions(+), 43 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e735488e..fd734cb2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,11 +174,6 @@ set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") -# Create a version string suitable for file names -string(REPLACE " " "_" PROJECT_VERSION_SAFE "${PROJECT_VERSION}") -string(REPLACE "(" "" PROJECT_VERSION_SAFE "${PROJECT_VERSION_SAFE}") -string(REPLACE ")" "" PROJECT_VERSION_SAFE "${PROJECT_VERSION_SAFE}") - if(UNIX) if(APPLE) set(CPACK_GENERATOR DragNDrop ${CPACK_GENERATOR}) @@ -186,19 +181,20 @@ if(UNIX) set(CPACK_DMG_FORMAT "UDBZ") set(CPACK_DMG_VOLUME_NAME "${PROJECT_NAME}") set(CPACK_SYSTEM_NAME "OSX") - set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-osx_git-${PROJECT_VERSION_SAFE}") + set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}") set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice/resources/appicon.icns") else() # linux set(CPACK_GENERATOR DEB ${CPACK_GENERATOR}) - set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}/${PROJECT_VERSION_SAFE}") - set(CPACK_STRIP_FILES "bin/${PROJECT_NAME}") - set(CPACK_SOURCE_STRIP_FILES "") - set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_git-${PROJECT_VERSION_SAFE}") + set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}") + set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) + set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins") + set(CPACK_DEBIAN_PACKAGE_SECTION "games") + set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice") endif() elseif(WIN32) set(CPACK_GENERATOR NSIS ${CPACK_GENERATOR}) - set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_git-${PROJECT_VERSION_SAFE}") + set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}") # Configure file with custom definitions for NSIS. configure_file( diff --git a/cmake/createversionfile.cmake b/cmake/createversionfile.cmake index b71aa8ad..ca5d0d76 100644 --- a/cmake/createversionfile.cmake +++ b/cmake/createversionfile.cmake @@ -3,7 +3,7 @@ set(VERSION_STRING_H "${PROJECT_BINARY_DIR}/version_string.h") INCLUDE_DIRECTORIES(${PROJECT_BINARY_DIR}) set( hstring "extern const char *VERSION_STRING\;\n" ) -set( cppstring "const char * VERSION_STRING = \"${PROJECT_VERSION}\"\;\n") +set( cppstring "const char * VERSION_STRING = \"${PROJECT_VERSION_FRIENDLY}\"\;\n") file(WRITE ${PROJECT_BINARY_DIR}/version_string.cpp.txt ${cppstring} ) file(WRITE ${PROJECT_BINARY_DIR}/version_string.h.txt ${hstring} ) diff --git a/cmake/getversion.cmake b/cmake/getversion.cmake index 7397a4fc..39859ee7 100644 --- a/cmake/getversion.cmake +++ b/cmake/getversion.cmake @@ -1,20 +1,40 @@ find_package(Git) if(GIT_FOUND) execute_process( - COMMAND ${GIT_EXECUTABLE} log -1 --date=short "--pretty=%h (%cd)" + COMMAND ${GIT_EXECUTABLE} log -1 --date=short "--pretty=%h" WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} RESULT_VARIABLE res_var OUTPUT_VARIABLE GIT_COM_ID ) if( NOT ${res_var} EQUAL 0 ) - set( GIT_COMMIT_ID "git commit id unknown") + set( GIT_COMMIT_ID "unknown") message( WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info." ) endif() string( REPLACE "\n" "" GIT_COMMIT_ID "${GIT_COM_ID}" ) + + execute_process( + COMMAND ${GIT_EXECUTABLE} log -1 --date=short "--pretty=%cd" + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE res_var + OUTPUT_VARIABLE GIT_COM_DATE + ) + if( NOT ${res_var} EQUAL 0 ) + set( GIT_COMMIT_DATE "unknown") + set( GIT_COMMIT_DATE_FRIENDLY "unknown") + message( WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info." ) + endif() + string( REPLACE "\n" "" GIT_COMMIT_DATE_FRIENDLY "${GIT_COM_DATE}" ) + string( REPLACE "-" "" GIT_COMMIT_DATE "${GIT_COMMIT_DATE_FRIENDLY}" ) else() - set( GIT_COMMIT_ID "unknown (git not found!)") + set( GIT_COMMIT_ID "unknown") + set( GIT_COMMIT_DATE "unknown") + set( GIT_COMMIT_DATE_FRIENDLY "unknown") message( WARNING "Git not found. Build will not contain git revision info." ) endif() -set(PROJECT_VERSION_MAJOR ${GIT_COMMIT_ID}) -set(PROJECT_VERSION ${GIT_COMMIT_ID} ) \ No newline at end of file +set(PROJECT_VERSION_MAJOR "0") +set(PROJECT_VERSION_MINOR "0") +set(PROJECT_VERSION_PATCH "1~git${GIT_COMMIT_DATE}.${GIT_COMMIT_ID}") +set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") +set(PROJECT_VERSION_FRIENDLY "${GIT_COMMIT_ID} (${GIT_COMMIT_DATE_FRIENDLY})") + diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index ca9342a4..bbf3d119 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -109,10 +109,6 @@ SET(cockatrice_SOURCES ${VERSION_STRING_CPP} ) -if (UNIX AND NOT APPLE) - set_source_files_properties(src/main.cpp PROPERTIES COMPILE_FLAGS -DTRANSLATION_PATH=\\"${CMAKE_INSTALL_PREFIX}/share/cockatrice/translations\\") -endif (UNIX AND NOT APPLE) - set(cockatrice_RESOURCES cockatrice.qrc) IF(UPDATE_TRANSLATIONS) diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index 058eea63..2195beb5 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -54,11 +54,7 @@ QSystemTrayIcon *trayIcon; ThemeManager *themeManager; const QString translationPrefix = "cockatrice"; -#ifdef TRANSLATION_PATH -QString translationPath = TRANSLATION_PATH; -#else -QString translationPath = QString(); -#endif +QString translationPath; #if QT_VERSION < 0x050000 static void myMessageOutput(QtMsgType /*type*/, const char *msg) @@ -136,13 +132,13 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationDomain("cockatrice.de"); QCoreApplication::setApplicationName("Cockatrice"); - if (translationPath.isEmpty()) { #ifdef Q_OS_MAC - translationPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + translationPath = qApp->applicationDirPath() + "/../Resources/translations"; #elif defined(Q_OS_WIN) - translationPath = app.applicationDirPath() + "/translations"; + translationPath = qApp->applicationDirPath() + "/translations"; +#else // linux + translationPath = qApp->applicationDirPath() + "/../share/cockatrice/translations"; #endif - } rng = new RNG_SFMT; settingsCache = new SettingsCache; diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 1440ee24..90558cfc 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -24,10 +24,6 @@ SET(oracle_SOURCES ../cockatrice/src/qt-json/json.cpp ) -if (UNIX AND NOT APPLE) - set_source_files_properties(src/main.cpp PROPERTIES COMPILE_FLAGS -DTRANSLATION_PATH=\\"${CMAKE_INSTALL_PREFIX}/share/oracle/translations\\") -endif (UNIX AND NOT APPLE) - set(oracle_RESOURCES oracle.qrc) IF(UPDATE_TRANSLATIONS) diff --git a/oracle/src/main.cpp b/oracle/src/main.cpp index 60e5e8d9..5a6e0367 100644 --- a/oracle/src/main.cpp +++ b/oracle/src/main.cpp @@ -14,11 +14,7 @@ SettingsCache *settingsCache; ThemeManager *themeManager; const QString translationPrefix = "oracle"; -#ifdef TRANSLATION_PATH -QString translationPath = TRANSLATION_PATH; -#else -QString translationPath = QString(); -#endif +QString translationPath; void installNewTranslator() { @@ -44,13 +40,13 @@ int main(int argc, char *argv[]) // this can't be changed, as it influences the default savepath for cards.xml QCoreApplication::setApplicationName("Cockatrice"); - if (translationPath.isEmpty()) { #ifdef Q_OS_MAC - translationPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + translationPath = qApp->applicationDirPath() + "/../Resources/translations"; #elif defined(Q_OS_WIN) - translationPath = app.applicationDirPath() + "/translations"; + translationPath = qApp->applicationDirPath() + "/translations"; +#else // linux + translationPath = qApp->applicationDirPath() + "/../share/cockatrice/translations"; #endif - } settingsCache = new SettingsCache; themeManager = new ThemeManager; diff --git a/travis-compile.sh b/travis-compile.sh index 37467226..14b558a4 100755 --- a/travis-compile.sh +++ b/travis-compile.sh @@ -10,6 +10,7 @@ if [[ $TRAVIS_OS_NAME == "osx" && $QT4 == 0 ]]; then fi if [[ $TRAVIS_OS_NAME == "linux" && $QT4 == 0 ]]; then prefix="-DCMAKE_PREFIX_PATH=`echo /opt/qt5*/lib/cmake/`" + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`echo /opt/qt5*/lib/` fi if [[ $BUILDTYPE == "Debug" ]]; then From 4dba476ab67be7e31da2c2959b03c74d04dab182 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 14 Oct 2015 15:45:54 +0200 Subject: [PATCH 14/49] modernize bash dialect --- travis-compile.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/travis-compile.sh b/travis-compile.sh index 14b558a4..6f24558c 100755 --- a/travis-compile.sh +++ b/travis-compile.sh @@ -6,11 +6,11 @@ mkdir build cd build prefix="" if [[ $TRAVIS_OS_NAME == "osx" && $QT4 == 0 ]]; then - prefix="-DCMAKE_PREFIX_PATH=`echo /usr/local/Cellar/qt5/5.*/`" + prefix="-DCMAKE_PREFIX_PATH=$(echo /usr/local/Cellar/qt5/5.*/)" fi if [[ $TRAVIS_OS_NAME == "linux" && $QT4 == 0 ]]; then - prefix="-DCMAKE_PREFIX_PATH=`echo /opt/qt5*/lib/cmake/`" - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`echo /opt/qt5*/lib/` + prefix="-DCMAKE_PREFIX_PATH=$(echo /opt/qt5*/lib/cmake/)" + export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$(echo /opt/qt5*/lib/)" fi if [[ $BUILDTYPE == "Debug" ]]; then From 8f674bd18c5ccfd995c4fd9f1e1e84b881790a2b Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 14 Oct 2015 18:45:10 +0200 Subject: [PATCH 15/49] Updated to qt 5.4.2 --- travis-dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/travis-dependencies.sh b/travis-dependencies.sh index dcb65e29..3ef048b6 100755 --- a/travis-dependencies.sh +++ b/travis-dependencies.sh @@ -15,7 +15,7 @@ else sudo apt-get update -qq sudo apt-get install -y qtmobility-dev libqt4-dev else - sudo add-apt-repository -y ppa:beineri/opt-qt541 + sudo add-apt-repository -y ppa:beineri/opt-qt542 sudo apt-get update -qq sudo apt-get install -y libsqlite3-dev\ qt54base qt54webkit qt54tools qt54svg qt54multimedia From 23e84273ed28937eab179a392fc098b7cb4b0540 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 7 Oct 2015 16:49:11 +0200 Subject: [PATCH 16/49] If the user request to join a room and we are already in, just focus the correct tab Also, handle all the Command_JoinRoom return values --- cockatrice/src/tab_server.cpp | 77 ++++++++++++++++++++++------------- cockatrice/src/tab_server.h | 7 ++-- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/cockatrice/src/tab_server.cpp b/cockatrice/src/tab_server.cpp index d15f6d8c..56556a7e 100644 --- a/cockatrice/src/tab_server.cpp +++ b/cockatrice/src/tab_server.cpp @@ -12,6 +12,7 @@ #include "tab_server.h" #include "abstractclient.h" #include "userlist.h" +#include "tab_supervisor.h" #include #include "pending_command.h" @@ -111,43 +112,19 @@ void RoomSelector::processListRoomsEvent(const Event_ListRooms &event) roomList->addTopLevelItem(twi); if (room.has_auto_join()) if (room.auto_join()) - joinRoom(room.room_id(), false); + emit joinRoomRequest(room.room_id(), false); } } -void RoomSelector::joinRoom(int id, bool setCurrent) -{ - Command_JoinRoom cmd; - cmd.set_room_id(id); - - PendingCommand *pend = client->prepareSessionCommand(cmd); - pend->setExtraData(setCurrent); - connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(joinFinished(Response, CommandContainer, QVariant))); - - client->sendCommand(pend); -} - void RoomSelector::joinClicked() { QTreeWidgetItem *twi = roomList->currentItem(); if (!twi) return; - joinRoom(twi->data(0, Qt::UserRole).toInt(), true); -} + int id = twi->data(0, Qt::UserRole).toInt(); -void RoomSelector::joinFinished(const Response &r, const CommandContainer & /*commandContainer*/, const QVariant &extraData) -{ - switch (r.response_code()) { - case Response::RespOk: break; - case Response::RespUserLevelTooLow: QMessageBox::critical(this, tr("Error"), tr("You do not have the proper permission to join this room.")); return; - default: - QMessageBox::critical(this, tr("Error"), tr("Failed to join the room due to an unknown error.")); - return; - } - - const Response_JoinRoom &resp = r.GetExtension(Response_JoinRoom::ext); - emit roomJoined(resp.room_info(), extraData.toBool()); + emit joinRoomRequest(id, true); } TabServer::TabServer(TabSupervisor *_tabSupervisor, AbstractClient *_client, QWidget *parent) @@ -157,7 +134,7 @@ TabServer::TabServer(TabSupervisor *_tabSupervisor, AbstractClient *_client, QWi serverInfoBox = new QTextBrowser; serverInfoBox->setOpenExternalLinks(true); - connect(roomSelector, SIGNAL(roomJoined(const ServerInfo_Room &, bool)), this, SIGNAL(roomJoined(const ServerInfo_Room &, bool))); + connect(roomSelector, SIGNAL(joinRoomRequest(int, bool)), this, SLOT(joinRoom(int, bool))); connect(client, SIGNAL(serverMessageEventReceived(const Event_ServerMessage &)), this, SLOT(processServerMessageEvent(const Event_ServerMessage &))); @@ -178,3 +155,47 @@ void TabServer::processServerMessageEvent(const Event_ServerMessage &event) serverInfoBox->setHtml(QString::fromStdString(event.message())); emit userEvent(); } + +void TabServer::joinRoom(int id, bool setCurrent) +{ + TabRoom *room = tabSupervisor->getRoomTabs().value(id); + if(!room) + { + Command_JoinRoom cmd; + cmd.set_room_id(id); + + PendingCommand *pend = client->prepareSessionCommand(cmd); + pend->setExtraData(setCurrent); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(joinRoomFinished(Response, CommandContainer, QVariant))); + + client->sendCommand(pend); + + return; + } + + if(setCurrent) + tabSupervisor->setCurrentWidget((QWidget*)room); +} + +void TabServer::joinRoomFinished(const Response &r, const CommandContainer & /*commandContainer*/, const QVariant &extraData) +{ + switch (r.response_code()) { + case Response::RespOk: + break; + case Response::RespNameNotFound: + QMessageBox::critical(this, tr("Error"), tr("Failed to join the room: it doesn't exists on the server.")); + return; + case Response::RespContextError: + QMessageBox::critical(this, tr("Error"), tr("The server thinks you are in the room but Cockatrice is unable to display it. Try restarting Cockatrice.")); + return; + case Response::RespUserLevelTooLow: + QMessageBox::critical(this, tr("Error"), tr("You do not have the required permission to join this room.")); + return; + default: + QMessageBox::critical(this, tr("Error"), tr("Failed to join the room due to an unknown error: %1.").arg(r.response_code())); + return; + } + + const Response_JoinRoom &resp = r.GetExtension(Response_JoinRoom::ext); + emit roomJoined(resp.room_info(), extraData.toBool()); +} diff --git a/cockatrice/src/tab_server.h b/cockatrice/src/tab_server.h index a057660b..b7cd4e2a 100644 --- a/cockatrice/src/tab_server.h +++ b/cockatrice/src/tab_server.h @@ -24,14 +24,11 @@ private: QTreeWidget *roomList; QPushButton *joinButton; AbstractClient *client; - - void joinRoom(int id, bool setCurrent); private slots: void processListRoomsEvent(const Event_ListRooms &event); void joinClicked(); - void joinFinished(const Response &resp, const CommandContainer &commandContainer, const QVariant &extraData); signals: - void roomJoined(const ServerInfo_Room &info, bool setCurrent); + void joinRoomRequest(int, bool setCurrent); public: RoomSelector(AbstractClient *_client, QWidget *parent = 0); void retranslateUi(); @@ -43,6 +40,8 @@ signals: void roomJoined(const ServerInfo_Room &info, bool setCurrent); private slots: void processServerMessageEvent(const Event_ServerMessage &event); + void joinRoom(int id, bool setCurrent); + void joinRoomFinished(const Response &resp, const CommandContainer &commandContainer, const QVariant &extraData); private: AbstractClient *client; RoomSelector *roomSelector; From 8001b05ce750d7a936c60cc9583a6a1fc00a91a9 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Tue, 6 Oct 2015 08:38:09 +0200 Subject: [PATCH 17/49] Travis - deploy to bintray --- .travis.yml | 42 ++++++++++++++++++++++++-------- CMakeLists.txt | 10 +++++++- cmake/bintray_deploy.json.in | 28 ++++++++++++++++++++++ travis-dependencies.sh | 46 +++++++++++++++++++++++++----------- 4 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 cmake/bintray_deploy.json.in diff --git a/.travis.yml b/.travis.yml index 31a722e7..95b8ba0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,27 @@ language: cpp -env: - - QT4=1 BUILDTYPE=Debug - - QT4=0 BUILDTYPE=Debug - - QT4=0 BUILDTYPE=Release -os: - - linux - - osx +matrix: + fast_finish: true + include: + # linux precise debug build, precise + gcc + qt4 + - os: linux + env: QT4=1 BUILDTYPE=Debug DIST=precise + # linux debug build, trusty + gcc + qt5 + - os: linux + dist: trusty + env: QT4=0 BUILDTYPE=Debug DIST=trusty + # osx debug build, osx + clang + qt5 + - os: osx + env: QT4=0 BUILDTYPE=Debug + # linux precise release build, precise + gcc + qt4 + - os: linux + env: QT4=1 BUILDTYPE=Release DIST=precise + # linux trusty release build, precise + gcc + qt5 + - os: linux + dist: trusty + env: QT4=0 BUILDTYPE=Release DIST=trusty + # osx release build, osx + gcc + qt5 + - os: osx + env: QT4=0 BUILDTYPE=Release script: ./travis-compile.sh install: ./travis-dependencies.sh cache: apt @@ -16,6 +32,12 @@ notifications: - https://webhooks.gitter.im/e/d94969c3b01b22cbdcb7 on_success: change on_failure: change - on_start: false -matrix: - fast_finish: true + on_start: never +deploy: + provider: bintray + file: "build/bintray_deploy.json" + user: "ctrlaltca" + key: + secure: b9Nv8FeYJGaTs1bZD8z0a7VksmEDkgVSDu8Os1ldYVaE6AwgYTt++RAbX60eu6jRb0G1bUCTWNF5wh3vhjYNl2xrj7N89O5FQAIgQcyWveAU91pn2I92pMcRfhHMIUVLwgzHoSy9JQPFG3ecE6CtWAi9+rbqcRwQRg+A2jfd7yI= + on: + condition: $BUILDTYPE = Release \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index fd734cb2..b8781a23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,9 +188,11 @@ if(UNIX) set(CPACK_GENERATOR DEB ${CPACK_GENERATOR}) set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}") set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) - set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins") set(CPACK_DEBIAN_PACKAGE_SECTION "games") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice") + IF(Qt5Widgets_FOUND) + set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins") + ENDIF() endif() elseif(WIN32) set(CPACK_GENERATOR NSIS ${CPACK_GENERATOR}) @@ -203,6 +205,12 @@ elseif(WIN32) ) endif() +# Configure file with build deployment data for travis +configure_file( + ${CMAKE_MODULE_PATH}/bintray_deploy.json.in + ${PROJECT_BINARY_DIR}/bintray_deploy.json +) + include(CPack) # Compile servatrice (default off) diff --git a/cmake/bintray_deploy.json.in b/cmake/bintray_deploy.json.in new file mode 100644 index 00000000..9ef1f2e0 --- /dev/null +++ b/cmake/bintray_deploy.json.in @@ -0,0 +1,28 @@ +{ + "package": { + "name": "Cockatrice-dev", + "repo": "Cockatrice", + "subject": "cockatrice", + "desc": "Cockatrice master branch automated builds", + "website_url": "https://github.com/Cockatrice/Cockatrice", + "issue_tracker_url": "https://github.com/Cockatrice/Cockatrice/issues", + "vcs_url": "https://github.com/Cockatrice/Cockatrice.git", + "github_use_tag_release_notes": true, + "github_release_notes_file": "RELEASE.txt", + "licenses": ["GPL-2.0"], + "labels": ["card", "tabletop", "game"], + "public_download_numbers": false, + "public_stats": false + }, + "version": { + "name": "@PROJECT_VERSION@", + "desc": "Unstable builds from master", + "vcs_tag": "@GIT_COMMIT_ID@", + "gpgSign": false + }, + "files": [ + { "includePattern": "build/(Cockatrice.*\\.deb)", "uploadPattern": "Ubuntu/$ENV{DIST}/$1" }, + { "includePattern": "build/(Cockatrice.*\\.dmg)", "uploadPattern": "MacOSX/$1" } + ], + "publish": true +} \ No newline at end of file diff --git a/travis-dependencies.sh b/travis-dependencies.sh index 3ef048b6..2aa2b024 100755 --- a/travis-dependencies.sh +++ b/travis-dependencies.sh @@ -9,23 +9,41 @@ if [[ $TRAVIS_OS_NAME == "osx" ]] ; then fi brew unlink cmake brew upgrade cmake + else - sudo add-apt-repository -y ppa:george-edison55/precise-backports + + # common prerequisites + sudo apt-get update -qq + sudo apt-get install -y libprotobuf-dev protobuf-compiler cmake + if (( QT4 )); then - sudo apt-get update -qq + # qt4 prerequisites sudo apt-get install -y qtmobility-dev libqt4-dev else - sudo add-apt-repository -y ppa:beineri/opt-qt542 - sudo apt-get update -qq - sudo apt-get install -y libsqlite3-dev\ - qt54base qt54webkit qt54tools qt54svg qt54multimedia + # qt5 prerequisites + sudo apt-get install -y libprotobuf-dev protobuf-compiler \ + qt5-default qttools5-dev qttools5-dev-tools \ + qtmultimedia5-dev libqt5multimedia5-plugins libqt5svg5-dev libqt5sql5-mysql + fi + + # prerequisites for tests + if [[ $BUILDTYPE == "Debug" ]]; then + if [[ $DIST == "precise" ]]; then + sudo add-apt-repository -y ppa:george-edison55/precise-backports + sudo apt-get update -qq + sudo apt-get install -y cmake cmake-data libgtest-dev + else + sudo add-apt-repository -y ppa:george-edison55/cmake-3.x + sudo apt-get update -qq + sudo apt-get install -y cmake cmake-extras libgtest-dev + fi + + sudo mkdir /usr/src/gtest/build + cd /usr/src/gtest/build + sudo cmake .. -DBUILD_SHARED_LIBS=1 + sudo make -j2 + sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so + sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so + cd - fi - sudo apt-get install -y cmake cmake-data libgtest-dev libprotobuf-dev protobuf-compiler - sudo mkdir /usr/src/gtest/build - cd /usr/src/gtest/build - sudo cmake .. -DBUILD_SHARED_LIBS=1 - sudo make -j2 - sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so - sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so - cd - fi From fc8aa32d7f41caac16a8d84e0282650da657892b Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Mon, 19 Oct 2015 22:09:24 +0200 Subject: [PATCH 18/49] Bintray package updates --- cmake/bintray_deploy.json.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/bintray_deploy.json.in b/cmake/bintray_deploy.json.in index 9ef1f2e0..37306db7 100644 --- a/cmake/bintray_deploy.json.in +++ b/cmake/bintray_deploy.json.in @@ -1,6 +1,6 @@ { "package": { - "name": "Cockatrice-dev", + "name": "Cockatrice-git", "repo": "Cockatrice", "subject": "cockatrice", "desc": "Cockatrice master branch automated builds", @@ -12,7 +12,7 @@ "licenses": ["GPL-2.0"], "labels": ["card", "tabletop", "game"], "public_download_numbers": false, - "public_stats": false + "public_stats": true }, "version": { "name": "@PROJECT_VERSION@", From ea5666c443cefd206bb417b3d0b6987e9ba0b4e3 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Tue, 20 Oct 2015 19:29:04 +0200 Subject: [PATCH 19/49] fix #1652 ; fix #467 --- common/server_cardzone.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/server_cardzone.cpp b/common/server_cardzone.cpp index 5ae65093..3e128a3e 100644 --- a/common/server_cardzone.cpp +++ b/common/server_cardzone.cpp @@ -147,7 +147,12 @@ int Server_CardZone::getFreeGridColumn(int x, int y, const QString &cardName, bo if (x == -1) { if (!dontStackSameName && freePilesMap[y].contains(cardName)) { x = (freePilesMap[y].value(cardName) / 3) * 3; - if (!coordMap.contains(x)) + + if(coordMap.contains(x) && + (coordMap[x]->getFaceDown() || + !coordMap[x]->getAttachedCards().isEmpty())) { + // don't pile up on: 1. facedown cards 2. cards with attached cards + } else if (!coordMap.contains(x)) return x; else if (!coordMap.contains(x + 1)) return x + 1; From 64c15d1fdc29f3c547843c5397d241bfbf97ae1e Mon Sep 17 00:00:00 2001 From: Brandon Griffin Date: Fri, 23 Oct 2015 20:11:13 -0400 Subject: [PATCH 20/49] Changed the default setting to play cards to the stack --- cockatrice/src/settingscache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index d5eb062d..8a8226f0 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -176,7 +176,7 @@ SettingsCache::SettingsCache() notificationsEnabled = settings->value("interface/notificationsenabled", true).toBool(); spectatorNotificationsEnabled = settings->value("interface/specnotificationsenabled", false).toBool(); doubleClickToPlay = settings->value("interface/doubleclicktoplay", true).toBool(); - playToStack = settings->value("interface/playtostack", false).toBool(); + playToStack = settings->value("interface/playtostack", true).toBool(); annotateTokens = settings->value("interface/annotatetokens", false).toBool(); cardInfoMinimized = settings->value("interface/cardinfominimized", 0).toInt(); tabGameSplitterSizes = settings->value("interface/tabgame_splittersizes").toByteArray(); From 813a0c2070030a760b0371715fbf6bb1b64ed544 Mon Sep 17 00:00:00 2001 From: Brandon Griffin Date: Mon, 26 Oct 2015 14:33:09 -0400 Subject: [PATCH 21/49] Added set focus to description anytime a create dialong is opened --- cockatrice/src/dlg_creategame.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/dlg_creategame.cpp b/cockatrice/src/dlg_creategame.cpp index 0f8cea4f..611a9fef 100644 --- a/cockatrice/src/dlg_creategame.cpp +++ b/cockatrice/src/dlg_creategame.cpp @@ -133,6 +133,7 @@ DlgCreateGame::DlgCreateGame(TabRoom *_room, const QMap &_gameType actReset(); } + descriptionEdit->setFocus(); clearButton = new QPushButton(tr("&Clear")); buttonBox->addButton(QDialogButtonBox::Cancel); buttonBox->addButton(clearButton, QDialogButtonBox::ActionRole); @@ -205,7 +206,7 @@ void DlgCreateGame::actReset() gameTypeCheckBoxIterator.value()->setChecked(false); } -descriptionEdit->setFocus(); + descriptionEdit->setFocus(); } From 9963e9dfe7b6c8f102caf9e99f51cfe6c969d917 Mon Sep 17 00:00:00 2001 From: Michael Hogg Date: Sat, 7 Nov 2015 12:57:58 -0800 Subject: [PATCH 22/49] Having token set to not null breaks the use of requireemail=false in the servatrice.ini as token will be null in this use case. --- servatrice/servatrice.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index 38f454e5..b4ff1738 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -82,7 +82,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_users` ( `avatar_bmp` blob NOT NULL, `registrationDate` datetime NOT NULL, `active` tinyint(1) NOT NULL, - `token` binary(16) NOT NULL, + `token` binary(16), `clientid` varchar(15) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), From d9c0c97ae76be7275d9b685e16e8cb87e9c8e65d Mon Sep 17 00:00:00 2001 From: Michael Hogg Date: Sun, 8 Nov 2015 03:05:12 -0800 Subject: [PATCH 23/49] Updating schema version to 12 and adding migration script. --- servatrice/migrations/servatrice_0011_to_0012.sql | 5 +++++ servatrice/servatrice.sql | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 servatrice/migrations/servatrice_0011_to_0012.sql diff --git a/servatrice/migrations/servatrice_0011_to_0012.sql b/servatrice/migrations/servatrice_0011_to_0012.sql new file mode 100644 index 00000000..37336320 --- /dev/null +++ b/servatrice/migrations/servatrice_0011_to_0012.sql @@ -0,0 +1,5 @@ +-- Servatrice db migration from version 11 to version 12 + +alter table cockatrice_users modify token binary(16) NULL; + +UPDATE cockatrice_schema_version SET version=12 WHERE version=11; diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index b4ff1738..7a35b695 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_schema_version` ( PRIMARY KEY (`version`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -INSERT INTO cockatrice_schema_version VALUES(11); +INSERT INTO cockatrice_schema_version VALUES(12); CREATE TABLE IF NOT EXISTS `cockatrice_decklist_files` ( `id` int(7) unsigned zerofill NOT NULL auto_increment, From bc44f8ce2ae3cf9347a7476485ddaf8965930392 Mon Sep 17 00:00:00 2001 From: tooomm Date: Tue, 17 Nov 2015 16:04:29 +0100 Subject: [PATCH 24/49] link renaming -`Linking FAQ` --> `How to set a custom picture url` -updated define accordingly --- cockatrice/src/dlg_settings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 8f84001f..787882ac 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -30,7 +30,7 @@ #include "soundengine.h" #include "sequenceEdit/shortcutstab.h" -#define LINKING_FAQ_URL "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Download-URLs" +#define WIKI_CUSTOM_PIC_URL "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Download-URLs" GeneralSettingsPage::GeneralSettingsPage() { @@ -247,7 +247,7 @@ void GeneralSettingsPage::retranslateUi() tokenDatabasePathLabel.setText(tr("Token database:")); pixmapCacheLabel.setText(tr("Picture cache size:")); highQualityURLLabel.setText(tr("Custom Card Download URL:")); - highQualityURLLinkLabel.setText(QString("%2").arg(LINKING_FAQ_URL).arg(tr("Linking FAQ"))); + highQualityURLLinkLabel.setText(QString("%2").arg(WIKI_CUSTOM_PIC_URL).arg(tr("How to set a custom picture url"))); clearDownloadedPicsButton.setText(tr("Reset/Clear Downloaded Pictures")); updateNotificationCheckBox.setText(tr("Notify when new client features are available")); } From 91bb2b295414d5da824d1e22cdf7aa4974e27c7b Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Fri, 11 Dec 2015 16:34:46 +0100 Subject: [PATCH 25/49] Fix velvet marble theme fix #1695 --- themes/VelvetMarble/zones/playerzone.jpg | Bin 112253 -> 42143 bytes themes/VelvetMarble/zones/tablezone.jpg | Bin 42143 -> 112253 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/themes/VelvetMarble/zones/playerzone.jpg b/themes/VelvetMarble/zones/playerzone.jpg index 9f511491a582b1a3c053772164826e248768e029..dd13cab78e88acb93ce0102665456d8e7f326a3f 100644 GIT binary patch delta 32302 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 JUF82h|39JrzqJ4W literal 112253 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⁣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 z zSgXQb9KbVtieujsNLfOI>(H?qn--k;MK=zKY*_8k&E(9WUoKuP)I-u-^|m~H?v#}A z%DhJ6C%7(0e>>|I!Ys~cn&E9R)vP_k@JmNiQmqhvi_WCeyHz;w#56qf<6AMBJ*!xi zWy+t%J37G%Mn)ir11wRJ!_(WEV1T$J30+cK^`b5$Lf_LHGr3f`#fh79L?z$bXb!Cz zpapLc_~w_nqUk34*^>B74Jb_-hRDDYvb7D!a{7u=y=z*r>3CJ|J-7qkff#|=@WsyF z(Xkq@S&Q2-ok7jB)}UIi4S%IeAphp=zHO_;$Kh0sNM6DnmT7<&Y?E@+^dq3c zDQ4=6S#h_Rj@EexE3Yc|W)VoJ`VTu_VN^EEjhhwxSCGK#%HI^d++uN(+0fnW@bO3+_1mhX zBN3tUX~OKpn<>=6kE7tv=80TdBooN?ka-gy@C9%28b8I?>3V-% z$oTOlKuSI`&!Drx^dM?kb>#7iQ%wq4L1&rPi)B|x#8b!0HMI?{Io@b9FvS<)Kc_tpx3DcJg>wTSWI=+|yO$hO|HL8hffGSwoiyqjJLB zM>8#9@u8ADW5bu3f$l^7OS1$xzV*;9%E-U(psZ7rnAYHN)2NJee#0KECaqVYKzd4) z!}`~v!>jyrtE*V*@@gC9K-N|qDfh~i9E<&w)hVZe(38~JWNGV-n_XMJAWcImo~#qLRH zzJvrkxA)X`IkR~h&cF-^u?x@*APfl`VyG zU6=;L9|C;`-YI&t_{8SSo;n%;{?r0EppZ*O7XMSh`gY8|{MTs4_3$tt$+D=wuiVdw zGOM($8y9Yn5<@2k%twnK*_p!a8VU3a;5RcHvcT;(&7bQSSkN#UH!%`aE`0Bn>}Vk0 zCr5lAe~QA_!1409;od`_8Sz9Lpz_nGf5~ILnM*Bs?!OFc6u=b#XJEYb0P6(mNWPt& zq1y%oM|KOYKKIT)%}@9I(~Nz;bK66*aoT$cY?j5SQ~2wgj|5BBL9p7u=vo>SwepLG z^)97G?D_P&KMf415F|lRl0X0M!T2?|HZXeL5LFgQm^N-R*DWZ6ooKwI4^t6{z>IW) z+Oz^QUB?&>{63M?QIWRy@$;5URfG-Xas&o60K0=ZH}#IDPk-w80RY=xa3vQkdw)@3 zkO}SGKrxM^7cE?HXvyu*CH>!k&2NC$jdyPyey3pF8ws`p-1?W2C*Gyko-G`!-U~V| z@*T#zL9wlq=&TcND78?@Z_G+wzXWK`AaG9b*PpP}eL_xz4l`j1#?BpV<@4(Vcb0dI z`sLm*$4nZLFiaTv?tLyu&8`ugxxal#5eT(|L`E|1cIQdD_3X7d*)=Nvpd=XCTdWibgpJm zIa}*^g>g+_g%H^>zbMAyabJ8@wsI+N$CoCo`qF4m=)m)MN1Ov=_g!;<-&Q|L!JRPc z2)M|^p8GR?eVFB_EOzNB8dq`go)aa#Yp4edg2GaG#0y#}AB&Iar?uOfgv#sW_0XJP zEHayGtF&+|$`;nR>z=}hx47zbn02TTLm|#}#-l%%dA5rF#}29d&=mAH{m%1LOSttY zL96#EcJ6e8gLieo){zYP2590Y6TU(yvV+SJ^y0(+Nx zhSk_$t$V-3y4Ntn0Wv1)vQ8vVBd3ID@1bWr%Un*b>R|O9y@WEkm54m-oU-}g)7*xk zIHoQ_0Kpq^b^kK@K2J9df5W)*p2acN{&EFOz#bA%^l@r|d+U84xgsM{?<|w2hjw(5 z>qmJL-*+)fj@JVBFCe}`5-raLznaCC9QE8;)FJHhw2@_lkJq$3N?D8X%X6^mg}qExW~G0UcSeZM`K1yzYO=CG8Mz0~!_ZAV`6W9R3T?|7qd z@7FI5a5-V2%5DcT#;BWFx<+$b=zAky4g`on47Hg>MPZL$J+AuZY+~xYVG0v5tq)s* z`swW^K4}5}Ivw+l{?@M_Dua65uxPZI_#3c$?w1FczMxOSFO$)IBny6`}x3uR;rhAM==qNx?p?8v97fN5n#!n1JMqV z?bPqOzdD813HN*_xkX>rh5|~D;FQk&T?^o!SOq#4@B`t}9Lu^SwAN9XfHF7OXK6ex z2ugEIrcNJHiY(2N!^}GH71(|*qvJErVH|5Dsbm_0GuU!EI`t9tw&~HP6TA1xjZ-l_ zfJ?FcjC5d^t|s@UgTWKgQrwcKXR&$3P`sk_y1b41QqtgWoal64TLqr|?-P{;qBCq> zeEFAn8_DL`@HtK`LO{5fXI(=7d!6B%O|cs`U;?3PxP_K+%8szFJTQ#@$i1f`yNO8Q zQt*1Gd=k10x05}n;KgVx)xZSeuP5V*NkkQq_=SqLg`w0^0*Uc&VvS{vu+UOJV`0^n zJzu?fgy~mJ5cfLt$AV_{Om2;tHaLGVIrcV`u$lZ4dSlnepYCKyk0FVVPm?`)DshA* zR!-Q{I_NdQxdFA>wvi*F`N}klmVi+UVl3R)A`~eB8*(4DRaN zpmpKyAq5q4^9@k*4J}?TIS|(4j zFvfP$!Axc>hJogy>Dx(n2X`2F?rP4rL}ZD~YvEzQZExh2XQe*ewLRhWf1l3y@|CMY z2>^tWv6TY!EDxojGejpO;Xj(=7U&FS8ez{*R(U>sd&N^t*UIYOzCDeIq0sA0-Nl|O zySo0plc>6p_vaqV(32vikVvq%gr+tVTc)>vyJ4ZaIX&u&m(+f%iBm+DLI`9IH zjSi?Y1&BFoS@^_<;N(jsn}AWcyZerj*@9G^JQzOyfm7~TvT(@{f2V~Ju(Z1nM2@n? z*Q|I_I{kjcC^lLSVV=HNFx=fL*JV$!g;qt?^H_Q5-YJrg-|kXa@;FYI$m9WT`=kfg4el+$Hn3nHLi+}U{dT7k?S z<4V@1$D#SVMtkHe_CoQ6+lUV6WqLxj(pKv3?4GjLiWOf@dnIJG+E=?-6v==HX6mX?XrK6;DS_Y*I77(TGS0=PfKf9YdV76AMum<9DEy zc!vSqfpEocEY|dAkj|W3j|DV!aBV1o^E9u|p%Uh4mtTDGH+8D>pBLs^j6r`|s zeF@$!>`HVHxS`tTTS?TuK`VtYH7n%vm<6BfjqiWjI0E&zhBY~@*$cXA&rO-mgPUOt zZfh(Jm7~SUsd+SKQO2Q&`daH*8D@ke#W&}_y!%!YMl2L@0&pV;%ksQ(L`ES9JBCv1 zKDgukxV3_AeB_gNj&5ykCi_6<6|oY2I&fSPAtmy( znD#GQdfJR>T&*D)^aE|M#G0{3^U7RzvA%h^v8pvrl>GK>lZodSznnpe^_7Z# zh7!Nx4jAb}+OENqShb*A578 zBkhxEpoK2@spXno+E?uEd%iLrgQnbfImoOptjX%COE2Yp$o)gIudwA&KUCE3VOSA} zXW6EfcO{ROg&#eeItM*mAK4xe=)dPV<+sdqVsKxL)~;(shR?&>=acniOsWa;t=_If z@ofSySD$Xz7ui@wHOC^OsBd!z=mx!#L*l$gtuv5?g77cuquyR((%CTdwS-d+J&M5!( zLeN+V@;8f7$}U8t-L8vyJIt0{MK@Rz{ssgQv((!oRD^(4yK%Z;w0kpZ)kfdwhhFzo z+~6xvlVLW9X@)G6$@x1whI%xNBv(~f6fp%?{iK^`3o?1Zm6INu_O7Heg^0YbEb3@7@IAxFVBNRgem(R*h4Vh?=pub`kG`l!;2(8A zrknQnNI!)Jvo|O%2PK^mPY4QU72I-xxnXPr5&pM#E5E{gCrq z^Gon)M$Fei;Exk7b8G^xI)y+zsbnC`sl3z9S>mdmSSJLJ zA_tfVPbiiK>Eqs3f}_JK#m7v{XP1>bxJ5UV%o)PULxXN-5FA~n4)!RlEsQqEnrB!@ zv*yJ`C@&BCw$X=a>ThD#G?^>qLYxXDSkkDjwoIThZd)0C) zcak*eTJMyKI#WtTx@MHB$Ln!r)|!P6W9=+h+Q&98U5( z>Psp_^@rz>51!|Ef(M8+WOlq2c{%?@qTa?El67b0jg8P+X6XMu0FQ~ujvW3v$u&jv zptYjj_QfNKeXZej1fM-}QeieLbqqIH27eVldYFfry+{Iy#4D9 zS4I}M4U}rQdjI5HE!t5&brcfoX z<-*AB#^I9SlD~iaf`vmNEaTSrq2cU zCravudzk)S+Dna=MF}BQJ9OFJ*N|#?smE)79pKm~LoVQ;`JIjgtJ8ga z#TbQYvY1ADq0RKAx<0SH;ZE*=*ML8z=;T`!*T8e8H`rENTS+P)e=FnH$>>4%?`0fU zY_-5-Fc&p=;CFhqIW|c7ZrV~y9}=~2!uM_%qopS2aU2b&_L6^41PGFwV=3>?8q6i9 zWDowwGd(My-N=A zZZM#ET*rGm+=Y&QbD0YsXFX6YRTFuM$Y>qm{fZEaWt`zubP^vl5up9aHV{gtI$XXT zx3H*{DujoNYfybjCyl8)3Ikmmd|uVntyNAE*1+D)aealT!$c&}r!2^B$NI0?Nq*O% zX#rEOJJ@xssO<%!lbCA!?{-_+usRMcHE={~BX{P?!QV z(ZV!XeRmB%)BV~E<9Jk5wo`XcQLK^`gge{qLu!F(r1kGnzGenFqW|?M@3Q=x4*kjXXxqS;nDi-KYJE$euPY4bav!Ih0c2Kn(NQNA8->rP6*~5%>%w9fnB250 z>!)3_4%V0|={cKy)0T|OEtI5IjQPcu!k;~5o-StmT_criik1^$*m(RpXp4OKKg@*! zT`PmlC##Q;3M2N(SHM&P{aZKc*-D;^U3RjYVMctd#G-RE_r?Zo3vonbYUazM`g+zO zGen22ttpxdhy=*rCs1Z>)s8ks3TYkPF{2b7nMqGH6@26MoK5><{T6$3NeYaH7Y9@6 zl@#I`36s_qae<;|_v*WPSotQs-Lb)bYHCnR_I? zK_(WSr`|LGCZNz==Z~IAG9W56bTP;Fz~FUdfS&35fJX*Z!H5%|)1kO@fr>1RpdON6 z`rdXHq%*q?RZ}{Ycrr@__LJ;|zxT(Ijh=!^ZoB zM;+O7ZGl-#1a?dXE!|0z?*`?@5 zoR11aoq2fw#8N-BZF(|_>bDVP=d(A=QfYR6SZ*=dtLKyd!g}qVN!U~y9%ToAKczLu z`NaXx51D}b(3al{*~{g76bYO|t}cw;&3$QC-BR<_gHsE-yOxVmwS4(uYdTnCFt}=~ z*yXKv&}&Dilgf`C2`qa^pbK`}v&4$GL8K@&nthuzPa?9b@Dci1b+}NY#P}3I9x_<5 zF>?cl@~d_74$JjtvZ|a)A-qjv2?`uN!f6%#X8a#57`?Y?rCEfu`BEr5Cf^{?&yCPT zH0zkVRxMx?6X!?4Yp_=XT~Yc6a9oGs0Dca6>!foFG>hD! zmpuB9sNry%US=wWud)j+=&W9*`G&>yal22(#mr8t6O^{O2@X z&3J9`P;QEx+#^B!1+&p#R=oNaa^GLeI8C~}{g6Y6hgmnDW6+XcziE*Ef<0Bg7B8W6T3`}u|Du%Uk{QC;(DfMRF(4yd{Guf|lhuRxu}iJA zJ4g<=!!!Mg-+OMK5iZ)@U2WSlvw z#k&d_nwb~0y`M;W6u@N%rU7E|-dlW)nP8O?P0*^lcsak)y7_(yQ#Lg^*r@+VU}<+( z5=Y;oDeRpl;8L%`({9O(`oW!=51X9_KQ*%DzFAiSr|*jmXa4#X}$ zZ-F?Tf)M8tdik%5^zWM1O(S>ekP@cBsRzrB9B&oA(%au|j}6Yy>@~1a8Rn7awvG_$ zQTR)C|448tsFfbu+*D=Ov+}m-ak^o#+T1++^1ey8WM

sRopaU2rFEpLvoZ`O@Go~~xavOV%bvy->_fN% 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 NlTML+V5j=!{{RhTd*%QD diff --git a/themes/VelvetMarble/zones/tablezone.jpg b/themes/VelvetMarble/zones/tablezone.jpg index dd13cab78e88acb93ce0102665456d8e7f326a3f..9f511491a582b1a3c053772164826e248768e029 100644 GIT binary patch literal 112253 zcmeFZcT`hbw>Y{(fY77}VgnKZ#X|4o5JE31genLMBA`-32}%)yqGBbA9hHD0RgBnB z6oR704iX?7sfr3oq)9?BB;mJ$=XgBd{qDWr_`UJ|c;lVT*ei3-HCLN!?zLuFo7t|} z0Z4hRtA{HDlYl|);18PpfcWl`7QGFEJUyY+5Cq9U2-tiG4j>r#gJ4S_sW}*ew!)VF zgkxYU|DcfoG$s&8Aq{XR!%XL3H2~-5MMIK*lr06nt&rTz00ikN|M}|>iQgK9^4@_b z;8S+s6H!inW~jjEgh>3Z6v))r!~|_@i8ir88JnU_ZP2Ecpb7|H4Ew8Z#S(wk4_{Oa z{}YCt788O?8V3J^R{Ga*z_k7v2W-W!;{ZJQqrWgiiJx^#H~*+k%q=lTe*Dh<%1bd$ z%*zo-VQx%ePM#2Kb`(;adx9c!#3g6Dp?#2ojEsz|jDoDJf{MJHyvhP41qG!Ai&Rw? zsH!efQJA~_`1$ehuPJPvy!<@HdCH24%4&*=ifZCSQSC<&mH(uI*(OLu2J(f9;V^AT zLInm_fz9@UVav@vfnh-}rC@Ux=&<;L13{6Ll19kL%E<$9_ja z2`PlMj1*kK3}mXnC6~=Nk#g_}SJh71V=Aq7qNLVQM|XkmqpfD<$$Q^9A(j{TjenSk zNU>1Ad^2~2^GW|mm&aEKmJ8bgK1O}JwXd0;IymX7cdB&%?bA=%KXIQ6JNBcTWAjqsP|2i~g9h`2k@UA$-d*i=q!|3xed2 zO0Ib0{I>XtRe;XMT{?~h!DIJqM zyN>DxTYp&Ydp`2q%KYr(*52=q(fv*E4_@s%)m2kZdWxy-{&Y^|d7EGG7U;KK9&6)^ zWj>Z6o-a6myF{X6jv2)n)gpMj)0<>kfB0xjmJy% z_Z~VVyZp$3FBK|XN`*Jp7;HhDKj?nbVqfKkqpGlqlH9Gyt?%kp6ZS4!ZmOVM)&#$D z{IZ4zV_(dBouqRbm-k<^TC~G`P(t_kiL@22d*m4gx@*@GXWOc8KhJmY?Q_UW88|q@ z9?x&fi**`|KL53-_IR4!bG_cKT`TP_lzU~KS{pPX`gH2jvGaQVt{d96Khb{Y-9DvJ zo??H3r+xRmQbk_$<2}mZo3e{>a+gXTiTac`&2RrAeZIA_^Re@EMO$xkz?SAEN<>L- z@1T~5Lv6p6T^|fFH~Dhq_)xv>%x+~(1Cwdg@x@DbHKx194(#EfjE|>nU?;+#`TB&> zdoaB-%YvG$U6N34hZmz+Dvs@={Z+RI&5tW(IbLt3j627jbxUBbEcxxg#eI4=PA(6* zzo&G>J9d%6!zuckpA|KmK4YS4*wQ(4O5yp3@yh$sd6FaH zwuuF|;{r|%FJbm>j3^vSGF4AKsz2ZE(^`#@8*AN{-R8MgNR1R|s5b6=6!7(?!o0Ps z=M@&8Ujln+u>axu9OYF6U&3B*a&&uQLFKZgZ(E7&sNXjhsjPwH(WfJwE_4+!SM&_w z5cZ^XO&aC(l_rmm#3mZ2$*j5=DuXy>bx%uTr_CKw@{wz*^9G-)=?|$NKltYDpySC@ zADzwfeOtJ}v(PRr*+VMGP&>aP`Sw5Fe=CfZGxF}UFP^TDqNMUDxpBCZ zinl-rLTG?3U83vb58U!OoHFJ7n0ra)(TMkqJ)6y*csb3{8_ngdeg06aQT*5)=P)2y z1k{K~v1;(g_466F5-1e{kPCy9^u&8A1l=ACn*;m@#kImm=8(#e+sqIkg8lhB*W!w~ zJjJi3*dLgZ^MyY#rgQjUkp4+yF^4hu3o}>eG<~SoGt{W1M6^v8D{>S|X7qtKunp1^-@Mnqm5sm}NkMW2fA+eeTUj%@8 zkRL>V@K7=o52Zs+(0Tx;LXl7kWG*HX)Bel#3r<|aPlfF#*)Ie?aes7G{G5;)7YBaC zf+f8bpPCSvg4-Gs0iOOS=NmyV0Z(&+><{$HIDEo(@H}^|kJ?7~_1TetkN@%fqZ_{4 zcl?n*H#q5(xaf$elnrs~Q$TUp-`bIy%LC~7;6}_ncsT!3H)4A-J~i=gC~16h^!Dh4 zsD#vbP)5PUPh2~04hNp+g{KnmZczzQ$>D^kNPvL+(-WiqAeQ?)aIMmju>;2*yIZ#cz>C{Qjcjo^{u?(eyND;^&w zu2AMr^xueOci@vV9O9z4|H&z(AA@)Q6AeltBBQp2r^XQgD7!N%necDOH~fkIJGuPU z?N0bOeDW{MRryiBqub96Pz3VACy041OTZ`MQVA(hzY30A91x`cf>qdxCjf!^FL-${ z#XJ6$eC|D5DQ*Z-05=RfJ_rAJ=fnoo4-xjAqZ3m=()}P2v#l81z5pD6pz}=t-wZ7R z2F5=o$v-B^m#KeDl7CE+e@v2pOp^aEnj}B=zGYx{gP;I#!^E4N6665XNj$U_ih@v( zH?#x5Vudqh4g%^kr24;5pnzKH2bqa0{L#V>Jr)6+opWu>eo%V5gFr|`8yO{}7>0}8 zl7nrIo3%KFSlHfO3u6iUK}SQ)6Q*l&LA&+!$?a zjr!x#22CbMZbSPzyZ+H2c*1D^F|u8|b{Xz6GsGuvH!`uYu`x0>H8M3d02Br(=?R4J zG=qc`oj+@Ej!KD0j!q;*gBHa#h66R5fYAos{V|U4#GlpvTZa8f7#Z=CE)i&wbB#wv z7)8ZJ#YZI&QUG&J#5|a*H1SVA@XsQDtNLg5+>HEL2+WYD=f5xT=j6r5|7;?K;2ZT9 zL;hC%pVTR0t{q;plaqHcep_@L$Vl;Zb3}Q#I9Zz5SXvsG8k+n{=pLOyz$d5A37U9f(H;pYgz$uj zC=X|hc4}&LB--54$;QIL)!D+r#=!+J%-Yn!(b?46$;sK+#>T?Rd5%}0pff%qRV-pZ z3pzWxIyx9T{|^Plvxo+^7$_Ak7JQ60=-V7JNGuLTbP2|%oN~2Jyyox2DDbnE@7x zQ*kS1fVJWjlm+QrS-@Cx6LXL&PC;)$`U7J%2V09%8_-XX&e7Od0NBb@jJE=l08%hj z=0Hxwc$>MhHrC>@Hr8SqzS#zx#C5aziQv&N z*H_Wm`hR<)eiV%S*H_WZ%-Gz<%G&(DeTR)eH;lyF&M({0pD3`miNT*ctKFZQ-#@?q zaNr*f{KJ8NIPeb#{^7v?k2vs`Z61{X4DwyT@}B*u5Cn{lFmGR+tH*kmIX8lW`}*hv zyaWg%#U~Jw#a2kr#$XiUEeIpYg18n47==fqBzpR}ienuRuuxE9Q$-B_bxn2th=R}! z+`Un#zwrMNq!N*s45Aejz#qbjVAKaNL4j{qT1Ngws=%`%)ZU?Y#TxxtY zfW?tVwfLxT5Y&h5M`IUpV|_5d8#EUik69a9htqP~CnI$A148PPZ6%r1nA3 z-Iia*qb`=0U;Hf5Q4xk>hW@<%SAw6E|2^Zo z!cYePD#ZWS1%K(*Fa6L54w9(kC}5$ZHUKG$PS_5nJ0UW9&T$l-@Ha#Mmm2;ri~UlA z82lmE08lLzL26-!kaCL}1Rr?~Nvg_2@J&S^2ljK`*2``Hu0se4L=OFsdjNy{x%6*| z1PMG!q(sk!S0N`~e^f+j^3ES5lHwmJa5g{*QiB#lT96L35;A~HAuH%N$R2Wm)r>*P)wG9rO@-1U-XZfrF79P&YIH zeTLZ3I5Y*#fFpPa*gTjjY%vT8TMk|idi^)O%9M%WhEc31)|6}B6e3p)ZU zfEB_@V3n|1*nQX|*bCS@*zd3pFedC9j4uI~P>@iQ(3DsqVI*NK;UKY2!e3&u#CC}! zi42K-68RG6BrZ!_lejDKNaB@5hs1ycTY?MDOv=O6;mhC#aBH{|90w1EZ-Xbpv*Cx~ zXW*CN*WvYW8k`OvfPaO5mz0)Nl|)GzNd6}2E*T&hA(QXJSh#SHBx9P52+xj?NYm>4oIDmDwDb+^;D`u>a*08w6wIkw4St;w7c|1=@{uu z>7&w@q$$!(((j}PrMURB$TsDo0hSR9>hI&qvH(G2dl=1EmeK0`dLj{O;623Ek^B#T8-LUwebb>7nm>Dupn*0g#`@@ z2Grr|E7U#I4IYi%sKqXe zV;7%TT(`J)iR6;iOMI5>T2i#+>5{KY)t3IYG-B!Dr8k$p*O1Uyt>LSYsc}W)wFXyH zOVdp=QS-cJv*uSVbuD|X7_C!Ujap2kD$))ajXZ^Ky6`YJ8c-GWTVv z%Sx8LT{fewryZcZSDT{Ut0Sjlt+P$%l+I(Fab2XYr|xduYTftCWtUqo-?9Aca@um< z3cVG9EAm#B-mshs0l3r!KDt6VSRjsQfR$HvzvHHU5 zmNgP^FnoVt=dp zZP-@NHp;ftb`YbD3CEOR2JBFFTkMMM2C&Pp;n>UAkM=tDk@jWwLk_DPVjQkHj5-=S zCOh77TUWU5lh@aKN_ejIJn7lvwcIPgs}2X@Ja8v*z217> zN!|~9q9p3-hb;oAP(`C;4{=tPDsEc)USr!=??D8+d`vfdzpB zK}JDYL9aG0*%-a?UN9nfLvUFzH^e36bjasT7Ml)i>fEfiIc@Wc&?TX9p$%b5Vc}u7 zwn%LW+)}kg5RMDK9M0Y9vGwBC@d&4gvk~k_`^Zy~%qUD$LDbMT%(jAU!`m_2Pi|-K z!0tG+<7>22bYb*FjC%|@h8K&AEsvdz3yh=0%fxSqzn?HaAvWO&UJIXwf0wu>@j&80 z5<2O0(s=T^9PP?5Kc24iwu-5#>hth{K zTr;j_N@hl6KHj}-_nzG!vK+EXvSHast-ScLz@!o>HoZP_N z`hA-Fa`p}GciUfmK=DBG0Y=_$dE|qV2cr+ZJ!E$1{2|fd$ipv>7#}%vL~u0X=*wd! z$Icy_&EJ;)mSjmPIxc-Y;dti>?1_p3m4b|d!IPdR@18=PI(&+AI_&g|Gv;T=XXVbO zo*g*1{@lIuy62Cd7ZmO&?6}}~fpSsf;-QPYOOcn_itLN7lQqdl$o%5y;;s_6lDn5z zTt0I}`pV8LUrINW(ym%xtt?wocBD*Lo=`qe;al;f(yFquN~4NYEm56X&AhhhT5FAS z&Asae*Gnkslp{Bw8>u%~wOeXCZ?3!f_?Gpp>$jKRzIaFV&f&YTyXkkw>vq%)+}n8X z-Tk%qAJ_j@f9HYWgYt*V9$siz&_HUGYuwj3OUSwqe^I(Vgkf+CAD|b$E5W>kR1ZVr*gzbVYUz{~q`I#QUA^XS#EGqF`6QE3=gq!urfk zWY2s(FrqSYadhSA?J@hY*W*FspC^(hX1^VsTs&FMG3PwydUN}x;-_YKho=`$SA4hr zPU8pihh}yO5Q1~URl<7FI#JJTJUEN@*P(uUpbPvs)c@ni{C|{6{B^;9UcZzSA7PjH znem?jv*JVju+#8Ga2PlR|JR}Z2ymz$sI`!^1UQ)f^Fqi-OG(PY<-q3-;7~s#4F~Q2 z%mi6*goF%C7Lt=!h9qEcaI7CL?vaGFY$^;K_*apdFTD(*stw7Qs5zMGERgkaR1eoR zTj-m#XVKOZ%biMU$5)sq%PoHN&O+~lpXJ_(g3HeSDK4&7ZV@X3HUuuY`MB-lx5!+= z$$e2*ZZ*>fS6OeHOkHh*cK28dl(p?UqGNXMN=wh!e<1JRp~I(6pE-N(d|~O;vhs?` zs@r$&*4?{b|K#a2+VdAL+dDcLUBAEo^!dxsFq6gMPVuI{^TF`NpCL%Xr6eUKrKP3f zL6=p)X9!Xz(vY^QgAc+qTrEk)agWZ41->O))y-sUbsxQRN*-VMVXry(8evg{^Tg$w zD=ZfKTe?K*$))6;bR~%U8xWPc!D`?BxeiaRT5_uNw)lI5(?32)Sm_qH&HCpz34eW% z;2yLi?clkJd(XRur)@koMyDS-UwQw<@67M3(QAWaG7cA3)xUht;{W}-fk6-DXh=7> zQ)3o-NPL*NST)ycj~={y;U0^n#|^Eq>|_U>J)`vpS;dSBuOrE4kzb{#NCux+%S((w z*KO8?;iItR@mVOenN7^D;IezSoO>oqRY$T@@R{shJ(iw#7>+qap0X9X-R(L`ui-Qs zY`(76-=1C6U7?B3JL|pi^?^@&@Z4EwFq=!m1SzB6Ebs_{9Bk!@6k!&>adXUR40?G$ z9-{!kpM@rnxXcxo(v-X!c!4+WiOA%szG2Ud8wf_F!|o%oo3yg26IOyu$_AEx7dMH6 zDzf<^Am&m1S}2i(p6Q0w?of9TwvYI%x&IcslZy;(Smzb7vt8dVSJ)t&PGXgQ|IQiH zShCUU1fEOnSEAI}vrg-g6fZ>_OEu!C>&`-9_?4_u&ZuM*6oP=Vj525Hf?0$O(w{ad z8qj7KTrF1LrOe7CCxvV4Omu|{h3x%;?%o(zbfsS9JG%B7i#+-f(sX{BUN5e@vzo5< zVci*B2&WPDEo+Qkel~Bt#RwNtdPD8Qyn73=1~@0G_sL&K>>r_a&O)0qw;WDSjWWmR zus-@{l38i9P(;#n9YVmb(WlnLBEvGP0jkysP^7 zrH6P8A@H9`Hko9aceEoM6%Bm@{P4ni{uP$`8v!7xed}M=wo?aoX_C1-3)(3u=w zPkS~cl~)QTayXkjX4mfx4q#wp?k!1;Rx66LpM~(>kzZw}uCx*gBihQfYgfBhA+83c zB&69*wO>bvuQ|-o3e0iPW0ec@>g(X4XUbu1>vR$fgjrz|3$cv%8=kpsD5k7e$O`*P z?9ax0P5jPnux4w#l>6p+e-=ugh2rs=geU33^ZG|}`Uy4)*sGwq z3?56>9^q+rm{4(;jFb3$Y=7PiJ2T7Q$m2+0rIBPQ1hcfAh4u(YGx= ziiXn;t2i9WOoCCkNL$3yQ|YWMDxkmA@!gbh-JTyNz@cWw7ce_o)}LO0yxYRHngu8Q zzc8?*odec{;Z2-^$`?|G8_!KXJ~EkYAhe!(mqLHEJI|AO_S1oTVNFy$VYWTbd#HLk zGEvj*!m`R2PLI?F`SO$D+37q-j{$Vw-n9G+q2-{RaKf97b z;}5clQwv2!f{cE}!qwcB_Zp}bNCuP0ZMfV2s{G1x=A}=^rR8wCB9B3S7M?rRn?YE{ zTD>?pEMP=5!2wIb%oMs7GDPn__SjY5rj9RoKQ#*}W_6c6 zmMUvCW_bSm#ChM5I5u&*WQut;!?g>ezsdd9V+d_*p_zg)_G|EHtnUiAp4^Q*lHDFT=rRB1YI$uebrLWr=(W$lwN;* zVj{)+F>4ETuw`b9iQnbZAxrT}a=hG=?Lq@``vKp?yWDpXUHO5Y`^4_|%|eYs{q=R8w|olru|k#BWD_aV#IQ_Cpwj2I>?-X#=)x?N zH!UQVH+&}yTQjL6M^+j<5|Ahj3$!9As?7EZN9|*%O~gcz{8WHP4p@i`fZ<8K5wW9(lo_(hM=Ws=|&ElZKdH*)Xts> zVFI?i_Tc2OdN2CWU=%mRTYzNQAo6W^RazHH5SsR;D^H%Bg;ZJqTfSzEHf|0*$8Pbt z>b|g6kc};$VKf|8on$}pD_dQ-cVligv5%@X<%wpj4~W^MT-JP&IA!;{4Q0b3W--0K zr^sza6-YyP(_pV7y;^n1tJacy^%@SzCO*mJMt^49=H{$&?Z~z}I}7c`ad>OFH&(Sj z-Qo^J9gK=Z7Y_DKGpf^5b(eKsGm4!QO%o}j4YB1_X0dRQ4(Q#Dq22?E8Qe6^rca;<n4=c?mC4A=;gzA(? zTe{_WHgQBs94eAM<3lU`Qp|m6qaq{C(8yMr$<7+1sQR?ut@IOOyHdtB`^yo!L;N(N zs{@WTUQ7~A5w$qaaxt6WZNX(kyBaI`OeD)*Kz=^dZLL@BS2_|}>{#;xd)z2CE9)-8 ztKw-!)A9CbuCi1k*-u34-?M=I>>_%P_qAgzElywK=8e(qp+?CvhJtHgN*g2$jXxQ?prZy z65^+Ivt07HyxIb#g&sxXh%jvU5XiZ1sF+*CKEx|m}SHZCeL`2!bn zxuU#b11@jFbVkTxipR@y`)a`wkVqUPvNN}gGh4_W=eI071azH7J~Dos<^|6$c`4(> z&jTCUQ=%QOkQ?4^fU5lRsVk%iY#yp9KpPDo%fiDeE}K-4r}{&QpR$=aySj=KUYB;L zI+d(S8GOlNg%H88!T9W0+pjI^nbsx8D6liAjxp{nj=ImIr1R2&&evbXX;g$5nWNTeN z6l#lh@+^oB?m4csf(Mi+F0aAq)zxJfbZ$Vfy52%TR($3x)bK5fc|mg%q~w11yw*0| zEDu2vSak-o>7Dm6tL&i`@5k;3K7|ef0c`+UB{>sOo!g1kS(cDKv=_{HywI9=IV_4rEDWLksCBDQm2p7-i6ekynN)M^Nc{UP1! zA72W!K-Y(FQy0L$Q}0f5N>_bjUqb8Le*gpGby$dP?rY+*J-yL84=ne4F$-l8nWN35 z^|z3Q%BrS&XuJuOYW)Pul+JK$*rRX)*R3&%Q<|VSRu$K4rcdA zW^m(*ilX;E&t+B+4-E+id($*H^gtU{^0PFgw^Yjv;6?_;n!M`sA69Ma zT5v}1j1~54yr^!ldy~>y>xyEgn!bqj|kh&=Mvfo4YW zX1gq}wK+X3BU9l$E+_5&r|y%ST0SwafqR8kR(Sj> zU=DJEFSMbyXTG~?*4cD@Rl9-xB`oR9EYwd7izlB|q>p7CKZn9VWn18t65ux$VyzOd+6?;3(7m!WPJ&U##Laij|2_4 z5L%ZPaWPH&unS2Xpz%HD4WJo5TG&9Qpl=`;*a}(#w_vuudjeu$LWn@FD?%`OPo-YY zC~VyU?GYhPKF5OL){MNp(|)jW&%*E&&b@(bY*$bwFPQ1#=~alOwB~qN^27L~mii?V zto@|38;{CY65kWc0srp`kg4qV)ePB3_ZQym37v&T@}@)j>Thz2p5Ifsf%R(w)&j9F z%}&sxv{o&Vxh!CfDbs5q3-<}A8n!`U-L8$BZRkD+JS-OYy3NpLS_XT~zvtHlr%JEY zS)k)}p=DeXVtgIif3*5#M=b-p%E{n+D5*DoV?bt8G;y zS5T+3h>WRLM|9frYo_R|tLHx?%4*XlzSmhHTS=5Un5~k>u_aetU9HjbPka0z+m5=| zrIFk^lN5T6dB#_1QU?Ho-;{!wYg^u3$w>E=5g8~^UmsjIPiAIex zi!Rre?UXunR0~_qANrQ{E_8n7 z4v~OcrQ{p?S#j!@N|(Xhq$G~0=&Ksn>l}Nv52>?9Xf#^mu!sI!mU+MGgk4Rx5uP$6 zz;W${L&^#Hg;lKe#W0$7KZ#PWejS}{ER{iWm-8y4bvyzr&Ze?wZr+h$K6_cTPj2-s zW#4_d;VB8&E)iC%z4M`x`(md+MhQ~(Y?R51q@>vSuv%m;7eOko3&@qzI`IOj()wWU z8|l<-&Xy=+?T<88lL+&57Lvv2>Nsd(w{P3KRoA@(Uek@!IQ=QhT`K{~(oSeHnytz*_auRr zJoBzrqAlhF(H!%T=1vU|*#`r0EzR8^gq}p`YO)etk%O4vGit5lTTHLGo-F<7!nZsJV{#b z?Q+X~V(D`25}i8CtvIojGUm@5!FcLMFTB%OHw!88Xuar!^WQn$-Vq&49cIgPiZFg` z`+7zIJ?X4SKP6kgTg!p@S-%phUiDaTCSI@Q+64(!H&#AST;FZ zhhhcKFMs&fUH&PxT#z8*eJ6CDp!aF%ow#GHdlD#L(Sk&!I@LYqOxtIdwS@_H~KacBDCxlP5{G^i8~vDz&Ka=EHGP|t%N8_uWRimXKSBg zy^P85TJO^?edAsoDnx*-5q>2MBg%sJ`D^84!An4DAfF9HlvO=n=ltd^g)$@GEo^A1 zzd_r8Fy}Cr`>cPlm56O89^xknF*ma^ylcEL7;jf~l)cp!EY>@lJTfxYv?bMvO@9r; zXTyLXOoP3dGTMx&vho^!>5rU3l6XBcDOIWS%lrdwB;emr)%ksWyp9ycY}>rX@k7?1 z!boh|P!~C{r8pf^La}NT4qmKLjA?+yIS~=gCvk=CMb#w3bF84vxB=uipFD+~pb`u^nrOZjED{TL zgAdH-zLJh;@>x%_UQZEFI~oTkh=dgl;N_gi`6@pPJt2Al^{;U={&V|t<+I_L2G3@cVVi{mVf}ep4Q)l|a_2Rc^$?%*gme$) zu>H$7J%l9O!Q_*GandZv8mrN&#aP<{_pLh6GBa%=P`1VDO%g+-VI_gRV1~X#A&K3K^@GxU9tnWgAIWXe>i$mXPGUB>UKU6R zdPKma-~}-2?@8e(GZ9HmdK0vUbk4`5MNFmRr_4^Igul3tr3Qb@hJL1ok{-lOTDbgV3(AS5AfDubyNbSf#}vJOoukx?&Db&IS6u5%T!D4|rTOV9bOT@WZG$rg+(BkGmceuV zrc%i5I@p<}g81Cx%Ma08 zDqqA{8MWxKo3OazhK9SimU8HO>f+U}$(>WS{Uoq?gwYy+(&+m7^^#{z$=Hv8C)p^% zXO~rUp#l1N$@BXNksDXkIE9c)bv=pmx)C#f$CsWQV!7Z5bCl^F9a^ZnMC;7-8;@A6 zc~sk;V4ARY>YHlO$wm)ms!NEugZ|SX;5NS6co!Km<7c1fnz5c`*7o@(GU-_RlLA$* zBE!;&X)F5ZzCqlj49~U3n$q7F%P@qnA>k(_@T!fq-Dax#1%G{;qlrE^AdJIV@v|}I zl(mljisx9{lOGS{NR2!f)!BCUdYPgV=UHnmH$83ouo%`UP6k*4MLw>_%e&}do z&bzn7GJdtx_Z9tN!@$xE6}>lacV}+WVH8tPaZe3|I1LpMmchc5M>u8#p>g-N@;;H+ z*}$;ub&43GKR(6Q_ODlAp;Iyg@GRjXopj68GKZu(9&P^;}XJ1-j3Y=k6%|c*d z_TlOYWp;%qbg6BkQpGIfi0z&nvnLEGmcH@zpLk3`lEHe@Cu~3dFrx;GU*BO76L77G z+@#*n!bOte%_1~6d!O`0Z|{8T4wQtCMC_Q>`-@$I0L0QkjL=-A5g-=t#Fz{ok>__$K`?XCBa_$}n{);K>;27wC=#|%1PErHp z7A!l29oyYs-=$aKGwxM=%=SqCg|MP@EIFIQ>WOA7I`NQP#?z+pwdRpN&Bsi0Zzi?v zT`9D}Ru5yl;i5@#cS&Tw$^rV;1S0Z-h$cYPosOoJy`cQwJ^@>eV;p(kLu{;jz^DqW zcodMZr2j%_lL&a&`YO=(HB#Pn$)ye+~OW{ut!~kL_w}o#ZUFj zR4{|IFS#bA0((!80}S=Ah=30aB(&o#rHo7g!XHMNp5dJhMPN(<+E(BHI1v0K+d&Y< z9y#M{_L=@NiJtMmk#E1;V}SH@1h7Lfi(Z|&YK@BQlE){w2m!H#$D)%$*|jGYwrL#q z9JK~9tB?I*8693#Zm%kCZCFOF4xn}N>eNMz9I)I~1-_!p(xtavR7n9|jj%2(_Llt2RzEL{cYe`I@B-Z&iOpL24XoY+Pmtu`HMm>lPbdoYsg-)1E?{55 zxxu^n_#*#~7Tw8k(YR4&W@XF-+h5aiEvpAfqYt}`aBJ-6xmB(c>^@F!Tx&}Q%A6d#ornHpp!%j18@V!9Bl_7I85Z)$9V^8ZRJ&Fp#{7V zyGxlkKNly=_5RRoDw!?n$}FLT$kQ9lCtPz>TiVGKES)dJfY-@CH<(jJvraU0)9(Me zq@ONOsymq%j6pB3gig>9!0*gUsO~hCv7Hdjuxb20u&EEciV$JRSH<4$-i&veJ8RjN zH-=b5hKPJ&ra!>C#p@J@=p9ZQ)>lb!RT)n&&!BEpO~q4Qnx5vOTuE z-H?b7Hc-1Wd7nF*ck|bEwn^ZSIOcaGL$qe30K44_KGYUdp9|dSd}2dp3H%$gov5AR z@yhYtx>QN|o|@}MLbv)I9Hw`{<-F^Lud6dRDKT4SVuhIg51Kn5oOj{1->>TvpY#i3 zCr0;+vTTYYDyT_ogz{3@JwzH;kQrIa`fR_TETd_(^S)$X|A=rno$&j|jHe3UHeV|Z zo}#v9$G3bUXR=EDD(;xQc7li><7tR-r3Xve8e)pFEZo!zl-o8Hc!W>$MQ$IPg+sm3 zp><(in%0tcfnzzI)^LK^Sxe_Cz$mGFk@ZJ`P>N>B8(7nMs9fumKc~~_3W)*{!zQMN zA;rRN9kfbLEA-)H%lFH#Rp+}RS9Ft}0$rSTx8BziTEJF!Jrs<3UTiE4BJtVt8u*vu zk><8&XHR=AR}H4_1>|+guu|uznJp#OidsIA4mJOy}K4}?^lr(XJf@Dvpj_l)_W7q7nN(STD5B5 z&WIN}!){!lf@uk)&^x$iYHqJ6;R=w?hxnunbIm}_EoB*d9U=(W&K{*lf-FqCYUd#i zldrbIw4W}#LlKQ<)l0c#OkiAe&p_EY4sdQ~vxgY@Xu=-wPd~d);K$+2k$23FHGcHZ zm`x|TVss{BC-4+{AD_O`)|$J#`fB^qjlS50z#V!6xTMNRXV+?E#kyC1xBAjgnm~Tw zOnt07@qw;*O7E)tiz*AikT~I%U7oJL|Q*+!qS^h4-eKlQ3v>c#*X0G6xU@!;cn5 zH&<6@j58a9f;N#W1;j?GPH%2HVR%ah%OY~2`ISruZPUsU=mc&hdWb%r>7A*}CKM|PI$d5kNt1bH9^ zJi*?=xHdn>VeljkM8ByF-i8I5K})nwPQKH<*ac4k=RJV;IdfaytMXiW>F3RG+VaPD zNx)+!FU0m`jZ*8%1FFnu*Yw}(td6szj@(C%S@9Ydv6>lWOOC}TQpzoX*MT^}2hM7( zi#}~J8!v?XPSu+P>$2aY!EVV`&QMDk^YfJ@y;WJ~l-OxKSz~1-S6i}eU*fj1GO3@XqWEzNBzo~#+ zx)<0MDk5aqt_gGn>ALJ`StFJumN6-6?sd*=(~rPq~<+u;bL*jS@7LLW=Cx2 z%RLOuI3)qTuiC4wRO{lZ&pT1-16ha}#)I6@%Jsll*Eq9K7epIL**bXv;rVA5-Nz?n zS4R+EASWpJ^78doil?wg3W!a_uqoQa$m{&-DXm)bqGE8Es~q@@!us!VYfmtaKyf$* zUyInSn8mK5w^VwYO6dx-ffMO4(JO1F>V{sPZ*6#Cs z@mSz?Zpfr~!a@tboa8Q@XhF7%&(YM;5HBpDaJOe?& zk7dQB7RkEDg3W4%LDjl#-{QA<8rbDeJ$R$HFP~@MNpzQ-;RWl^D=nl#b@kX=l|Zb3 zJYLt3!3_ZCuK0(eB9e#{s<+l9$#IHL6wP%wC_kQSOU_t{wX3TxJ)OPmNI~uqqz6!F z9*%>>DSULFaz<)o8G4qS=J2)v2>Bxfu>Oq`b^0DBQ50{#ZNV*TKVN&;yP&#dri-72nQ^x$_sp|v zFqd1@aSj45c;I|2!#q6TN3V~v$GJ4 zc(tX0PKlwAH-3B;6T2S;=hRJKn3*d4o_cv834`%_Cv%2R?8+trpHop!0DOd5R&Y-P zeSVR|6hE6d7%FI{z}w%aEVVfFiRd791B$d*ZeQ8ksAs*#56axSu~29=_zn6*-lg`9 zc`KOneZ6x+;k>-o?qR?Z#jN^=8CSMnO;vboA$9CMI5jqm)lC^4UDI6pT9*Cr)>VRi zjzTe{GblrQ0I*)kPNGID+h~; zuS&4hr49};MN`=3UV93xX>9}I(}EK`Rbs~52odcc%$W%)PhC#CE>FR_oO zz#^EjR5V=suptso1D%y!qT- znB+LN&w6TDm*V067L8+MyveJ+Fe!T6d4G^SQuw98?wqDcyHhGSqJ`s~cq4p&rv+EZ z{7v6->pWnfA%V|!biZ3*4SJ9MlIw>0o+9!$Z2WPe;q`eL+lnB{Hd{rTI&4_E9ou0O z_eRG5;pp1qne6{JQFOA7Dq)I}BAtXiW)6=W!W<(+QBUQ3T3@DZq*5t!>L_zI=TSM< zOrfQop2=oYVwlps<-8fQt>4G5|LT?5cHf`(`+8sR>$>iDA5vL`+efx5M-z7N@>Xy_ zLHp@H={dYHZttLs0MTE=Uy>C?$P-@wd8;+) zz!oWOTl?;?&b)%2{0oPK5=8(;|kpaE#X~zzl1dzW?ZIukSOO2#NHPv4Q%XcnQ1(6q1wok*SN zyA{Ff7b+;l$Rg;=VasA?(YphShv_*z7VyvV=QtIrS`+%Q@47m^h5R!XvZdApeN znCL86mub|aH@ifmok=6FH6eSf+Pps7@;3Lkqqe9^G;LXOijRT4Ri{!y7EzpGNrNWB zgZJ>hW8A0p^D^TVtp4I2=}DYxuEDPz6JR<|Cv*{`gba_uho=?nTHythWd_x=DvPtL z=zsfiLmNQtmp4e4#evFdZAU<|>8kO#D9U2SYQTPSs+SQ&BmD=~m_FYiD2!MOHk@O) zUv5hnsyCF~bl|I^4(u&29e|(C!EhB<1EgN;<}P)R0Jaer8Tgh}em07ww8YYqwf<#nS4Ajl*3v4PX-s71W ziI&jwU^8O-a9){e(OvyC=NIGJ66^JhlQdKwEqL6zrfBFxYY_a&?fpB{^P^N+N z7xjDct|shns+|**3l_y=_R=$QsAfsT$2>hWG@uSgml1`4yVINz#B1mOMGx?Fq|zz@ zDzM)R|r}Ae zGB^t)#mkJgY;5}j3q+3c{hPROd@*<<^4aN-+D#j=*KA`djF;)C$(DtY(azGz_cuk1 z_jFc=f-%4;K=k}?2G#F8=A(hl?iCPw5NW6$vUgSKZoF~a1<*Ja77!t6j|0{fR7c~Rr1Ns| z^pNkS7*?=T&m}!MdND?*{p(aHLhKsPKL^v7MS(eZtX(Z)*>JZcz5g}`X{E?$P?$v#S2F(#|~K8fHgLbts|(!dgC zQkUo!;z3QQ;if0CuPeqf1o>TnunMYdcc`|o=G^=n3>oHI~yv>@YxG}z5~uYXaY&!h?W&-H=F&% zMzLrVagjSAqrL9Mc(|B}jv>5TLjYH?qJikgAtFnKquM5y&e=tQw)j8};|wKRQp3D0 zB;zdYBipyo^9JRABbOaT0>5e|&e=8jmR{Zp+H8>1h*<5kYPG!d7jTM1JOSElE%nz7 z)XngyjOxU%ZO~lll(Gh~Ei;XLN#i3xdSJO!zk^2Ikj2EGL-kfVFx@S*jpfSoOAAG2 z8C6(!`cdzdikys^1*cnkcnonN&9|)bq9UzG9o%C6QX~t|LUp7DeK!Y1_P1wjLlk`$ z=y;@UA_Eq3qk#(F7<>dZ!WeSuA%7$#<9Tt(L8SxrC#h*D0i0|pcEZ%2`|IuBeM%KE zQ4A)S==HgG;RRlXDuZnOcgHLn(X%aUxYH*A8RX8nS-2$HQ*mz7xz(*SxV1<>Yf&6w z%d8DmVU)@+yIL=5RyJ!Yfis&ZKI3;UBmc}O(>`TDh92Y1$1G0FMnzmj{uTI=zS)E3 zFE&U>n0A zd4cT*@sy?+ri7hvaFv-wpfr_%Do7&xU8;Z_mAa3uQt0oduK|?l^MT~Q|&eps}9iOgN=P;>&4$O*c#kVqUC?II}RWOtsM>WeuCWkMjHB<_F0DZ}VRrZ`$CLh2HNwcJwr zL`V4a12nnwm>*cDk{h*-VJFrG8iUikvh}+lQW+)&n(zahPZ!h`tZcVo_&Gi`_SU0a zk9ukvCu76SeC2+P zls$Lksp&X?JbRyOVCoTcUS#pW|m!c*%dfnyHNiSm~FjA(_;dwawwt9Ue z;{F<#c1})T<3Qf`t$>-oyD*HsZesZ#?GO7vzhgz)U`~wuJAu0V7hWO$^f)47`SSh_ zLc@ftxk)IZ9p1⁣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 z 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

sRopaU2rFEpLvoZ`O@Go~~xavOV%bvy->_fN% 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 NlTML+V5j=!{{RhTd*%QD delta 32302 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 JUF82h|39JrzqJ4W From beecc0f970f82b30e3e3e34411a34daccc371e9a Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Fri, 11 Dec 2015 18:23:34 +0100 Subject: [PATCH 26/49] Improved login error message --- cockatrice/src/dlg_connect.cpp | 7 +++++++ cockatrice/src/dlg_register.cpp | 5 +++++ common/server_protocolhandler.cpp | 2 +- servatrice/src/servatrice.cpp | 3 ++- servatrice/src/servatrice_database_interface.cpp | 2 ++ 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/dlg_connect.cpp b/cockatrice/src/dlg_connect.cpp index 13cf5480..ccdc02f8 100644 --- a/cockatrice/src/dlg_connect.cpp +++ b/cockatrice/src/dlg_connect.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "dlg_connect.h" #include "settingscache.h" @@ -149,6 +150,12 @@ void DlgConnect::actOk() settingsCache->servers().setPreviousHostList(hostList); settingsCache->servers().setPrevioushostindex(previousHosts->currentIndex()); + if(playernameEdit->text().isEmpty()) + { + QMessageBox::critical(this, tr("Connect Warning"), tr("The player name can't be empty.")); + return; + } + accept(); } diff --git a/cockatrice/src/dlg_register.cpp b/cockatrice/src/dlg_register.cpp index 2ccde232..db9ba363 100644 --- a/cockatrice/src/dlg_register.cpp +++ b/cockatrice/src/dlg_register.cpp @@ -361,6 +361,11 @@ void DlgRegister::actOk() QMessageBox::critical(this, tr("Registration Warning"), tr("Your email addresses do not match, please try again.")); return; } + if(playernameEdit->text().isEmpty()) + { + QMessageBox::critical(this, tr("Registration Warning"), tr("The player name can't be empty.")); + return; + } settingsCache->servers().setHostName(hostEdit->text()); settingsCache->servers().setPort(portEdit->text()); diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 0be08125..8cf1f260 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -387,7 +387,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd QString clientId = QString::fromStdString(cmd.clientid()).simplified(); QString clientVersion = QString::fromStdString(cmd.clientver()).simplified(); - if (userName.isEmpty() || (userInfo != 0)) + if (userInfo != 0) return Response::RespContextError; // check client feature set against server feature set diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index df7dea96..95e01ef6 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -176,7 +176,8 @@ bool Servatrice::initServer() bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); - qDebug() << "Registration enabled: " << regServerOnly; + qDebug() << "Accept registered users only: " << regServerOnly; + qDebug() << "Registration enabled: " << registrationEnabled; if (registrationEnabled) qDebug() << "Require email address to register: " << requireEmailForRegistration; diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index b4f84667..e1f53ae0 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -124,6 +124,8 @@ bool Servatrice_DatabaseInterface::execSqlQuery(QSqlQuery *query) bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString & error) { int minNameLength = settingsCache->value("users/minnamelength", 6).toInt(); + if(minNameLength < 1) + minNameLength = 1; int maxNameLength = settingsCache->value("users/maxnamelength", 12).toInt(); bool allowLowercase = settingsCache->value("users/allowlowercase", true).toBool(); bool allowUppercase = settingsCache->value("users/allowuppercase", true).toBool(); From 803589264dd9c9cb0580a594856cc508a1ad5e58 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Fri, 11 Dec 2015 18:51:40 +0100 Subject: [PATCH 27/49] Fix column widths in rooms list --- cockatrice/src/tab_server.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cockatrice/src/tab_server.cpp b/cockatrice/src/tab_server.cpp index 56556a7e..36e1b42d 100644 --- a/cockatrice/src/tab_server.cpp +++ b/cockatrice/src/tab_server.cpp @@ -36,7 +36,6 @@ RoomSelector::RoomSelector(AbstractClient *_client, QWidget *parent) #else roomList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); roomList->header()->setSectionResizeMode(1, QHeaderView::Stretch); - roomList->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); roomList->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); roomList->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); #endif From deec7cdc14379f4fa24506664578962865d7f5f2 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Fri, 11 Dec 2015 19:32:08 +0100 Subject: [PATCH 28/49] Avoid crash on dereferencing null pointer --- cockatrice/src/player.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index ca5ab211..ce33e24a 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -2115,6 +2115,9 @@ void Player::actSetPT() void Player::actDrawArrow() { + if(!game->getActiveCard()) + return; + game->getActiveCard()->drawArrow(Qt::red); } @@ -2183,6 +2186,9 @@ void Player::actSetAnnotation() void Player::actAttach() { + if(!game->getActiveCard()) + return; + ArrowAttachItem *arrow = new ArrowAttachItem(game->getActiveCard()); scene()->addItem(arrow); arrow->grabMouse(); @@ -2190,6 +2196,9 @@ void Player::actAttach() void Player::actUnattach() { + if(!game->getActiveCard()) + return; + Command_AttachCard cmd; cmd.set_start_zone(game->getActiveCard()->getZone()->getName().toStdString()); cmd.set_card_id(game->getActiveCard()->getId()); @@ -2268,16 +2277,25 @@ void Player::actCardCounterTrigger() void Player::actPlay() { + if(!game->getActiveCard()) + return; + playCard(game->getActiveCard(), false, game->getActiveCard()->getInfo()->getCipt()); } void Player::actHide() { + if(!game->getActiveCard()) + return; + game->getActiveCard()->getZone()->removeCard(game->getActiveCard()); } void Player::actPlayFacedown() { + if(!game->getActiveCard()) + return; + playCard(game->getActiveCard(), true, false); } From 3aac8930cc7cfcf8a6b6f01f6d6293ed0f9c62f6 Mon Sep 17 00:00:00 2001 From: Gavin Bisesi Date: Mon, 30 Nov 2015 20:51:00 -0500 Subject: [PATCH 29/49] Add script to validate schema changes. Basic assertions about version updates --- servatrice/check_schema_version.sh | 33 ++++++++++++++++++++++++++++++ travis-compile.sh | 4 +++- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100755 servatrice/check_schema_version.sh diff --git a/servatrice/check_schema_version.sh b/servatrice/check_schema_version.sh new file mode 100755 index 00000000..d59edc7a --- /dev/null +++ b/servatrice/check_schema_version.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +schema_ver="$(grep 'INSERT INTO cockatrice_schema_version' servatrice/servatrice.sql | sed 's/.*VALUES(//' | sed 's/).*//')" + +latest_migration="$(ls -1 servatrice/migrations/ | tail -n1)" +xtoysql="${latest_migration#servatrice_}" +xtoy="${xtoysql%.sql}" +old_ver="$(echo ${xtoy%%_to_*} | bc)" +new_ver="$(echo ${xtoy##*_to_} | bc)" + +if ((old_ver >= new_ver)); then + echo "New version $new_ver is not newer than $old_ver" + exit 1 +fi + +if ((schema_ver != new_ver)); then + echo "Schema version $schema_ver does not equal new version $new_ver" + exit 1 +fi + +expected_sql="^UPDATE cockatrice_schema_version SET version=${new_ver} WHERE version=${old_ver};$" +if ! grep -q "$expected_sql" servatrice/migrations/$latest_migration; then + echo "$latest_migration does not contain expected sql: $expected_sql" + exit 1 +fi + +expected_define="^#define DATABASE_SCHEMA_VERSION $new_ver$" +if ! grep -q "$expected_define" servatrice/src/servatrice_database_interface.h; then + echo "servatrice_database_interface.h does not contain expected #define: $expected_define" + exit 1 +fi diff --git a/travis-compile.sh b/travis-compile.sh index 6f24558c..0d26c5e4 100755 --- a/travis-compile.sh +++ b/travis-compile.sh @@ -2,6 +2,8 @@ set -e +./servatrice/check_schema_version.sh + mkdir build cd build prefix="" @@ -20,4 +22,4 @@ if [[ $BUILDTYPE == "Debug" ]]; then else cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE -DWITH_QT4=$QT4 $prefix make package -j2 -fi \ No newline at end of file +fi From 93566840b96e502aaab305947ccfe530c4e6ca27 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 13 Dec 2015 10:44:05 +0100 Subject: [PATCH 30/49] install bc package on linux for the check_schema_version script --- travis-dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/travis-dependencies.sh b/travis-dependencies.sh index 2aa2b024..8a64dd3c 100755 --- a/travis-dependencies.sh +++ b/travis-dependencies.sh @@ -14,7 +14,7 @@ else # common prerequisites sudo apt-get update -qq - sudo apt-get install -y libprotobuf-dev protobuf-compiler cmake + sudo apt-get install -y libprotobuf-dev protobuf-compiler cmake bc if (( QT4 )); then # qt4 prerequisites From 53191fcb992d17b6f65092d41d243a95e13294c5 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 13 Dec 2015 11:53:30 +0100 Subject: [PATCH 31/49] Whoops: Fix db schema number --- servatrice/src/servatrice_database_interface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index 01f53d79..14912131 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -9,7 +9,7 @@ #include "server.h" #include "server_database_interface.h" -#define DATABASE_SCHEMA_VERSION 11 +#define DATABASE_SCHEMA_VERSION 12 class Servatrice; From ed4149fa7beac9a2fe6154c85e4607beae88b2b5 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 13 Dec 2015 12:00:39 +0100 Subject: [PATCH 32/49] Add .txt to the common deck file types fix #1683 --- cockatrice/src/deck_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/deck_loader.cpp b/cockatrice/src/deck_loader.cpp index 4443b43b..02866c95 100644 --- a/cockatrice/src/deck_loader.cpp +++ b/cockatrice/src/deck_loader.cpp @@ -5,7 +5,7 @@ #include "decklist.h" const QStringList DeckLoader::fileNameFilters = QStringList() - << QObject::tr("Common deck formats (*.cod *.dec *.mwDeck)") + << QObject::tr("Common deck formats (*.cod *.dec *.txt *.mwDeck)") << QObject::tr("All files (*.*)"); DeckLoader::DeckLoader() From a637ff7cfacbd1f1dc13bf8f4b3ce4cba6e91843 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Mon, 14 Dec 2015 09:32:31 +0100 Subject: [PATCH 33/49] Add correct encrypted api key to deploy to bintray --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 95b8ba0c..b8a00074 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,6 @@ deploy: file: "build/bintray_deploy.json" user: "ctrlaltca" key: - secure: b9Nv8FeYJGaTs1bZD8z0a7VksmEDkgVSDu8Os1ldYVaE6AwgYTt++RAbX60eu6jRb0G1bUCTWNF5wh3vhjYNl2xrj7N89O5FQAIgQcyWveAU91pn2I92pMcRfhHMIUVLwgzHoSy9JQPFG3ecE6CtWAi9+rbqcRwQRg+A2jfd7yI= + secure: DtVeeLoi5fZG/RvLTecRnRQGW9fVNS4Oa5iut2vJa14PdKBAJiXACQ0EzcRJFsbtby7MyMc2IVtT5skpvsaSqkpaxoBaL1YtKwJ4CTkYcm2MDWHS7UlijuxxTjI6BnaL3lcCCIeG+NHBZa3dV2YNJ1sWv6Xmiiix1ujPPW8VtnM= on: condition: $BUILDTYPE = Release \ No newline at end of file From 1a6a1171e6d1cbaf21c6a9cc3dec48e8b98c84a6 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Mon, 14 Dec 2015 09:47:03 +0100 Subject: [PATCH 34/49] Add bintray badges --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 6802aea2..f889fe4b 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,12 @@ such as Magic: The Gathering, over a network. It is fully client-server based to prevent any kind of cheating, though it supports single-player games without a network interface as well. Both client and server are written in Qt, supporting both Qt4 and Qt5.
+# Downloads +We offer a download for both the last stable version (recommended for users) and the last development version. The development version contains the last implemented features, but can be unstable and unsuitable for gaming. +Downloads are hosted on [BinTray](https://bintray.com/). + +- Latest stable version download: [ ![Download](https://api.bintray.com/packages/cockatrice/Cockatrice/Cockatrice/images/download.svg) ](https://bintray.com/cockatrice/Cockatrice/Cockatrice/_latestVersion) +- Latest development (unstable) version download: [ ![Download](https://api.bintray.com/packages/cockatrice/Cockatrice/Cockatrice-git/images/download.svg) ](https://bintray.com/cockatrice/Cockatrice/Cockatrice-git/_latestVersion) # Get Involved [![Gitter chat](https://badges.gitter.im/Cockatrice/Cockatrice.png)](https://gitter.im/Cockatrice/Cockatrice) From 5636b43e6770338f748fca7daf2dc86acb59a80c Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Mon, 14 Dec 2015 11:40:03 +0100 Subject: [PATCH 35/49] Test appveyor deploy --- appveyor.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 4c4b81f1..c057f4c1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 0.0.0.0.1-branch-{branch}-build-{build} +version: 0.0.1-branch-{branch}-build-{build} cache: - c:\protobuf - c:\protoc @@ -10,11 +10,13 @@ environment: nuget_arch: x64 target_arch: x86_64 qt_ver: 5.5\msvc2013_64 + bintray_path: Win64 - vc_arch: amd64_x86 # cross-compile from amd64 to x86 choco_arch: --x86 nuget_arch: Win32 target_arch: x86 qt_ver: 5.5\msvc2013 + bintray_path: Win32 install: - systeminfo # upgrade cmake in order to have c++11 support @@ -74,4 +76,17 @@ build_script: $json = New-Object PSObject (New-Object PSObject | Add-Member -PassThru NoteProperty bin $new_name | Add-Member -PassThru NoteProperty cmake $cmake_name | Add-Member -PassThru NoteProperty commit $env:APPVEYOR_REPO_COMMIT) | ConvertTo-JSON | Out-File -FilePath "latest-$env:target_arch" -Encoding ASCII Push-AppveyorArtifact "latest-$env:target_arch" + $bt_password = ConvertTo-SecureString $Env:BINTRAY_APIKEY -AsPlainText -Force + $bt_credentials = New-Object System.Management.Automation.PSCredential ($Env:BINTRAY_USER, $bt_password) + $exe -match "Cockatrice-(?.*)\.exe" + $version = $matches['content'] + $bt_headers = @{ + "X-Bintray-Package" = "Cockatrice-git"; + "X-Bintray-Version" = $version; + "X-Bintray-Publish" = 1; + "X-Bintray-Override" = 1; + } + $url = "https://api.bintray.com/content/cockatrice/Cockatrice/$env:bintray_path/$new_name" + $result = Invoke-WebRequest -Uri $url -Credential $bt_credentials -Method PUT -Headers $bt_headers -InFile "$exe" + Write-Host $result test: off From 2712dd4248272bb923112363728cb3e6ac1165f7 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Tue, 15 Dec 2015 15:59:20 +0100 Subject: [PATCH 36/49] Extracted new translation strings --- cockatrice/translations/cockatrice_en.ts | 1953 +++++++++++++--------- 1 file changed, 1143 insertions(+), 810 deletions(-) diff --git a/cockatrice/translations/cockatrice_en.ts b/cockatrice/translations/cockatrice_en.ts index 6d351363..c2869a40 100644 --- a/cockatrice/translations/cockatrice_en.ts +++ b/cockatrice/translations/cockatrice_en.ts @@ -80,106 +80,106 @@ BanDialog - + ban &user name - + ban &IP address - + ban client I&D - + Ban type - + &permanent ban - + &temporary ban - + &Days: - + &Hours: - + &Minutes: - + Duration of the ban - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. - + Please enter the reason for the ban that will be visible to the banned person. - + &OK - + &Cancel - + Ban user from server - - - - + + + + Error - + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. - + You must have a value in the name ban when selecting the name ban checkbox. - + You must have a value in the ip ban when selecting the ip ban checkbox. - + You must have a value in the clientid ban when selecting the clientid ban checkbox. @@ -709,55 +709,65 @@ This is only saved for moderators and cannot be seen by the banned person. DlgConnect - + Previous Host - + New Host - + &Host: - + Enter host name - + &Port: - + Player &name: - + P&assword: - + &Save password - + A&uto connect at start - + Connect to server + + + Connect Warning + + + + + The player name can't be empty. + + DlgCreateGame @@ -827,27 +837,27 @@ This is only saved for moderators and cannot be seen by the banned person. - + &Clear - + Create game - + Game information - + Error - + Server error. @@ -1307,6 +1317,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + Registration Warning @@ -1320,6 +1331,11 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Your email addresses do not match, please try again. + + + The player name can't be empty. + + Country: @@ -1339,19 +1355,19 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgSettings + - Error - + Unknown Error loading card database - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1362,7 +1378,7 @@ Would you like to change your database location setting? - + Your card database version is too old. This can cause problems loading card information or images @@ -1373,21 +1389,21 @@ Would you like to change your database location setting? - + File Error loading your card database. Would you like to change your database location setting? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1396,7 +1412,7 @@ Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1405,52 +1421,52 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? - + Settings - + General - + Appearance - + User Interface - + Deck Editor - + Chat - + Sound - + Shortcuts @@ -1728,7 +1744,7 @@ Would you like to change your database location setting? - Linking FAQ + How to set a custom picture url @@ -1964,8 +1980,8 @@ Will now login. - - + + Error @@ -2246,35 +2262,35 @@ Local version is %1, remote version is %2. - + A card database update is already running. - + Unable to run the card database updater: - + The card database updater exited with an error: %1 - + Update completed successfully. Cockatrice will now reload the card database. - + Your client appears to be missing features that the server supports. This usually means that your client version is out of date, please check to see if there is a new client available for download. - - - + + + Information @@ -2295,32 +2311,32 @@ You need to provide the activation token received in the activation email - + failed to start. - + crashed. - + timed out. - + write error. - + read error. - + unknown error. @@ -2328,17 +2344,17 @@ You need to provide the activation token received in the activation email MessageLogWidget - + The game has been closed. - + %1 is now watching the game. - + %1 is not watching the game any more. @@ -2386,136 +2402,136 @@ You need to provide the activation token received in the activation email - + %1 has left the game. female - + %1 has left the game. male - + You have been kicked out of the game. - + %1 has loaded a deck (%2). - + %1 has loaded a deck with %2 sideboard cards (%3). - + %1 is ready to start the game. female - + %1 is ready to start the game. male - + %1 is not ready to start the game any more. female - + %1 is not ready to start the game any more. male - + %1 has locked her sideboard. female - + %1 has locked his sideboard. male - + %1 has unlocked her sideboard. female - + %1 has unlocked his sideboard. male - + %1 has conceded the game. female - + %1 has conceded the game. male - - - %1 has restored connection to the game. - female - - - - - %1 has restored connection to the game. - male - - - %1 has lost connection to the game. + %1 has restored connection to the game. female + %1 has restored connection to the game. + male + + + + + %1 has lost connection to the game. + female + + + + %1 has lost connection to the game. male - + %1 shuffles %2. female - + %1 shuffles %2. male - + %1 rolls a %2 with a %3-sided die. female - + %1 rolls a %2 with a %3-sided die. male @@ -2537,182 +2553,182 @@ You need to provide the activation token received in the activation email - + %1 undoes his last draw. - + %1 undoes her last draw. - + %1 undoes his last draw (%2). - + %1 undoes her last draw (%2). - + from exile - + the bottom card of %1's library - + the bottom card of his library - + the bottom card of her library - + from the bottom of %1's library - + from the bottom of his library - + from the bottom of her library - + the top card of %1's library - + the top card of his library - + the top card of her library - + from the top of %1's library - + from the top of his library - + from the top of her library - + from %1's library - + from library - + from sideboard - + from the stack - + %1 gives %2 control over %3. - + %1 puts %2 into play tapped%3. - + %1 puts %2 into play%3. - + %1 exiles %2%3. - + %1 puts %2%3 into his library. - + %1 puts %2%3 into her library. - + %1 puts %2%3 on bottom of his library. - + %1 puts %2%3 on bottom of her library. - + %1 puts %2%3 on top of his library. - + %1 puts %2%3 on top of her library. - + %1 puts %2%3 into his library at position %4. - + %1 puts %2%3 into her library at position %4. - + %1 moves %2%3 to sideboard. - + %1 plays %2%3. - + %1 takes a mulligan to %n. female @@ -2721,7 +2737,7 @@ You need to provide the activation token received in the activation email - + %1 takes a mulligan to %n. male @@ -2730,277 +2746,277 @@ You need to provide the activation token received in the activation email - + %1 flips %2 face-down. female - + %1 flips %2 face-down. male - - - %1 flips %2 face-up. - female - - - - - %1 flips %2 face-up. - male - - - - - %1 destroys %2. - female - - + %1 flips %2 face-up. + female + + + + + %1 flips %2 face-up. + male + + + + + %1 destroys %2. + female + + + + %1 destroys %2. male - + %1 unattaches %2. female - + %1 unattaches %2. male - + %1 creates token: %2%3. female - - - %1 creates token: %2%3. - male - - + %1 creates token: %2%3. + male + + + + %1 points from her %2 to herself. female - + %1 points from his %2 to himself. male - + %1 points from her %2 to %3. p1 female, p2 female - + %1 points from her %2 to %3. p1 female, p2 male - + %1 points from his %2 to %3. p1 male, p2 female - + %1 points from his %2 to %3. p1 male, p2 male - + %1 points from %2's %3 to herself. card owner female, target female - + %1 points from %2's %3 to herself. card owner male, target female - + %1 points from %2's %3 to himself. card owner female, target male - + %1 points from %2's %3 to himself. card owner male, target male - + %1 points from %2's %3 to %4. p1 female, p2 female, p3 female - + %1 points from %2's %3 to %4. p1 female, p2 female, p3 male - + %1 points from %2's %3 to %4. p1 female, p2 male, p3 female - + %1 points from %2's %3 to %4. p1 female, p2 male, p3 male - + %1 points from %2's %3 to %4. p1 male, p2 female, p3 female - + %1 points from %2's %3 to %4. p1 male, p2 female, p3 male - + %1 points from %2's %3 to %4. p1 male, p2 male, p3 female - + %1 points from %2's %3 to %4. p1 male, p2 male, p3 male - + %1 points from her %2 to her %3. female - + %1 points from his %2 to his %3. male - + %1 points from her %2 to %3's %4. p1 female, p2 female - + %1 points from her %2 to %3's %4. p1 female, p2 male - + %1 points from his %2 to %3's %4. p1 male, p2 female - + %1 points from his %2 to %3's %4. p1 male, p2 male - + %1 points from %2's %3 to her own %4. card owner female, target female - + %1 points from %2's %3 to her own %4. card owner male, target female - + %1 points from %2's %3 to his own %4. card owner female, target male - + %1 points from %2's %3 to his own %4. card owner male, target male - + %1 points from %2's %3 to %4's %5. p1 female, p2 female, p3 female - + %1 points from %2's %3 to %4's %5. p1 female, p2 female, p3 male - + %1 points from %2's %3 to %4's %5. p1 female, p2 male, p3 female - + %1 points from %2's %3 to %4's %5. p1 female, p2 male, p3 male - + %1 points from %2's %3 to %4's %5. p1 male, p2 female, p3 female - + %1 points from %2's %3 to %4's %5. p1 male, p2 female, p3 male - + %1 points from %2's %3 to %4's %5. p1 male, p2 male, p3 female - + %1 points from %2's %3 to %4's %5. p1 male, p2 male, p3 male @@ -3038,121 +3054,121 @@ You need to provide the activation token received in the activation email - + %1 taps her permanents. female - + %1 untaps her permanents. female - + %1 taps his permanents. male - + %1 untaps his permanents. male - + %1 taps %2. female - + %1 untaps %2. female - + %1 taps %2. male - + %1 untaps %2. male - + %1 sets counter %2 to %3 (%4%5). female - + %1 sets counter %2 to %3 (%4%5). male - + %1 sets %2 to not untap normally. female - - - %1 sets %2 to not untap normally. - male - - - - - %1 sets %2 to untap normally. - female - - - - - %1 sets %2 to untap normally. - male - - + %1 sets %2 to not untap normally. + male + + + + + %1 sets %2 to untap normally. + female + + + + + %1 sets %2 to untap normally. + male + + + + %1 sets PT of %2 to %3. female - + %1 sets PT of %2 to %3. male - + %1 sets annotation of %2 to %3. female - + %1 sets annotation of %2 to %3. male - + %1 is looking at %2. female - + %1 is looking at %2. male @@ -3174,174 +3190,174 @@ You need to provide the activation token received in the activation email - + %1 stops looking at %2. female - + %1 stops looking at %2. male - + %1 reveals %2 to %3. p1 female, p2 female - + %1 reveals %2 to %3. p1 female, p2 male - + %1 reveals %2 to %3. p1 male, p2 female - + %1 reveals %2 to %3. p1 male, p2 male - + %1 reveals %2. female - + %1 reveals %2. male - + %1 randomly reveals %2%3 to %4. p1 female, p2 female - + %1 randomly reveals %2%3 to %4. p1 female, p2 male - - - %1 randomly reveals %2%3 to %4. - p1 male, p2 female - - - - - %1 randomly reveals %2%3 to %4. - p1 male, p2 male - - - - - %1 randomly reveals %2%3. - female - - - - - %1 randomly reveals %2%3. - male - - - - - %1 peeks at face down card #%2. - female - - + %1 randomly reveals %2%3 to %4. + p1 male, p2 female + + + + + %1 randomly reveals %2%3 to %4. + p1 male, p2 male + + + + + %1 randomly reveals %2%3. + female + + + + + %1 randomly reveals %2%3. + male + + + + + %1 peeks at face down card #%2. + female + + + + %1 peeks at face down card #%2. male - + %1 peeks at face down card #%2: %3. female - + %1 peeks at face down card #%2: %3. male - + %1 reveals %2%3 to %4. p1 female, p2 female - + %1 reveals %2%3 to %4. p1 female, p2 male - + %1 reveals %2%3 to %4. p1 male, p2 female - + %1 reveals %2%3 to %4. p1 male, p2 male - + %1 reveals %2%3. female - + %1 reveals %2%3. male - + %1 is now keeping the top card %2 revealed. - + %1 is not revealing the top card %2 any longer. - + It is now %1's turn. female - + It is now %1's turn. male - - + + a card @@ -3360,7 +3376,7 @@ You need to provide the activation token received in the activation email - + red @@ -3368,7 +3384,7 @@ You need to provide the activation token received in the activation email - + yellow @@ -3376,7 +3392,7 @@ You need to provide the activation token received in the activation email - + green @@ -3384,17 +3400,17 @@ You need to provide the activation token received in the activation email - + The game has started. - + %1 draws his initial hand. - + %1 draws her initial hand. @@ -3413,179 +3429,179 @@ You need to provide the activation token received in the activation email - + ending phase - + untap step - + %1 draws %2 card(s). female - + %1 draws %2 card(s). male - + from play - + from her graveyard - + from his graveyard - + from her hand - + from his hand - + %1 puts %2%3 into her graveyard. - + %1 puts %2%3 into his graveyard. - + %1 moves %2%3 to her hand. - + %1 moves %2%3 to his hand. - + %1 attaches %2 to %3's %4. p1 female, p2 female - + %1 attaches %2 to %3's %4. p1 female, p2 male - + %1 attaches %2 to %3's %4. p1 male, p2 female - + %1 attaches %2 to %3's %4. p1 male, p2 male - + %1 places %2 %3 counter(s) on %4 (now %5). female - + %1 places %2 %3 counter(s) on %4 (now %5). male - + %1 removes %2 %3 counter(s) from %4 (now %5). female - + %1 removes %2 %3 counter(s) from %4 (now %5). male - + %1 is looking at the top %2 card(s) %3. female - + %1 is looking at the top %2 card(s) %3. male - + upkeep step - + draw step - + first main phase - + beginning of combat step - + declare attackers step - + declare blockers step - + combat damage step - + end of combat step - + second main phase - + It is now the %1. @@ -3593,74 +3609,79 @@ You need to provide the activation token received in the activation email MessagesSettingsPage - + Chat settings - + Custom alert words - + Enable chat mentions - + Enable mention completer - + In-game message macros - + Ignore chat room messages sent by unregistered users - + Ignore private messages sent by unregistered users - + Enable desktop notifications for private messages - + + Enable desktop notification for mentions + + + + + Enable room message history on join + + + + Separate words with a space, alphanumeric characters only - - - - Invert text color - - - - - Enable desktop notification for mentions. - - + Invert text color + + + + + (Color is hexadecimal) - + Add message - + Message: @@ -4104,7 +4125,7 @@ You need to provide the activation token received in the activation email - + Number: @@ -4139,22 +4160,22 @@ You need to provide the activation token received in the activation email - + Set annotation - + Please enter the new annotation: - + Set counters - + Cr&eate related card @@ -4206,7 +4227,7 @@ You need to provide the activation token received in the activation email - Common deck formats (*.cod *.dec *.mwDeck) + Common deck formats (*.cod *.dec *.txt *.mwDeck) @@ -4303,27 +4324,11 @@ You need to provide the activation token received in the activation emailGames - - - - Error - - - - - You do not have the proper permission to join this room. - - - - - Failed to join the room due to an unknown error. - - SequenceEdit - + Shortcut already in use @@ -4356,6 +4361,29 @@ You need to provide the activation token received in the activation email + + ShortcutsTab + + + Restore all default shortcuts + + + + + Do you really want to restore all default shortcuts? + + + + + Clear all default shortcuts + + + + + Do you really want to clear all shortcuts? + + + ShutdownDialog @@ -4377,37 +4405,32 @@ You need to provide the activation token received in the activation email SoundSettingsPage - - Choose path - - - - + Enable &sounds - - Path to sounds directory: + + Current sounds theme: - + Test system sound engine - + Sound settings - + Master volume requires QT5 - + Master volume @@ -4603,12 +4626,12 @@ You need to provide the activation token received in the activation email - + Welcome - + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. @@ -4867,40 +4890,198 @@ Please enter a name: + + TabLog + + + Logs + + + + + + Error + + + + + You must select at least one filter. + + + + + You have to select a valid number of days to locate. + + + + + Username: + + + + + IP Address: + + + + + Game Name: + + + + + GameID: + + + + + Message: + + + + + Main Room + + + + + Game Room + + + + + Private Chat + + + + + Past X Days: + + + + + Today + + + + + Last Hour + + + + + Maximum Results: + + + + + At least one filter is required. +The more information you put in, the more specific your results will be. + + + + + Get User Logs + + + + + Clear Filters + + + + + Filters + + + + + Log Locations + + + + + Date Range + + + + + Maximum Results + + + + + Room Logs + + + + + + + Time;SenderName;SenderIP;Message;TargetID;TargetName + + + + + Game Logs + + + + + Chat Logs + + + + + + Message History + + + + + There is no messages for the selected iilters. + + + + + Failed to collecting message history information. + + + TabMessage - + Private &chat - + &Leave - + %1 - Private chat - + This user is ignoring you. - + Private message from - + %1 has left the server. - + %1 has joined the server. @@ -4968,47 +5149,47 @@ Please enter a name: TabRoom - + &Say: - + Chat - + &Room - + &Leave room - + &Clear chat - + Chat Settings... - + mentioned you. - + Click to view - + You are flooding the chat. Please wait a couple of seconds. @@ -5016,33 +5197,72 @@ Please enter a name: TabServer - + Server + + + + + + Error + + + + + Failed to join the room: it doesn't exists on the server. + + + + + The server thinks you are in the room but Cockatrice is unable to display it. Try restarting Cockatrice. + + + + + You do not have the required permission to join this room. + + + + + Failed to join the room due to an unknown error: %1. + + TabSupervisor - + Are you sure? - + There are still open games. Are you sure you want to quit? - + Promotion - + You have been promoted to moderator. Please log out and back in for changes to take effect. + + + Warned + + + + + You have received a warning due to %1. +Please refrain from engaging in this activity or further actions may be taken against you. If you have any questions, please private message a moderator. + + TabUserLists @@ -5065,94 +5285,153 @@ Please enter a name: UserContextMenu - + User &details - + Private &chat - + Show this user's &games - + Add to &buddy list - + Remove from &buddy list - + Add to &ignore list - + Remove from &ignore list - + Kick from &game - + + Warn user + + + + + View user's war&n history + + + + Ban from &server - + + View user's &ban history + + + + &Promote user to moderator - + Dem&ote user from moderator - + %1's games - - + + + + Ban History + + + + + Ban Time;Moderator;Ban Length;Ban Reason;Visible Reason + + + + + User has never been banned. + + + + + Failed to collecting ban information. + + + + + + + Warning History + + + + + Warning Time;Moderator;User Name;Reason + + + + + User has never been warned. + + + + + Failed to collecting warning information. + + + + + Success - + Successfully promoted user. - + Successfully demoted user. - - + + Failed - + Failed to promote user. - + Failed to demote user. @@ -5367,26 +5646,65 @@ Please enter a name: UserList - + Users connected to server: %1 - + Users in this room: %1 - + Buddies online: %1 / %2 - + Ignored users online: %1 / %2 + + WarningDialog + + + Which warning would you like to send? + + + + + &OK + + + + + &Cancel + + + + + Warn user for misconduct + + + + + + Error + + + + + User name to send a warning to can not be blank, please specify a user to warn. + + + + + Warning to use can not be blank, please select a valid warning to send. + + + WndSets @@ -5463,22 +5781,22 @@ Please enter a name: ZoneViewWidget - + sort by name - + sort by type - + shuffle when closing - + pile view @@ -5486,557 +5804,572 @@ Please enter a name: shortcutsTab - + Form - + Main Window - + Deck editor - + Local gameplay - + Watch replay - + Connect - + Register - + Full screen - + Settings - + Check for card updates - + Exit - + Deck Editor - + Analyze deck - + Load deck (clipboard) - + Clerar all filters - + New deck - + Clear one filter - + Open custom folder - + Close - + Print deck - + Edit sets - + Delete card - + Edit tokens - + Reset layout - + Add card - + Save deck - + Remove card - + Save deck as - + Load deck - + Save deck (clipboard) - - + + Counters - + Life - - - - - + + + + + Set - - - - + + + + Add - - - - + + + + Remove - + Red - + Green - + Yellow - - - Mainwindow / Deck editor - - - - - Power / toughness - - - - - Power and toughness - - - - - Add (+1/+1) - - - - - Remove (-1/-1) - - - - - Toughness - - - - - Remove (-0/-1) - - - - - Add (+0/+1) - - - - - Power - - - - - Remove (-1/-0) - - - - - Add (+1/+0) - - - - - Game Phases - - - - - Untap - - - - - Disconnect - - - - - Upkeep - - - - - - Draw - - - - - Main 1 - - - - - Start combat - - - - - Attack - - - - - Block - - - - - Damage - - - - - End combat - - - - - Main 2 - - - - - End - - - - - Next phase - - - - - Next turn - - - - - Player - - - - - Tap Card - - - - - Untap Card - - - - - Untap all - - - - - Toogle untap - - - - - Flip card - - - - - Peek card - - - - - Play card - - - - - Attach card - - - - - Unattach card - - - - - Clone card - - - - - Create token - - - - - Create another token - - - - - Set annotation - - - - - Phases / P/T / Player - - - - - Move card to - - - - - Bottom library - - - - - Top library - - - - - - Graveyard - - - - - - Exile - - - - - Hand - - - - - View - - - Library - - - - - Tops card of library - - - - - Sideboard - - - - - Close recent view - - - - - Pre-play - - - - - Load remote deck - - - - - Load local deck - - - - - Game play - - - - - Draw arrow - - - - - Leave game - - - - - Remove local arrows - - - - - Concede - - - - - Roll dice + Playing Area - Rotate view CW + Phases | P/T | Playing Area + + + + + Game Lobby + + + + + Gameplay + + + + + Draw | Move | View | Gameplay + + + + + How to set custom shortcuts + + + + + Restore all default shortcuts + + + + + Clear all shortcuts + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Main Window | Deck Editor + + + + + Power / Toughness + + + + + Power and Toughness + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation - Shuffle library + Move card to - Rotate view CCW + Bottom library + + + + + Top library - Mulligan + + Graveyard - Draw card + + Exile - Draw cards + Hand - Undo draw - - - - - Always reveal top card + View - Draw / Move / View / Game play + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Load remote deck + + + + + Load local deck + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card From 5093ea4170c3e60fd08d05d56edb524581d1f9fb Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Tue, 15 Dec 2015 16:31:58 +0100 Subject: [PATCH 37/49] Small typos + reextracted strings --- cockatrice/src/tab_logs.cpp | 4 ++-- cockatrice/translations/cockatrice_en.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cockatrice/src/tab_logs.cpp b/cockatrice/src/tab_logs.cpp index 8be008cd..bd4a32e2 100644 --- a/cockatrice/src/tab_logs.cpp +++ b/cockatrice/src/tab_logs.cpp @@ -322,10 +322,10 @@ void TabLog::viewLogHistory_processResponse(const Response &resp) } } else - QMessageBox::information(static_cast(parent()), tr("Message History"), tr("There is no messages for the selected iilters.")); + QMessageBox::information(static_cast(parent()), tr("Message History"), tr("There are no messages for the selected filters.")); } else - QMessageBox::critical(static_cast(parent()), tr("Message History"), tr("Failed to collecting message history information.")); + QMessageBox::critical(static_cast(parent()), tr("Message History"), tr("Failed to collect message history information.")); } void TabLog::restartLayout() diff --git a/cockatrice/translations/cockatrice_en.ts b/cockatrice/translations/cockatrice_en.ts index c2869a40..cd4b6c80 100644 --- a/cockatrice/translations/cockatrice_en.ts +++ b/cockatrice/translations/cockatrice_en.ts @@ -5039,12 +5039,12 @@ The more information you put in, the more specific your results will be. - There is no messages for the selected iilters. + There are no messages for the selected filters. - Failed to collecting message history information. + Failed to collect message history information. From 25d77fb64a27701e589c40e45d00ec0b237b2ed2 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Tue, 15 Dec 2015 16:58:44 +0100 Subject: [PATCH 38/49] Possibly fix #1598 --- CMakeLists.txt | 4 ++++ common/server_protocolhandler.cpp | 7 +++++-- common/server_room.cpp | 3 ++- common/server_room.h | 5 +++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b8781a23..1bbb7b84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,10 @@ if(POLICY CMP0048) cmake_policy(SET CMP0048 OLD) endif() +if(POLICY CMP0064) + cmake_policy(SET CMP0064 OLD) +endif() + set(PROJECT_NAME "Cockatrice") # Default to "Release" build type diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 8cf1f260..406e20e7 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -603,8 +603,11 @@ Response::ResponseCode Server_ProtocolHandler::cmdJoinRoom(const Command_JoinRoo joinMessageEvent.set_message_type(Event_RoomSay::Welcome); rc.enqueuePostResponseItem(ServerMessage::ROOM_EVENT, r->prepareRoomEvent(joinMessageEvent)); - ServerInfo_ChatMessage chatMessage; for (int i = 0; i < r->chatHistory.size(); ++i) { - chatMessage = r->chatHistory.at(i); + QReadLocker chatHistoryLocker(&r->historyLock); + QList chatHistory = r->getChatHistory(); + ServerInfo_ChatMessage chatMessage; + for (int i = 0; i < chatHistory.size(); ++i) { + chatMessage = chatHistory.at(i); qDebug() << QString::fromStdString(chatMessage.message()).simplified(); Event_RoomSay roomChatHistory; roomChatHistory.set_message(chatMessage.sender_name() + ": " + chatMessage.message()); diff --git a/common/server_room.cpp b/common/server_room.cpp index a53fdd97..e9338627 100644 --- a/common/server_room.cpp +++ b/common/server_room.cpp @@ -236,7 +236,6 @@ void Server_Room::say(const QString &userName, const QString &s, bool sendToIsl) sendRoomEvent(prepareRoomEvent(event), sendToIsl); if (chatHistorySize != 0) { - ServerInfo_ChatMessage chatMessage; QDateTime dateTime = dateTime.currentDateTimeUtc(); QString dateTimeString = dateTime.toString(); @@ -244,10 +243,12 @@ void Server_Room::say(const QString &userName, const QString &s, bool sendToIsl) chatMessage.set_sender_name(userName.toStdString()); chatMessage.set_message(s.simplified().toStdString()); + historyLock.lockForWrite(); if (chatHistory.size() >= chatHistorySize) chatHistory.removeAt(0); chatHistory << chatMessage; + historyLock.unlock(); } } diff --git a/common/server_room.h b/common/server_room.h index 6e79ba6b..af652944 100644 --- a/common/server_room.h +++ b/common/server_room.h @@ -42,12 +42,13 @@ private: QMap externalGames; QMap users; QMap externalUsers; + QList chatHistory; private slots: void broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool sendToIsl = true); public: mutable QReadWriteLock usersLock; mutable QReadWriteLock gamesLock; - QList chatHistory; + mutable QReadWriteLock historyLock; Server_Room(int _id, int _chatHistorySize, const QString &_name, const QString &_description, const QString &_permissionLevel, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent ); ~Server_Room(); int getId() const { return id; } @@ -63,7 +64,7 @@ public: const ServerInfo_Room &getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes = false, bool includeExternalData = true) const; int getGamesCreatedByUser(const QString &name) const; QList getGamesOfUser(const QString &name) const; - QList getChatHistory() { return chatHistory; } + QList & getChatHistory() { return chatHistory; } void addClient(Server_ProtocolHandler *client); void removeClient(Server_ProtocolHandler *client); From 696ae7e85f6bb0be02d8f474b597b019edcdf83a Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sat, 19 Dec 2015 11:48:54 +0100 Subject: [PATCH 39/49] Add gear icon to settings menu entry --- cockatrice/src/main.cpp | 4 ++++ cockatrice/src/window_main.cpp | 1 + 2 files changed, 5 insertions(+) diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index 2195beb5..cf4adb0b 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -132,6 +132,10 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationDomain("cockatrice.de"); QCoreApplication::setApplicationName("Cockatrice"); +#ifdef Q_OS_MAC + qApp->setAttribute(Qt::AA_DontShowIconsInMenus, true); +#endif + #ifdef Q_OS_MAC translationPath = qApp->applicationDirPath() + "/../Resources/translations"; #elif defined(Q_OS_WIN) diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 5bc42118..17eaf5ba 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -485,6 +485,7 @@ void MainWindow::retranslateUi() aFullScreen->setText(tr("&Full screen")); aRegister->setText(tr("&Register to server...")); aSettings->setText(tr("&Settings...")); + aSettings->setIcon(QPixmap("theme:icons/settings")); aExit->setText(tr("&Exit")); #if defined(__APPLE__) /* For OSX */ From 81cad7cefdb554fd32762b326e42ec7cccc077f8 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 20 Dec 2015 14:38:27 +0100 Subject: [PATCH 40/49] Possibly fix #1716 --- common/server_abstractuserinterface.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/server_abstractuserinterface.cpp b/common/server_abstractuserinterface.cpp index d00c5034..632bd320 100644 --- a/common/server_abstractuserinterface.cpp +++ b/common/server_abstractuserinterface.cpp @@ -86,6 +86,8 @@ void Server_AbstractUserInterface::joinPersistentGames(ResponseContainer &rc) QMutexLocker gameLocker(&game->gameMutex); Server_Player *player = game->getPlayers().value(pr.getPlayerId()); + if (!player) + continue; player->setUserInterface(this); playerAddedToGame(game->getGameId(), room->getId(), player->getPlayerId()); From 4d7795ca3af041c3deaf6119eaea90bab0bdc1c4 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 23 Sep 2015 21:55:41 +0200 Subject: [PATCH 41/49] Split pictuloader out of carddatabase --- cockatrice/CMakeLists.txt | 1 + cockatrice/src/abstractcarditem.cpp | 3 +- cockatrice/src/carddatabase.cpp | 558 +--------------------------- cockatrice/src/carddatabase.h | 92 +---- cockatrice/src/cardinfopicture.cpp | 5 +- cockatrice/src/cardinfowidget.cpp | 5 +- cockatrice/src/dlg_settings.cpp | 63 +++- cockatrice/src/dlg_settings.h | 13 +- cockatrice/src/pictureloader.cpp | 462 +++++++++++++++++++++++ cockatrice/src/pictureloader.h | 80 ++++ cockatrice/src/settingscache.cpp | 22 -- cockatrice/src/settingscache.h | 12 - cockatrice/src/tab_deck_editor.cpp | 3 +- cockatrice/src/tab_game.cpp | 5 +- cockatrice/src/thememanager.cpp | 2 + cockatrice/src/window_sets.cpp | 4 +- doc/cards.xsd | 2 +- oracle/CMakeLists.txt | 1 + oracle/src/oracleimporter.h | 1 + 19 files changed, 648 insertions(+), 686 deletions(-) create mode 100644 cockatrice/src/pictureloader.cpp create mode 100644 cockatrice/src/pictureloader.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index bbf3d119..9b951d62 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -96,6 +96,7 @@ SET(cockatrice_SOURCES src/qt-json/json.cpp src/soundengine.cpp src/pending_command.cpp + src/pictureloader.cpp src/shortcutssettings.cpp src/sequenceEdit/sequenceedit.cpp src/sequenceEdit/shortcutstab.cpp diff --git a/cockatrice/src/abstractcarditem.cpp b/cockatrice/src/abstractcarditem.cpp index dcd68fa1..7f9dfe23 100644 --- a/cockatrice/src/abstractcarditem.cpp +++ b/cockatrice/src/abstractcarditem.cpp @@ -9,6 +9,7 @@ #include "carddatabase.h" #include "cardinfowidget.h" #include "abstractcarditem.h" +#include "pictureloader.h" #include "settingscache.h" #include "main.h" #include "gamescene.h" @@ -93,7 +94,7 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS QPixmap translatedPixmap; // don't even spend time trying to load the picture if our size is too small if(translatedSize.width() > 10) - imageSource->getPixmap(translatedSize.toSize(), translatedPixmap); + PictureLoader::getPixmap(translatedPixmap, imageSource, translatedSize.toSize()); painter->save(); QColor bgColor = Qt::transparent; diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 3e5a1489..05fca91d 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -1,20 +1,14 @@ #include "carddatabase.h" +#include "pictureloader.h" #include "settingscache.h" #include "thememanager.h" #include +#include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include const int CardDatabase::versionNeeded = 3; @@ -88,38 +82,6 @@ void SetList::sortByKey() qSort(begin(), end(), KeyCompareFunctor()); } -class SetList::EnabledAndKeyCompareFunctor { -public: - inline bool operator()(CardSet *a, CardSet *b) const - { - if(a->getEnabled()) - { - if(b->getEnabled()) - { - // both enabled: sort by key - return a->getSortKey() < b->getSortKey(); - } else { - // only a enabled - return true; - } - } else { - if(b->getEnabled()) - { - // only b enabled - return false; - } else { - // both disabled: sort by key - return a->getSortKey() < b->getSortKey(); - } - } - } -}; - -void SetList::sortByEnabledAndKey() -{ - qSort(begin(), end(), EnabledAndKeyCompareFunctor()); -} - int SetList::getEnabledSetsNum() { int num=0; @@ -195,334 +157,6 @@ void SetList::guessSortKeys() } } -PictureToLoad::PictureToLoad(CardInfo *_card, bool _hq) - : card(_card), setIndex(0), hq(_hq) -{ - if (card) { - sortedSets = card->getSets(); - sortedSets.sortByEnabledAndKey(); - } -} - -bool PictureToLoad::nextSet() -{ - if (setIndex == sortedSets.size() - 1) - return false; - ++setIndex; - return true; -} - -QString PictureToLoad::getSetName() const -{ - if (setIndex < sortedSets.size()) - return sortedSets[setIndex]->getCorrectedShortName(); - else - return QString(""); -} - -CardSet *PictureToLoad::getCurrentSet() const -{ - if (setIndex < sortedSets.size()) - return sortedSets[setIndex]; - else - return 0; -} - -QStringList PictureLoader::md5Blacklist = QStringList() - << "db0c48db407a907c16ade38de048a441"; // card back returned by gatherer when card is not found - -PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent) - : QObject(parent), - _picsPath(__picsPath), picDownload(_picDownload), picDownloadHq(_picDownloadHq), - downloadRunning(false), loadQueueRunning(false) -{ - connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection); - - networkManager = new QNetworkAccessManager(this); - connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *))); -} - -PictureLoader::~PictureLoader() -{ - // This does not work with the destroyed() signal as this destructor is called after the main event loop is done. - thread()->quit(); -} - -void PictureLoader::processLoadQueue() -{ - if (loadQueueRunning) - return; - - loadQueueRunning = true; - forever { - mutex.lock(); - if (loadQueue.isEmpty()) { - mutex.unlock(); - loadQueueRunning = false; - return; - } - cardBeingLoaded = loadQueue.takeFirst(); - mutex.unlock(); - - QString setName = cardBeingLoaded.getSetName(); - QString correctedCardname = cardBeingLoaded.getCard()->getCorrectedName(); - qDebug() << "Trying to load picture (set: " << setName << " card: " << correctedCardname << ")"; - - //The list of paths to the folders in which to search for images - QList picsPaths = QList() << _picsPath + "/CUSTOM/" + correctedCardname; - - if(!setName.isEmpty()) - { - picsPaths << _picsPath + "/" + setName + "/" + correctedCardname - << _picsPath + "/downloadedPics/" + setName + "/" + correctedCardname; - } - - QImage image; - QImageReader imgReader; - imgReader.setDecideFormatFromContent(true); - bool found = false; - - //Iterates through the list of paths, searching for images with the desired name with any QImageReader-supported extension - for (int i = 0; i < picsPaths.length() && !found; i ++) { - imgReader.setFileName(picsPaths.at(i)); - if (imgReader.read(&image)) { - qDebug() << "Picture found on disk (set: " << setName << " card: " << correctedCardname << ")"; - emit imageLoaded(cardBeingLoaded.getCard(), image); - found = true; - break; - } - imgReader.setFileName(picsPaths.at(i) + ".full"); - if (imgReader.read(&image)) { - qDebug() << "Picture.full found on disk (set: " << setName << " card: " << correctedCardname << ")"; - emit imageLoaded(cardBeingLoaded.getCard(), image); - found = true; - } - } - - if (!found) { - if (picDownload) { - qDebug() << "Picture NOT found, trying to download (set: " << setName << " card: " << correctedCardname << ")"; - cardsToDownload.append(cardBeingLoaded); - cardBeingLoaded=0; - if (!downloadRunning) - startNextPicDownload(); - } else { - if (cardBeingLoaded.nextSet()) - { - qDebug() << "Picture NOT found and download disabled, moving to next set (newset: " << setName << " card: " << correctedCardname << ")"; - mutex.lock(); - loadQueue.prepend(cardBeingLoaded); - cardBeingLoaded=0; - mutex.unlock(); - } else { - qDebug() << "Picture NOT found, download disabled, no more sets to try: BAILING OUT (oldset: " << setName << " card: " << correctedCardname << ")"; - emit imageLoaded(cardBeingLoaded.getCard(), QImage()); - } - } - } - } -} - -QString PictureLoader::getPicUrl() -{ - if (!picDownload) return QString(""); - - CardInfo *card = cardBeingDownloaded.getCard(); - CardSet *set=cardBeingDownloaded.getCurrentSet(); - QString picUrl = QString(""); - - // if sets have been defined for the card, they can contain custom picUrls - if(set) - { - // first check if Hq is enabled and a custom Hq card url exists in cards.xml - if(picDownloadHq) - { - picUrl = card->getCustomPicURLHq(set->getShortName()); - if (!picUrl.isEmpty()) - return picUrl; - } - - // then, test for a custom, non-Hq card url in cards.xml - picUrl = card->getCustomPicURL(set->getShortName()); - if (!picUrl.isEmpty()) - return picUrl; - } - - // if a card has a muid, use the default url; if not, use the fallback - int muid = set ? card->getMuId(set->getShortName()) : 0; - if(muid) - picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl(); - else - picUrl = picDownloadHq ? settingsCache->getPicUrlHqFallback() : settingsCache->getPicUrlFallback(); - - picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); - picUrl.replace("!name_lower!", QUrl::toPercentEncoding(card->getCorrectedName().toLower())); - picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(muid))); - if (set) - { - picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); - picUrl.replace("!setcode_lower!", QUrl::toPercentEncoding(set->getShortName().toLower())); - picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); - picUrl.replace("!setname_lower!", QUrl::toPercentEncoding(set->getLongName().toLower())); - } - - if ( - picUrl.contains("!name!") || - picUrl.contains("!name_lower!") || - picUrl.contains("!setcode!") || - picUrl.contains("!setcode_lower!") || - picUrl.contains("!setname!") || - picUrl.contains("!setname_lower!") || - picUrl.contains("!cardid!") - ) - { - qDebug() << "Insufficient card data to download" << card->getName() << "Url:" << picUrl; - return QString(""); - } - - return picUrl; -} - -void PictureLoader::startNextPicDownload() -{ - if (cardsToDownload.isEmpty()) { - cardBeingDownloaded = 0; - downloadRunning = false; - return; - } - - downloadRunning = true; - - cardBeingDownloaded = cardsToDownload.takeFirst(); - - QString picUrl = getPicUrl(); - if (picUrl.isEmpty()) { - downloadRunning = false; - picDownloadFailed(); - } else { - QUrl url(picUrl); - - QNetworkRequest req(url); - qDebug() << "starting picture download:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url(); - networkManager->get(req); - } -} - -void PictureLoader::picDownloadFailed() -{ - if (cardBeingDownloaded.nextSet()) - { - qDebug() << "Picture NOT found, download failed, moving to next set (newset: " << cardBeingDownloaded.getSetName() << " card: " << cardBeingDownloaded.getCard()->getCorrectedName() << ")"; - mutex.lock(); - loadQueue.prepend(cardBeingDownloaded); - mutex.unlock(); - emit startLoadQueue(); - } else { - qDebug() << "Picture NOT found, download failed, no more sets to try: BAILING OUT (oldset: " << cardBeingDownloaded.getSetName() << " card: " << cardBeingDownloaded.getCard()->getCorrectedName() << ")"; - cardBeingDownloaded = 0; - emit imageLoaded(cardBeingDownloaded.getCard(), QImage()); - } -} - -void PictureLoader::picDownloadFinished(QNetworkReply *reply) -{ - QString picsPath = _picsPath; - if (reply->error()) { - qDebug() << "Download failed:" << reply->errorString(); - } - - int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (statusCode == 301 || statusCode == 302) { - QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); - QNetworkRequest req(redirectUrl); - qDebug() << "following redirect:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url(); - networkManager->get(req); - return; - } - - const QByteArray &picData = reply->peek(reply->size()); //peek is used to keep the data in the buffer for use by QImageReader - - // check if the image is blacklisted - QString md5sum = QCryptographicHash::hash(picData, QCryptographicHash::Md5).toHex(); - if(md5Blacklist.contains(md5sum)) - { - qDebug() << "Picture downloaded, but blacklisted (" << md5sum << "), will consider it as not found"; - picDownloadFailed(); - reply->deleteLater(); - startNextPicDownload(); - return; - } - - QImage testImage; - - QImageReader imgReader; - imgReader.setDecideFormatFromContent(true); - imgReader.setDevice(reply); - QString extension = "." + imgReader.format(); //the format is determined prior to reading the QImageReader data into a QImage object, as that wipes the QImageReader buffer - if (extension == ".jpeg") - extension = ".jpg"; - - if (imgReader.read(&testImage)) { - QString setName = cardBeingDownloaded.getSetName(); - if(!setName.isEmpty()) - { - if (!QDir().mkpath(picsPath + "/downloadedPics/" + setName)) { - qDebug() << picsPath + "/downloadedPics/" + setName + " could not be created."; - return; - } - - QFile newPic(picsPath + "/downloadedPics/" + setName + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + extension); - if (!newPic.open(QIODevice::WriteOnly)) - return; - newPic.write(picData); - newPic.close(); - } - - emit imageLoaded(cardBeingDownloaded.getCard(), testImage); - } else { - picDownloadFailed(); - } - - reply->deleteLater(); - startNextPicDownload(); -} - -void PictureLoader::loadImage(CardInfo *card) -{ - QMutexLocker locker(&mutex); - - // avoid queueing the same card more than once - if(card == cardBeingLoaded.getCard() || card == cardBeingDownloaded.getCard()) - return; - - foreach(PictureToLoad pic, loadQueue) - { - if(pic.getCard() == card) - return; - } - - loadQueue.append(PictureToLoad(card)); - emit startLoadQueue(); -} - -void PictureLoader::setPicsPath(const QString &path) -{ - QMutexLocker locker(&mutex); - _picsPath = path; -} - -void PictureLoader::setPicDownload(bool _picDownload) -{ - QMutexLocker locker(&mutex); - picDownload = _picDownload; -} - -void PictureLoader::setPicDownloadHq(bool _picDownloadHq) -{ - QMutexLocker locker(&mutex); - picDownloadHq = _picDownloadHq; -} - CardInfo::CardInfo(CardDatabase *_db, const QString &_name, bool _isToken, @@ -539,7 +173,6 @@ CardInfo::CardInfo(CardDatabase *_db, int _tableRow, const SetList &_sets, const QStringMap &_customPicURLs, - const QStringMap &_customPicURLsHq, MuidMap _muIds ) : db(_db), @@ -556,7 +189,6 @@ CardInfo::CardInfo(CardDatabase *_db, upsideDownArt(_upsideDownArt), loyalty(_loyalty), customPicURLs(_customPicURLs), - customPicURLsHq(_customPicURLsHq), muIds(_muIds), cipt(_cipt), tableRow(_tableRow) @@ -570,7 +202,7 @@ CardInfo::CardInfo(CardDatabase *_db, CardInfo::~CardInfo() { - clearPixmapCache(); + PictureLoader::clearPixmapCache(this); } QString CardInfo::getMainCardType() const @@ -617,82 +249,6 @@ void CardInfo::addToSet(CardSet *set) sets << set; } -void CardInfo::loadPixmap(QPixmap &pixmap) -{ - if(QPixmapCache::find(pixmapCacheKey, &pixmap)) - return; - - pixmap = QPixmap(); - - if (getName().isEmpty()) { - pixmap = QPixmap("theme:cardback"); - return; - } - - db->loadImage(this); -} - -void CardInfo::imageLoaded(const QImage &image) -{ - if (!image.isNull()) { - if(upsideDownArt) - { - QImage mirrorImage = image.mirrored(true, true); - QPixmapCache::insert(pixmapCacheKey, QPixmap::fromImage(mirrorImage)); - } else { - QPixmapCache::insert(pixmapCacheKey, QPixmap::fromImage(image)); - } - emit pixmapUpdated(); - } -} - -void CardInfo::getPixmap(QSize size, QPixmap &pixmap) -{ - QString key = QLatin1String("card_") + name + QLatin1Char('_') + QString::number(size.width()) + QString::number(size.height()); - if(QPixmapCache::find(key, &pixmap)) - return; - - QPixmap bigPixmap; - loadPixmap(bigPixmap); - if (bigPixmap.isNull()) { - if (getName().isEmpty()) { - pixmap = pixmap = QPixmap("theme:cardback"); - } else { - pixmap = QPixmap(); // null - return; - } - } - - pixmap = bigPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); - QPixmapCache::insert(key, pixmap); -} - -void CardInfo::clearPixmapCache() -{ - //qDebug() << "Deleting pixmap for" << name; - QPixmapCache::remove(pixmapCacheKey); -} - -void CardInfo::clearPixmapCacheMiss() -{ - QPixmap pixmap; - if(!QPixmapCache::find(pixmapCacheKey, &pixmap)) - return; - - if (pixmap.isNull()) - clearPixmapCache(); -} - -void CardInfo::updatePixmapCache() -{ - qDebug() << "Updating pixmap cache for" << name; - clearPixmapCache(); - QPixmap tmp; - loadPixmap(tmp); - - emit pixmapUpdated(); -} - QString CardInfo::simplifyName(const QString &name) { QString simpleName(name); @@ -731,10 +287,6 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) if(!tmpString.isEmpty()) xml.writeAttribute("picURL", tmpString); - tmpString = info->getCustomPicURLHq(tmpSet); - if(!tmpString.isEmpty()) - xml.writeAttribute("picURLHq", tmpString); - xml.writeCharacters(tmpSet); xml.writeEndElement(); } @@ -769,35 +321,19 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) CardDatabase::CardDatabase(QObject *parent) : QObject(parent), noCard(0), loadStatus(NotLoaded) { - connect(settingsCache, SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged())); connect(settingsCache, SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabase())); connect(settingsCache, SIGNAL(tokenDatabasePathChanged()), this, SLOT(loadTokenDatabase())); - connect(settingsCache, SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged())); - connect(settingsCache, SIGNAL(picDownloadHqChanged()), this, SLOT(picDownloadHqChanged())); loadCardDatabase(); loadTokenDatabase(); - pictureLoaderThread = new QThread; - pictureLoader = new PictureLoader(settingsCache->getPicsPath(), settingsCache->getPicDownload(), settingsCache->getPicDownloadHq()); - pictureLoader->moveToThread(pictureLoaderThread); - connect(pictureLoader, SIGNAL(imageLoaded(CardInfo *, const QImage &)), this, SLOT(imageLoaded(CardInfo *, const QImage &))); - pictureLoaderThread->start(QThread::LowPriority); - noCard = new CardInfo(this); - QPixmap tmp; - noCard->loadPixmap(tmp); // cache pixmap for card back - connect(themeManager, SIGNAL(themeChanged()), noCard, SLOT(updatePixmapCache())); } CardDatabase::~CardDatabase() { clear(); delete noCard; - - pictureLoader->deleteLater(); - pictureLoaderThread->wait(); - delete pictureLoaderThread; } void CardDatabase::clear() @@ -839,6 +375,15 @@ CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) return getCardFromMap(cards, cardName, createIfNotFound); } +QList CardDatabase::getCards(const QStringList &cardNames) +{ + QList cardInfos; + foreach(QString cardName, cardNames) + cardInfos.append(getCardFromMap(cards, cardName, false)); + + return cardInfos; +} + CardInfo *CardDatabase::getCardBySimpleName(const QString &cardName, bool createIfNotFound) { QString simpleName = CardInfo::simplifyName(cardName); return getCardFromMap(simpleNameCards, simpleName, createIfNotFound); @@ -866,21 +411,6 @@ SetList CardDatabase::getSetList() const return result; } -void CardDatabase::clearPixmapCache() -{ - // This also clears the cards in simpleNameCards since they point to the - // same object. - QHashIterator i(cards); - while (i.hasNext()) { - i.next(); - i.value()->clearPixmapCache(); - } - if (noCard) - noCard->clearPixmapCache(); - - QPixmapCache::clear(); -} - void CardDatabase::loadSetsFromXml(QXmlStreamReader &xml) { while (!xml.atEnd()) { @@ -918,7 +448,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) if (xml.name() == "card") { QString name, manacost, cmc, type, pt, text; QStringList colors, relatedCards; - QStringMap customPicURLs, customPicURLsHq; + QStringMap customPicURLs; MuidMap muids; SetList sets; int tableRow = 0; @@ -951,9 +481,6 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) if (attrs.hasAttribute("picURL")) { customPicURLs[setName] = attrs.value("picURL").toString(); } - if (attrs.hasAttribute("picURLHq")) { - customPicURLsHq[setName] = attrs.value("picURLHq").toString(); - } } else if (xml.name() == "color") colors << xml.readElementText(); else if (xml.name() == "related") @@ -971,24 +498,24 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) } if (isToken == tokens) { - addCard(new CardInfo(this, name, isToken, manacost, cmc, type, pt, text, colors, relatedCards, upsideDown, loyalty, cipt, tableRow, sets, customPicURLs, customPicURLsHq, muids)); + addCard(new CardInfo(this, name, isToken, manacost, cmc, type, pt, text, colors, relatedCards, upsideDown, loyalty, cipt, tableRow, sets, customPicURLs, muids)); } } } } CardInfo *CardDatabase::getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound) { - if (cardName.isEmpty()) - return noCard; - else if (cardMap.contains(cardName)) + if (cardMap.contains(cardName)) return cardMap.value(cardName); - else if (createIfNotFound) { + + if (createIfNotFound) { CardInfo *newCard = new CardInfo(this, cardName, true); newCard->addToSet(getSet(CardDatabase::TOKENS_SETNAME)); cardMap.insert(cardName, newCard); return newCard; - } else - return 0; + } + + return noCard; } LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) @@ -1061,26 +588,6 @@ bool CardDatabase::saveToFile(const QString &fileName, bool tokens) return true; } -void CardDatabase::picDownloadChanged() -{ - pictureLoader->setPicDownload(settingsCache->getPicDownload()); - if (settingsCache->getPicDownload()) { - QHashIterator cardIterator(cards); - while (cardIterator.hasNext()) - cardIterator.next().value()->clearPixmapCacheMiss(); - } -} - -void CardDatabase::picDownloadHqChanged() -{ - pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq()); - if (settingsCache->getPicDownloadHq()) { - QHashIterator cardIterator(cards); - while (cardIterator.hasNext()) - cardIterator.next().value()->clearPixmapCacheMiss(); - } -} - void CardDatabase::emitCardListChanged() { emit cardListChanged(); @@ -1159,31 +666,6 @@ QStringList CardDatabase::getAllMainCardTypes() const return types.toList(); } -void CardDatabase::cacheCardPixmaps(const QStringList &cardNames) -{ - QPixmap tmp; - // never cache more than 300 cards at once for a single deck - int max = qMin(cardNames.size(), 300); - for (int i = 0; i < max; ++i) - getCard(cardNames[i])->loadPixmap(tmp); -} - -void CardDatabase::loadImage(CardInfo *card) -{ - pictureLoader->loadImage(card); -} - -void CardDatabase::imageLoaded(CardInfo *card, QImage image) -{ - card->imageLoaded(image); -} - -void CardDatabase::picsPathChanged() -{ - pictureLoader->setPicsPath(settingsCache->getPicsPath()); - clearPixmapCache(); -} - void CardDatabase::checkUnknownSets() { SetList sets = getSetList(); diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index df3995da..ed06f64d 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -8,17 +8,9 @@ #include #include #include -#include -#include -#include -#include -#include class CardDatabase; class CardInfo; -class QNetworkAccessManager; -class QNetworkReply; -class QNetworkRequest; typedef QMap QStringMap; @@ -55,9 +47,7 @@ public: class SetList : public QList { private: class KeyCompareFunctor; - class EnabledAndKeyCompareFunctor; public: - void sortByEnabledAndKey(); void sortByKey(); void guessSortKeys(); void enableAllUnknown(); @@ -67,53 +57,6 @@ public: int getUnknownSetsNum(); }; -class PictureToLoad { -private: - CardInfo *card; - SetList sortedSets; - int setIndex; - bool hq; -public: - PictureToLoad(CardInfo *_card = 0, bool _hq = true); - CardInfo *getCard() const { return card; } - CardSet *getCurrentSet() const; - QString getSetName() const; - bool nextSet(); - bool getHq() const { return hq; } - void setHq(bool _hq) { hq = _hq; } -}; - -class PictureLoader : public QObject { - Q_OBJECT -private: - QString _picsPath; - QList loadQueue; - QMutex mutex; - QNetworkAccessManager *networkManager; - QList cardsToDownload; - PictureToLoad cardBeingLoaded; - PictureToLoad cardBeingDownloaded; - bool picDownload, picDownloadHq, downloadRunning, loadQueueRunning; - void startNextPicDownload(); - QString getPicUrl(); - static QStringList md5Blacklist; -public: - PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent = 0); - ~PictureLoader(); - void setPicsPath(const QString &path); - void setPicDownload(bool _picDownload); - void setPicDownloadHq(bool _picDownloadHq); - void loadImage(CardInfo *card); -private slots: - void picDownloadFinished(QNetworkReply *reply); - void picDownloadFailed(); -public slots: - void processLoadQueue(); -signals: - void startLoadQueue(); - void imageLoaded(CardInfo *card, const QImage &image); -}; - class CardInfo : public QObject { Q_OBJECT private: @@ -138,7 +81,7 @@ private: QStringList relatedCards; bool upsideDownArt; int loyalty; - QStringMap customPicURLs, customPicURLsHq; + QStringMap customPicURLs; MuidMap muIds; bool cipt; int tableRow; @@ -160,7 +103,6 @@ public: int _tableRow = 0, const SetList &_sets = SetList(), const QStringMap &_customPicURLs = QStringMap(), - const QStringMap &_customPicURLsHq = QStringMap(), MuidMap muids = MuidMap() ); ~CardInfo(); @@ -173,6 +115,7 @@ public: const QString &getCardType() const { return cardtype; } const QString &getPowTough() const { return powtough; } const QString &getText() const { return text; } + const QString &getPixmapCacheKey() const { return pixmapCacheKey; } const int &getLoyalty() const { return loyalty; } bool getCipt() const { return cipt; } void setManaCost(const QString &_manaCost) { manacost = _manaCost; emit cardInfoChanged(this); } @@ -185,7 +128,6 @@ public: const QStringList &getRelatedCards() const { return relatedCards; } bool getUpsideDownArt() const { return upsideDownArt; } QString getCustomPicURL(const QString &set) const { return customPicURLs.value(set); } - QString getCustomPicURLHq(const QString &set) const { return customPicURLsHq.value(set); } int getMuId(const QString &set) const { return muIds.value(set); } QString getMainCardType() const; QString getCorrectedName() const; @@ -193,22 +135,15 @@ public: void setTableRow(int _tableRow) { tableRow = _tableRow; } void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); } void setCustomPicURL(const QString &_set, const QString &_customPicURL) { customPicURLs.insert(_set, _customPicURL); } - void setCustomPicURLHq(const QString &_set, const QString &_customPicURL) { customPicURLsHq.insert(_set, _customPicURL); } void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); } void addToSet(CardSet *set); - void loadPixmap(QPixmap &pixmap); - void getPixmap(QSize size, QPixmap &pixmap); - void clearPixmapCache(); - void clearPixmapCacheMiss(); - void imageLoaded(const QImage &image); + void emitPixmapUpdated() { emit pixmapUpdated(); } /** * Simplify a name to have no punctuation and lowercase all letters, for * less strict name-matching. */ static QString simplifyName(const QString &name); -public slots: - void updatePixmapCache(); signals: void pixmapUpdated(); void cardInfoChanged(CardInfo *card); @@ -237,10 +172,11 @@ protected: */ SetNameMap sets; + /* + * A dummy card returned by getCard() ad a fallback + */ CardInfo *noCard; - QThread *pictureLoaderThread; - PictureLoader *pictureLoader; LoadStatus loadStatus; bool detectedFirstRun; private: @@ -258,13 +194,17 @@ public: void clear(); void addCard(CardInfo *card); void removeCard(CardInfo *card); - CardInfo *getCard(const QString &cardName = QString(), bool createIfNotFound = true); + /* + * Get card object by name. Ensured to return a valid CardInfo * object; check noCard + */ + CardInfo *getCard(const QString &cardName = QString(), bool createIfNotFound = false); + QList getCards(const QStringList &cardNames); /* * Get a card by its simple name. The name will be simplified in this * function, so you don't need to simplify it beforehand. */ - CardInfo *getCardBySimpleName(const QString &cardName = QString(), bool createIfNotFound = true); + CardInfo *getCardBySimpleName(const QString &cardName = QString(), bool createIfNotFound = false); CardSet *getSet(const QString &setName); QList getCardList() const { return cards.values(); } @@ -275,20 +215,12 @@ public: QStringList getAllMainCardTypes() const; LoadStatus getLoadStatus() const { return loadStatus; } bool getLoadSuccess() const { return loadStatus == Ok; } - void cacheCardPixmaps(const QStringList &cardNames); - void loadImage(CardInfo *card); bool hasDetectedFirstRun(); public slots: - void clearPixmapCache(); LoadStatus loadCardDatabase(const QString &path, bool tokens = false); void loadCustomCardDatabases(const QString &path); void emitCardListChanged(); private slots: - void imageLoaded(CardInfo *card, QImage image); - void picDownloadChanged(); - void picDownloadHqChanged(); - void picsPathChanged(); - void loadCardDatabase(); void loadTokenDatabase(); signals: diff --git a/cockatrice/src/cardinfopicture.cpp b/cockatrice/src/cardinfopicture.cpp index 9bce273e..d4f7e179 100644 --- a/cockatrice/src/cardinfopicture.cpp +++ b/cockatrice/src/cardinfopicture.cpp @@ -6,6 +6,7 @@ #include "carditem.h" #include "carddatabase.h" +#include "pictureloader.h" #include "main.h" CardInfoPicture::CardInfoPicture(QWidget *parent) @@ -40,13 +41,13 @@ void CardInfoPicture::updatePixmap() void CardInfoPicture::loadPixmap() { if(info) - info->getPixmap(size(), resizedPixmap); + PictureLoader::getPixmap(resizedPixmap, info, size()); else resizedPixmap = QPixmap(); if (resizedPixmap.isNull()) - db->getCard()->getPixmap(size(), resizedPixmap); + PictureLoader::getPixmap(resizedPixmap, db->getCard(), size()); } void CardInfoPicture::paintEvent(QPaintEvent *) diff --git a/cockatrice/src/cardinfowidget.cpp b/cockatrice/src/cardinfowidget.cpp index 8ee5663c..807221c6 100644 --- a/cockatrice/src/cardinfowidget.cpp +++ b/cockatrice/src/cardinfowidget.cpp @@ -8,6 +8,7 @@ #include "cardinfowidget.h" #include "carditem.h" #include "carddatabase.h" +#include "pictureloader.h" #include "main.h" #include "settingscache.h" @@ -195,10 +196,10 @@ void CardInfoWidget::updatePixmap() return; QPixmap resizedPixmap; - info->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio), resizedPixmap); + PictureLoader::getPixmap(resizedPixmap, info, QSize(pixmapWidth, pixmapWidth * aspectRatio)); if (resizedPixmap.isNull()) - getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio), resizedPixmap); + PictureLoader::getPixmap(resizedPixmap, getCard(), QSize(pixmapWidth, pixmapWidth * aspectRatio)); cardPicture->setPixmap(resizedPixmap); } diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 787882ac..c05648bc 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -44,7 +44,6 @@ GeneralSettingsPage::GeneralSettingsPage() } picDownloadCheckBox.setChecked(settingsCache->getPicDownload()); - picDownloadHqCheckBox.setChecked(settingsCache->getPicDownloadHq()); updateNotificationCheckBox.setChecked(settingsCache->getNotifyAboutUpdates()); pixmapCacheEdit.setMinimum(PIXMAPCACHE_SIZE_MIN); @@ -53,20 +52,22 @@ GeneralSettingsPage::GeneralSettingsPage() pixmapCacheEdit.setSingleStep(64); pixmapCacheEdit.setValue(settingsCache->getPixmapCacheSize()); pixmapCacheEdit.setSuffix(" MB"); - picDownloadHqCheckBox.setChecked(settingsCache->getPicDownloadHq()); - picDownloadCheckBox.setChecked(settingsCache->getPicDownload()); - highQualityURLEdit = new QLineEdit(settingsCache->getPicUrlHq()); - highQualityURLEdit->setEnabled(settingsCache->getPicDownloadHq()); + defaultUrlEdit = new QLineEdit(settingsCache->getPicUrl()); + fallbackUrlEdit = new QLineEdit(settingsCache->getPicUrlFallback()); connect(&clearDownloadedPicsButton, SIGNAL(clicked()), this, SLOT(clearDownloadedPicsButtonClicked())); connect(&languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int))); connect(&picDownloadCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownload(int))); - connect(&picDownloadHqCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownloadHq(int))); connect(&pixmapCacheEdit, SIGNAL(valueChanged(int)), settingsCache, SLOT(setPixmapCacheSize(int))); - connect(&picDownloadHqCheckBox, SIGNAL(clicked(bool)), this, SLOT(setEnabledStatus(bool))); connect(&updateNotificationCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setNotifyAboutUpdate(int))); - connect(highQualityURLEdit, SIGNAL(textChanged(QString)), settingsCache, SLOT(setPicUrlHq(QString))); + connect(&picDownloadCheckBox, SIGNAL(clicked(bool)), this, SLOT(setEnabledStatus(bool))); + connect(defaultUrlEdit, SIGNAL(textChanged(QString)), settingsCache, SLOT(setPicUrl(QString))); + connect(fallbackUrlEdit, SIGNAL(textChanged(QString)), settingsCache, SLOT(setPicUrlFallback(QString))); + connect(&defaultUrlRestoreButton, SIGNAL(clicked()), this, SLOT(defaultUrlRestoreButtonClicked())); + connect(&fallbackUrlRestoreButton, SIGNAL(clicked()), this, SLOT(fallbackUrlRestoreButtonClicked())); + + setEnabledStatus(settingsCache->getPicDownload()); QGridLayout *personalGrid = new QGridLayout; personalGrid->addWidget(&languageLabel, 0, 0); @@ -74,15 +75,18 @@ GeneralSettingsPage::GeneralSettingsPage() personalGrid->addWidget(&pixmapCacheLabel, 1, 0); personalGrid->addWidget(&pixmapCacheEdit, 1, 1); personalGrid->addWidget(&updateNotificationCheckBox, 2, 0); - personalGrid->addWidget(&picDownloadCheckBox, 3, 0); - personalGrid->addWidget(&picDownloadHqCheckBox, 4, 0); - personalGrid->addWidget(&highQualityURLLabel, 5, 0); - personalGrid->addWidget(highQualityURLEdit, 5, 1); - personalGrid->addWidget(&highQualityURLLinkLabel, 6, 1); - personalGrid->addWidget(&clearDownloadedPicsButton, 6, 0); + personalGrid->addWidget(&picDownloadCheckBox, 3, 0, 1, 3); + personalGrid->addWidget(&defaultUrlLabel, 4, 0, 1, 1); + personalGrid->addWidget(defaultUrlEdit, 4, 1, 1, 1); + personalGrid->addWidget(&defaultUrlRestoreButton, 4, 2, 1, 1); + personalGrid->addWidget(&fallbackUrlLabel, 5, 0, 1, 1); + personalGrid->addWidget(fallbackUrlEdit, 5, 1, 1, 1); + personalGrid->addWidget(&fallbackUrlRestoreButton, 5, 2, 1, 1); + personalGrid->addWidget(&urlLinkLabel, 6, 1, 1, 1); + personalGrid->addWidget(&clearDownloadedPicsButton, 7, 0, 1, 3); - highQualityURLLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); - highQualityURLLinkLabel.setOpenExternalLinks(true); + urlLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); + urlLinkLabel.setOpenExternalLinks(true); personalGroupBox = new QGroupBox; personalGroupBox->setLayout(personalGrid); @@ -154,6 +158,20 @@ QString GeneralSettingsPage::languageName(const QString &qmFile) return translator.translate("GeneralSettingsPage", "English"); } +void GeneralSettingsPage::defaultUrlRestoreButtonClicked() +{ + QString path = PIC_URL_DEFAULT; + defaultUrlEdit->setText(path); + settingsCache->setPicUrl(path); +} + +void GeneralSettingsPage::fallbackUrlRestoreButtonClicked() +{ + QString path = PIC_URL_FALLBACK; + fallbackUrlEdit->setText(path); + settingsCache->setPicUrlFallback(path); +} + void GeneralSettingsPage::deckPathButtonClicked() { QString path = QFileDialog::getExistingDirectory(this, tr("Choose path")); @@ -238,7 +256,6 @@ void GeneralSettingsPage::retranslateUi() personalGroupBox->setTitle(tr("Personal settings")); languageLabel.setText(tr("Language:")); picDownloadCheckBox.setText(tr("Download card pictures on the fly")); - picDownloadHqCheckBox.setText(tr("Download card pictures from a custom URL")); pathsGroupBox->setTitle(tr("Paths")); deckPathLabel.setText(tr("Decks directory:")); replaysPathLabel.setText(tr("Replays directory:")); @@ -246,15 +263,21 @@ void GeneralSettingsPage::retranslateUi() cardDatabasePathLabel.setText(tr("Card database:")); tokenDatabasePathLabel.setText(tr("Token database:")); pixmapCacheLabel.setText(tr("Picture cache size:")); - highQualityURLLabel.setText(tr("Custom Card Download URL:")); - highQualityURLLinkLabel.setText(QString("%2").arg(WIKI_CUSTOM_PIC_URL).arg(tr("How to set a custom picture url"))); + defaultUrlLabel.setText(tr("Primary download URL:")); + fallbackUrlLabel.setText(tr("Fallback download URL:")); + urlLinkLabel.setText(QString("%2").arg(WIKI_CUSTOM_PIC_URL).arg(tr("How to set a custom picture url"))); clearDownloadedPicsButton.setText(tr("Reset/Clear Downloaded Pictures")); updateNotificationCheckBox.setText(tr("Notify when new client features are available")); + defaultUrlRestoreButton.setText(tr("Reset")); + fallbackUrlRestoreButton.setText(tr("Reset")); } void GeneralSettingsPage::setEnabledStatus(bool status) { - highQualityURLEdit->setEnabled(status); + defaultUrlEdit->setEnabled(status); + fallbackUrlEdit->setEnabled(status); + defaultUrlRestoreButton.setEnabled(status); + fallbackUrlRestoreButton.setEnabled(status); } AppearanceSettingsPage::AppearanceSettingsPage() diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index 7b746564..52d3c9ba 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -45,6 +45,8 @@ private slots: void tokenDatabasePathButtonClicked(); void languageBoxChanged(int index); void setEnabledStatus(bool); + void defaultUrlRestoreButtonClicked(); + void fallbackUrlRestoreButtonClicked(); private: QStringList findQmFiles(); QString languageName(const QString &qmFile); @@ -53,13 +55,13 @@ private: QLineEdit *picsPathEdit; QLineEdit *cardDatabasePathEdit; QLineEdit *tokenDatabasePathEdit; - QLineEdit *highQualityURLEdit; + QLineEdit *defaultUrlEdit; + QLineEdit *fallbackUrlEdit; QSpinBox pixmapCacheEdit; QGroupBox *personalGroupBox; QGroupBox *pathsGroupBox; QComboBox languageBox; QCheckBox picDownloadCheckBox; - QCheckBox picDownloadHqCheckBox; QCheckBox updateNotificationCheckBox; QLabel languageLabel; QLabel pixmapCacheLabel; @@ -68,9 +70,12 @@ private: QLabel picsPathLabel; QLabel cardDatabasePathLabel; QLabel tokenDatabasePathLabel; - QLabel highQualityURLLabel; - QLabel highQualityURLLinkLabel; + QLabel defaultUrlLabel; + QLabel fallbackUrlLabel; + QLabel urlLinkLabel; QPushButton clearDownloadedPicsButton; + QPushButton defaultUrlRestoreButton; + QPushButton fallbackUrlRestoreButton; }; class AppearanceSettingsPage : public AbstractSettingsPage { diff --git a/cockatrice/src/pictureloader.cpp b/cockatrice/src/pictureloader.cpp new file mode 100644 index 00000000..9eee3847 --- /dev/null +++ b/cockatrice/src/pictureloader.cpp @@ -0,0 +1,462 @@ +#include "pictureloader.h" +#include "carddatabase.h" +#include "main.h" +#include "settingscache.h" +#include "thememanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class PictureToLoad::EnabledAndKeyCompareFunctor { +public: + inline bool operator()(CardSet *a, CardSet *b) const + { + if(a->getEnabled()) + { + if(b->getEnabled()) + { + // both enabled: sort by key + return a->getSortKey() < b->getSortKey(); + } else { + // only a enabled + return true; + } + } else { + if(b->getEnabled()) + { + // only b enabled + return false; + } else { + // both disabled: sort by key + return a->getSortKey() < b->getSortKey(); + } + } + } +}; + +PictureToLoad::PictureToLoad(CardInfo *_card) + : card(_card), setIndex(0) +{ + if (card) { + sortedSets = card->getSets(); + qSort(sortedSets.begin(), sortedSets.end(), EnabledAndKeyCompareFunctor()); + } +} + +bool PictureToLoad::nextSet() +{ + if (setIndex == sortedSets.size() - 1) + return false; + ++setIndex; + return true; +} + +QString PictureToLoad::getSetName() const +{ + if (setIndex < sortedSets.size()) + return sortedSets[setIndex]->getCorrectedShortName(); + else + return QString(""); +} + +CardSet *PictureToLoad::getCurrentSet() const +{ + if (setIndex < sortedSets.size()) + return sortedSets[setIndex]; + else + return 0; +} + +QStringList PictureLoader::md5Blacklist = QStringList() + << "db0c48db407a907c16ade38de048a441"; // card back returned by gatherer when card is not found + +PictureLoader::PictureLoader() + : QObject(0), + downloadRunning(false), loadQueueRunning(false) +{ + picsPath = settingsCache->getPicsPath(); + picDownload = settingsCache->getPicDownload(); + + connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection); + connect(settingsCache, SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged())); + connect(settingsCache, SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged())); + + networkManager = new QNetworkAccessManager(this); + connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *))); + + pictureLoaderThread = new QThread; + pictureLoaderThread->start(QThread::LowPriority); + moveToThread(pictureLoaderThread); +} + +PictureLoader::~PictureLoader() +{ + pictureLoaderThread->deleteLater(); +} + +void PictureLoader::processLoadQueue() +{ + if (loadQueueRunning) + return; + + loadQueueRunning = true; + forever { + mutex.lock(); + if (loadQueue.isEmpty()) { + mutex.unlock(); + loadQueueRunning = false; + return; + } + cardBeingLoaded = loadQueue.takeFirst(); + mutex.unlock(); + + QString setName = cardBeingLoaded.getSetName(); + QString correctedCardname = cardBeingLoaded.getCard()->getCorrectedName(); + qDebug() << "Trying to load picture (set: " << setName << " card: " << correctedCardname << ")"; + + //The list of paths to the folders in which to search for images + QList picsPaths = QList() << picsPath + "/CUSTOM/" + correctedCardname; + + if(!setName.isEmpty()) + { + picsPaths << picsPath + "/" + setName + "/" + correctedCardname + << picsPath + "/downloadedPics/" + setName + "/" + correctedCardname; + } + + QImage image; + QImageReader imgReader; + imgReader.setDecideFormatFromContent(true); + bool found = false; + + //Iterates through the list of paths, searching for images with the desired name with any QImageReader-supported extension + for (int i = 0; i < picsPaths.length() && !found; i ++) { + imgReader.setFileName(picsPaths.at(i)); + if (imgReader.read(&image)) { + qDebug() << "Picture found on disk (set: " << setName << " card: " << correctedCardname << ")"; + imageLoaded(cardBeingLoaded.getCard(), image); + found = true; + break; + } + imgReader.setFileName(picsPaths.at(i) + ".full"); + if (imgReader.read(&image)) { + qDebug() << "Picture.full found on disk (set: " << setName << " card: " << correctedCardname << ")"; + imageLoaded(cardBeingLoaded.getCard(), image); + found = true; + } + } + + if (!found) { + if (picDownload) { + qDebug() << "Picture NOT found, trying to download (set: " << setName << " card: " << correctedCardname << ")"; + cardsToDownload.append(cardBeingLoaded); + cardBeingLoaded=0; + if (!downloadRunning) + startNextPicDownload(); + } else { + if (cardBeingLoaded.nextSet()) + { + qDebug() << "Picture NOT found and download disabled, moving to next set (newset: " << setName << " card: " << correctedCardname << ")"; + mutex.lock(); + loadQueue.prepend(cardBeingLoaded); + cardBeingLoaded=0; + mutex.unlock(); + } else { + qDebug() << "Picture NOT found, download disabled, no more sets to try: BAILING OUT (oldset: " << setName << " card: " << correctedCardname << ")"; + imageLoaded(cardBeingLoaded.getCard(), QImage()); + } + } + } + } +} + +QString PictureLoader::getPicUrl() +{ + if (!picDownload) return QString(""); + + CardInfo *card = cardBeingDownloaded.getCard(); + CardSet *set=cardBeingDownloaded.getCurrentSet(); + QString picUrl = QString(""); + + // if sets have been defined for the card, they can contain custom picUrls + if(set) + { + picUrl = card->getCustomPicURL(set->getShortName()); + if (!picUrl.isEmpty()) + return picUrl; + } + + // if a card has a muid, use the default url; if not, use the fallback + int muid = set ? card->getMuId(set->getShortName()) : 0; + picUrl = muid ? settingsCache->getPicUrl() : settingsCache->getPicUrlFallback(); + + picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); + picUrl.replace("!name_lower!", QUrl::toPercentEncoding(card->getCorrectedName().toLower())); + picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(muid))); + if (set) + { + picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); + picUrl.replace("!setcode_lower!", QUrl::toPercentEncoding(set->getShortName().toLower())); + picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); + picUrl.replace("!setname_lower!", QUrl::toPercentEncoding(set->getLongName().toLower())); + } + + if ( + picUrl.contains("!name!") || + picUrl.contains("!name_lower!") || + picUrl.contains("!setcode!") || + picUrl.contains("!setcode_lower!") || + picUrl.contains("!setname!") || + picUrl.contains("!setname_lower!") || + picUrl.contains("!cardid!") + ) + { + qDebug() << "Insufficient card data to download" << card->getName() << "Url:" << picUrl; + return QString(""); + } + + return picUrl; +} + +void PictureLoader::startNextPicDownload() +{ + if (cardsToDownload.isEmpty()) { + cardBeingDownloaded = 0; + downloadRunning = false; + return; + } + + downloadRunning = true; + + cardBeingDownloaded = cardsToDownload.takeFirst(); + + QString picUrl = getPicUrl(); + if (picUrl.isEmpty()) { + downloadRunning = false; + picDownloadFailed(); + } else { + QUrl url(picUrl); + + QNetworkRequest req(url); + qDebug() << "starting picture download:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url(); + networkManager->get(req); + } +} + +void PictureLoader::picDownloadFailed() +{ + if (cardBeingDownloaded.nextSet()) + { + qDebug() << "Picture NOT found, download failed, moving to next set (newset: " << cardBeingDownloaded.getSetName() << " card: " << cardBeingDownloaded.getCard()->getCorrectedName() << ")"; + mutex.lock(); + loadQueue.prepend(cardBeingDownloaded); + mutex.unlock(); + emit startLoadQueue(); + } else { + qDebug() << "Picture NOT found, download failed, no more sets to try: BAILING OUT (oldset: " << cardBeingDownloaded.getSetName() << " card: " << cardBeingDownloaded.getCard()->getCorrectedName() << ")"; + cardBeingDownloaded = 0; + imageLoaded(cardBeingDownloaded.getCard(), QImage()); + } +} + +void PictureLoader::picDownloadFinished(QNetworkReply *reply) +{ + if (reply->error()) { + qDebug() << "Download failed:" << reply->errorString(); + } + + int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 301 || statusCode == 302) { + QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + QNetworkRequest req(redirectUrl); + qDebug() << "following redirect:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url(); + networkManager->get(req); + return; + } + + const QByteArray &picData = reply->peek(reply->size()); //peek is used to keep the data in the buffer for use by QImageReader + + // check if the image is blacklisted + QString md5sum = QCryptographicHash::hash(picData, QCryptographicHash::Md5).toHex(); + if(md5Blacklist.contains(md5sum)) + { + qDebug() << "Picture downloaded, but blacklisted (" << md5sum << "), will consider it as not found"; + picDownloadFailed(); + reply->deleteLater(); + startNextPicDownload(); + return; + } + + QImage testImage; + + QImageReader imgReader; + imgReader.setDecideFormatFromContent(true); + imgReader.setDevice(reply); + QString extension = "." + imgReader.format(); //the format is determined prior to reading the QImageReader data into a QImage object, as that wipes the QImageReader buffer + if (extension == ".jpeg") + extension = ".jpg"; + + if (imgReader.read(&testImage)) { + QString setName = cardBeingDownloaded.getSetName(); + if(!setName.isEmpty()) + { + if (!QDir().mkpath(picsPath + "/downloadedPics/" + setName)) { + qDebug() << picsPath + "/downloadedPics/" + setName + " could not be created."; + return; + } + + QFile newPic(picsPath + "/downloadedPics/" + setName + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + extension); + if (!newPic.open(QIODevice::WriteOnly)) + return; + newPic.write(picData); + newPic.close(); + } + + imageLoaded(cardBeingDownloaded.getCard(), testImage); + } else { + picDownloadFailed(); + } + + reply->deleteLater(); + startNextPicDownload(); +} + +void PictureLoader::enqueueImageLoad(CardInfo *card) +{ + QMutexLocker locker(&mutex); + + // avoid queueing the same card more than once + if(card == 0 || card == cardBeingLoaded.getCard() || card == cardBeingDownloaded.getCard()) + return; + + foreach(PictureToLoad pic, loadQueue) + { + if(pic.getCard() == card) + return; + } + + loadQueue.append(PictureToLoad(card)); + emit startLoadQueue(); +} + +void PictureLoader::picDownloadChanged() +{ + QMutexLocker locker(&mutex); + picDownload = settingsCache->getPicDownload(); + + QPixmapCache::clear(); +} + +void PictureLoader::picsPathChanged() +{ + QMutexLocker locker(&mutex); + picsPath = settingsCache->getPicsPath(); + + QPixmapCache::clear(); +} + +void PictureLoader::internalGetCardBackPixmap(QPixmap &pixmap, QSize size) +{ + QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + QString::number(size.height()); + if(!QPixmapCache::find(backCacheKey, &pixmap)) + { +qDebug() << "cache fail for" << backCacheKey; + pixmap = QPixmap("theme:cardback").scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QPixmapCache::insert(backCacheKey, pixmap); + } +} + +void PictureLoader::getPixmap(QPixmap &pixmap, CardInfo *card, QSize size) +{ + if(card) + { + if (card->getName().isEmpty()) { + internalGetCardBackPixmap(pixmap, size); + return; + } + + // search for an exact size copy of the picure in cache + QString key = card->getPixmapCacheKey(); + QString sizekey = key + QLatin1Char('_') + QString::number(size.width()) + QString::number(size.height()); + if(QPixmapCache::find(sizekey, &pixmap)) + return; + + // load the image and create a copy of the correct size + QPixmap bigPixmap; + if(QPixmapCache::find(key, &bigPixmap)) + { + pixmap = bigPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QPixmapCache::insert(sizekey, pixmap); + return; + } + } + + // load a temporary back picture + internalGetCardBackPixmap(pixmap, size); + + if(card) + { + // add the card to the load queue + getInstance().enqueueImageLoad(card); + } +} + + +void PictureLoader::imageLoaded(CardInfo *card, const QImage &image) +{ + if(image.isNull()) + return; + + if(card->getUpsideDownArt()) + { + QImage mirrorImage = image.mirrored(true, true); + QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap::fromImage(mirrorImage)); + } else { + QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap::fromImage(image)); + } + + card->emitPixmapUpdated(); +} + +void PictureLoader::clearPixmapCache(CardInfo *card) +{ + //qDebug() << "Deleting pixmap for" << name; + if(card) + QPixmapCache::remove(card->getPixmapCacheKey()); +} + +void PictureLoader::clearPixmapCache() +{ + QPixmapCache::clear(); +} + +void PictureLoader::cacheCardPixmaps(QList cards) +{ + QPixmap tmp; + // never cache more than 300 cards at once for a single deck + int max = qMin(cards.size(), 300); + for (int i = 0; i < max; ++i) + { + CardInfo * card = cards.at(i); + if(!card) + continue; + + QString key = card->getPixmapCacheKey(); + if(QPixmapCache::find(key, &tmp)) + continue; + + getInstance().enqueueImageLoad(card); + } +} diff --git a/cockatrice/src/pictureloader.h b/cockatrice/src/pictureloader.h new file mode 100644 index 00000000..0cdad4d5 --- /dev/null +++ b/cockatrice/src/pictureloader.h @@ -0,0 +1,80 @@ +#ifndef PICTURELOADER_H +#define PICTURELOADER_H + +#include +#include +#include +#include + +class CardInfo; +class CardSet; +class QNetworkAccessManager; +class QNetworkReply; +class QThread; + +class PictureToLoad { +private: + class EnabledAndKeyCompareFunctor; + + CardInfo *card; + QList sortedSets; + int setIndex; + bool hq; +public: + PictureToLoad(CardInfo *_card = 0); + CardInfo *getCard() const { return card; } + CardSet *getCurrentSet() const; + QString getSetName() const; + bool nextSet(); +}; + +class PictureLoader : public QObject { +Q_OBJECT +public: + static PictureLoader& getInstance() + { + static PictureLoader instance; + return instance; + } +private: + PictureLoader(); + ~PictureLoader(); + // Don't implement + PictureLoader(PictureLoader const&); + void operator=(PictureLoader const&); + + static QStringList md5Blacklist; + + QThread *pictureLoaderThread; + QString picsPath; + QList loadQueue; + QMutex mutex; + QNetworkAccessManager *networkManager; + QList cardsToDownload; + PictureToLoad cardBeingLoaded; + PictureToLoad cardBeingDownloaded; + bool picDownload, downloadRunning, loadQueueRunning; + void startNextPicDownload(); + void imageLoaded(CardInfo *card, const QImage &image); + QString getPicUrl(); +public: + void enqueueImageLoad(CardInfo *card); + static void getPixmap(QPixmap &pixmap, CardInfo *card, QSize size); + static void clearPixmapCache(CardInfo *card); + static void clearPixmapCache(); + static void cacheCardPixmaps(QList cards); +protected: + static void internalGetCardBackPixmap(QPixmap &pixmap, QSize size); +private slots: + void picDownloadFinished(QNetworkReply *reply); + void picDownloadFailed(); + + void picDownloadChanged(); + void picsPathChanged(); +public slots: + void processLoadQueue(); +signals: + void startLoadQueue(); +}; + +#endif diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 8a8226f0..dba3b134 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -165,12 +165,9 @@ SettingsCache::SettingsCache() pixmapCacheSize = PIXMAPCACHE_SIZE_DEFAULT; picDownload = settings->value("personal/picturedownload", true).toBool(); - picDownloadHq = settings->value("personal/picturedownloadhq", true).toBool(); picUrl = settings->value("personal/picUrl", PIC_URL_DEFAULT).toString(); - picUrlHq = settings->value("personal/picUrlHq", PIC_URL_HQ_DEFAULT).toString(); picUrlFallback = settings->value("personal/picUrlFallback", PIC_URL_FALLBACK).toString(); - picUrlHqFallback = settings->value("personal/picUrlHqFallback", PIC_URL_HQ_FALLBACK).toString(); mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray(); notificationsEnabled = settings->value("interface/notificationsenabled", true).toBool(); @@ -325,37 +322,18 @@ void SettingsCache::setPicDownload(int _picDownload) emit picDownloadChanged(); } -void SettingsCache::setPicDownloadHq(int _picDownloadHq) -{ - picDownloadHq = _picDownloadHq; - settings->setValue("personal/picturedownloadhq", picDownloadHq); - emit picDownloadHqChanged(); -} - void SettingsCache::setPicUrl(const QString &_picUrl) { picUrl = _picUrl; settings->setValue("personal/picUrl", picUrl); } -void SettingsCache::setPicUrlHq(const QString &_picUrlHq) -{ - picUrlHq = _picUrlHq; - settings->setValue("personal/picUrlHq", picUrlHq); -} - void SettingsCache::setPicUrlFallback(const QString &_picUrlFallback) { picUrlFallback = _picUrlFallback; settings->setValue("personal/picUrlFallback", picUrlFallback); } -void SettingsCache::setPicUrlHqFallback(const QString &_picUrlHqFallback) -{ - picUrlHqFallback = _picUrlHqFallback; - settings->setValue("personal/picUrlHqFallback", picUrlHqFallback); -} - void SettingsCache::setNotificationsEnabled(int _notificationsEnabled) { notificationsEnabled = _notificationsEnabled; diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index 722ccac1..036f6966 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -14,8 +14,6 @@ // the falbacks are used for cards without a muid #define PIC_URL_DEFAULT "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&type=card" #define PIC_URL_FALLBACK "http://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card" -#define PIC_URL_HQ_DEFAULT "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&type=card" -#define PIC_URL_HQ_FALLBACK "http://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card" // size should be a multiple of 64 #define PIXMAPCACHE_SIZE_DEFAULT 2047 #define PIXMAPCACHE_SIZE_MIN 64 @@ -32,7 +30,6 @@ signals: void tokenDatabasePathChanged(); void themeChanged(); void picDownloadChanged(); - void picDownloadHqChanged(); void displayCardNamesChanged(); void horizontalHandChanged(); void handJustificationChanged(); @@ -60,7 +57,6 @@ private: QString deckPath, replaysPath, picsPath, cardDatabasePath, tokenDatabasePath, themeName; bool notifyAboutUpdates; bool picDownload; - bool picDownloadHq; bool notificationsEnabled; bool spectatorNotificationsEnabled; bool doubleClickToPlay; @@ -87,9 +83,7 @@ private: bool ignoreUnregisteredUsers; bool ignoreUnregisteredUserMessages; QString picUrl; - QString picUrlHq; QString picUrlFallback; - QString picUrlHqFallback; QString clientID; int pixmapCacheSize; bool scaleCards; @@ -127,7 +121,6 @@ public: QString getChatMentionColor() const { return chatMentionColor; } QString getChatHighlightColor() const { return chatHighlightColor; } bool getPicDownload() const { return picDownload; } - bool getPicDownloadHq() const { return picDownloadHq; } bool getNotificationsEnabled() const { return notificationsEnabled; } bool getSpectatorNotificationsEnabled() const { return spectatorNotificationsEnabled; } bool getNotifyAboutUpdates() const { return notifyAboutUpdates; } @@ -160,9 +153,7 @@ public: bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; } bool getIgnoreUnregisteredUserMessages() const { return ignoreUnregisteredUserMessages; } QString getPicUrl() const { return picUrl; } - QString getPicUrlHq() const { return picUrlHq; } QString getPicUrlFallback() const { return picUrlFallback; } - QString getPicUrlHqFallback() const { return picUrlHqFallback; } int getPixmapCacheSize() const { return pixmapCacheSize; } bool getScaleCards() const { return scaleCards; } bool getShowMessagePopup() const { return showMessagePopups; } @@ -204,7 +195,6 @@ public slots: void setChatMentionColor(const QString &_chatMentionColor); void setChatHighlightColor(const QString &_chatHighlightColor); void setPicDownload(int _picDownload); - void setPicDownloadHq(int _picDownloadHq); void setNotificationsEnabled(int _notificationsEnabled); void setSpectatorNotificationsEnabled(int _spectatorNotificationsEnabled); void setDoubleClickToPlay(int _doubleClickToPlay); @@ -231,9 +221,7 @@ public slots: void setIgnoreUnregisteredUsers(int _ignoreUnregisteredUsers); void setIgnoreUnregisteredUserMessages(int _ignoreUnregisteredUserMessages); void setPicUrl(const QString &_picUrl); - void setPicUrlHq(const QString &_picUrlHq); void setPicUrlFallback(const QString &_picUrlFallback); - void setPicUrlHqFallback(const QString &_picUrlHqFallback); void setPixmapCacheSize(const int _pixmapCacheSize); void setCardScaling(const int _scaleCards); void setShowMessagePopups(const int _showMessagePopups); diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index d750e83b..1e27434b 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -26,6 +26,7 @@ #include "tab_deck_editor.h" #include "window_sets.h" #include "carddatabase.h" +#include "pictureloader.h" #include "carddatabasemodel.h" #include "decklistmodel.h" #include "cardinfowidget.h" @@ -1058,7 +1059,7 @@ void TabDeckEditor::setDeck(DeckLoader *_deck) deckView->expandAll(); setModified(false); - db->cacheCardPixmaps(deckModel->getDeckList()->getCardList()); + PictureLoader::cacheCardPixmaps(db->getCards(deckModel->getDeckList()->getCardList())); deckView->expandAll(); setModified(false); } diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index 5b5c3eda..dd52f64a 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -32,6 +32,7 @@ #include "main.h" #include "settingscache.h" #include "carddatabase.h" +#include "pictureloader.h" #include "replay_timeline_widget.h" #include "lineeditcompleter.h" @@ -242,7 +243,7 @@ void DeckViewContainer::deckSelectFinished(const Response &r) { const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext); DeckLoader newDeck(QString::fromStdString(resp.deck())); - db->cacheCardPixmaps(newDeck.getCardList()); + PictureLoader::cacheCardPixmaps(db->getCards(newDeck.getCardList())); setDeck(newDeck); } @@ -1043,7 +1044,7 @@ void TabGame::eventGameStateChanged(const Event_GameStateChanged &event, int /*e DeckViewContainer *deckViewContainer = deckViewContainers.value(playerId); if (playerInfo.has_deck_list()) { DeckLoader newDeck(QString::fromStdString(playerInfo.deck_list())); - db->cacheCardPixmaps(newDeck.getCardList()); + PictureLoader::cacheCardPixmaps(db->getCards(newDeck.getCardList())); deckViewContainer->setDeck(newDeck); player->setDeck(newDeck); } diff --git a/cockatrice/src/thememanager.cpp b/cockatrice/src/thememanager.cpp index 40ecdf2f..390d859a 100644 --- a/cockatrice/src/thememanager.cpp +++ b/cockatrice/src/thememanager.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #if QT_VERSION < 0x050000 #include @@ -115,6 +116,7 @@ void ThemeManager::themeChangedSlot() playerBgBrush = loadBrush(PLAYERZONE_BG_NAME, QColor(200, 200, 200)); stackBgBrush = loadBrush(STACKZONE_BG_NAME, QColor(113, 43, 43)); + QPixmapCache::clear(); emit themeChanged(); } diff --git a/cockatrice/src/window_sets.cpp b/cockatrice/src/window_sets.cpp index b647e0a9..5e0a49e2 100644 --- a/cockatrice/src/window_sets.cpp +++ b/cockatrice/src/window_sets.cpp @@ -1,6 +1,8 @@ #include "window_sets.h" #include "setsmodel.h" +#include "pictureloader.h" #include "main.h" + #include #include #include @@ -123,7 +125,7 @@ WndSets::~WndSets() void WndSets::actSave() { model->save(db); - db->clearPixmapCache(); + PictureLoader::clearPixmapCache(); QMessageBox::information(this, tr("Success"), tr("The sets database has been saved successfully.")); close(); } diff --git a/doc/cards.xsd b/doc/cards.xsd index d10032a7..11a51307 100644 --- a/doc/cards.xsd +++ b/doc/cards.xsd @@ -29,12 +29,12 @@ - + diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 90558cfc..7851c1d2 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -12,6 +12,7 @@ SET(oracle_SOURCES src/oraclewizard.cpp src/oracleimporter.cpp ../cockatrice/src/carddatabase.cpp + ../cockatrice/src/pictureloader.cpp ../cockatrice/src/settingscache.cpp ../cockatrice/src/shortcutssettings.cpp ../cockatrice/src/settings/carddatabasesettings.cpp diff --git a/oracle/src/oracleimporter.h b/oracle/src/oracleimporter.h index 888eabaf..17092ea5 100644 --- a/oracle/src/oracleimporter.h +++ b/oracle/src/oracleimporter.h @@ -2,6 +2,7 @@ #define ORACLEIMPORTER_H #include +#include #include From e721280bc096d0dda705246b5caa2f8ec727d3d1 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 15 Oct 2015 22:40:36 +0200 Subject: [PATCH 42/49] Implemented proposed changes --- cockatrice/src/pictureloader.cpp | 126 +++++++++++++++++-------------- cockatrice/src/pictureloader.h | 4 +- 2 files changed, 73 insertions(+), 57 deletions(-) diff --git a/cockatrice/src/pictureloader.cpp b/cockatrice/src/pictureloader.cpp index 9eee3847..bd5c9aa1 100644 --- a/cockatrice/src/pictureloader.cpp +++ b/cockatrice/src/pictureloader.cpp @@ -20,8 +20,16 @@ #include #include -class PictureToLoad::EnabledAndKeyCompareFunctor { +// never cache more than 300 cards at once for a single deck +#define CACHED_CARD_PER_DECK_MAX 300 + +class PictureToLoad::SetDownloadPriorityComparator { public: + /* + * Returns true if a has higher download priority than b + * Enabled sets have priority over disabled sets + * Both groups follows the user-defined order + */ inline bool operator()(CardSet *a, CardSet *b) const { if(a->getEnabled()) @@ -52,7 +60,7 @@ PictureToLoad::PictureToLoad(CardInfo *_card) { if (card) { sortedSets = card->getSets(); - qSort(sortedSets.begin(), sortedSets.end(), EnabledAndKeyCompareFunctor()); + qSort(sortedSets.begin(), sortedSets.end(), SetDownloadPriorityComparator()); } } @@ -127,61 +135,65 @@ void PictureLoader::processLoadQueue() QString correctedCardname = cardBeingLoaded.getCard()->getCorrectedName(); qDebug() << "Trying to load picture (set: " << setName << " card: " << correctedCardname << ")"; - //The list of paths to the folders in which to search for images - QList picsPaths = QList() << picsPath + "/CUSTOM/" + correctedCardname; + if(cardImageExistsOnDisk(setName, correctedCardname)) + continue; - if(!setName.isEmpty()) - { - picsPaths << picsPath + "/" + setName + "/" + correctedCardname - << picsPath + "/downloadedPics/" + setName + "/" + correctedCardname; - } - - QImage image; - QImageReader imgReader; - imgReader.setDecideFormatFromContent(true); - bool found = false; - - //Iterates through the list of paths, searching for images with the desired name with any QImageReader-supported extension - for (int i = 0; i < picsPaths.length() && !found; i ++) { - imgReader.setFileName(picsPaths.at(i)); - if (imgReader.read(&image)) { - qDebug() << "Picture found on disk (set: " << setName << " card: " << correctedCardname << ")"; - imageLoaded(cardBeingLoaded.getCard(), image); - found = true; - break; - } - imgReader.setFileName(picsPaths.at(i) + ".full"); - if (imgReader.read(&image)) { - qDebug() << "Picture.full found on disk (set: " << setName << " card: " << correctedCardname << ")"; - imageLoaded(cardBeingLoaded.getCard(), image); - found = true; - } - } - - if (!found) { - if (picDownload) { - qDebug() << "Picture NOT found, trying to download (set: " << setName << " card: " << correctedCardname << ")"; - cardsToDownload.append(cardBeingLoaded); + if (picDownload) { + qDebug() << "Picture NOT found, trying to download (set: " << setName << " card: " << correctedCardname << ")"; + cardsToDownload.append(cardBeingLoaded); + cardBeingLoaded=0; + if (!downloadRunning) + startNextPicDownload(); + } else { + if (cardBeingLoaded.nextSet()) + { + qDebug() << "Picture NOT found and download disabled, moving to next set (newset: " << setName << " card: " << correctedCardname << ")"; + mutex.lock(); + loadQueue.prepend(cardBeingLoaded); cardBeingLoaded=0; - if (!downloadRunning) - startNextPicDownload(); + mutex.unlock(); } else { - if (cardBeingLoaded.nextSet()) - { - qDebug() << "Picture NOT found and download disabled, moving to next set (newset: " << setName << " card: " << correctedCardname << ")"; - mutex.lock(); - loadQueue.prepend(cardBeingLoaded); - cardBeingLoaded=0; - mutex.unlock(); - } else { - qDebug() << "Picture NOT found, download disabled, no more sets to try: BAILING OUT (oldset: " << setName << " card: " << correctedCardname << ")"; - imageLoaded(cardBeingLoaded.getCard(), QImage()); - } + qDebug() << "Picture NOT found, download disabled, no more sets to try: BAILING OUT (oldset: " << setName << " card: " << correctedCardname << ")"; + imageLoaded(cardBeingLoaded.getCard(), QImage()); } } } } +bool PictureLoader::cardImageExistsOnDisk(QString & setName, QString & correctedCardname) +{ + QImage image; + QImageReader imgReader; + imgReader.setDecideFormatFromContent(true); + + //The list of paths to the folders in which to search for images + QList picsPaths = QList() << picsPath + "/CUSTOM/" + correctedCardname; + + if(!setName.isEmpty()) + { + picsPaths << picsPath + "/" + setName + "/" + correctedCardname + << picsPath + "/downloadedPics/" + setName + "/" + correctedCardname; + } + + //Iterates through the list of paths, searching for images with the desired name with any QImageReader-supported extension + for (int i = 0; i < picsPaths.length(); i ++) { + imgReader.setFileName(picsPaths.at(i)); + if (imgReader.read(&image)) { + qDebug() << "Picture found on disk (set: " << setName << " card: " << correctedCardname << ")"; + imageLoaded(cardBeingLoaded.getCard(), image); + return true; + } + imgReader.setFileName(picsPaths.at(i) + ".full"); + if (imgReader.read(&image)) { + qDebug() << "Picture.full found on disk (set: " << setName << " card: " << correctedCardname << ")"; + imageLoaded(cardBeingLoaded.getCard(), image); + return true; + } + } + + return false; +} + QString PictureLoader::getPicUrl() { if (!picDownload) return QString(""); @@ -271,6 +283,12 @@ void PictureLoader::picDownloadFailed() } } +bool PictureLoader::imageIsBlackListed(const QByteArray &picData) +{ + QString md5sum = QCryptographicHash::hash(picData, QCryptographicHash::Md5).toHex(); + return md5Blacklist.contains(md5sum); +} + void PictureLoader::picDownloadFinished(QNetworkReply *reply) { if (reply->error()) { @@ -288,11 +306,9 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply) const QByteArray &picData = reply->peek(reply->size()); //peek is used to keep the data in the buffer for use by QImageReader - // check if the image is blacklisted - QString md5sum = QCryptographicHash::hash(picData, QCryptographicHash::Md5).toHex(); - if(md5Blacklist.contains(md5sum)) + if(imageIsBlackListed(picData)) { - qDebug() << "Picture downloaded, but blacklisted (" << md5sum << "), will consider it as not found"; + qDebug() << "Picture downloaded, but blacklisted, will consider it as not found"; picDownloadFailed(); reply->deleteLater(); startNextPicDownload(); @@ -432,7 +448,6 @@ void PictureLoader::imageLoaded(CardInfo *card, const QImage &image) void PictureLoader::clearPixmapCache(CardInfo *card) { - //qDebug() << "Deleting pixmap for" << name; if(card) QPixmapCache::remove(card->getPixmapCacheKey()); } @@ -445,8 +460,7 @@ void PictureLoader::clearPixmapCache() void PictureLoader::cacheCardPixmaps(QList cards) { QPixmap tmp; - // never cache more than 300 cards at once for a single deck - int max = qMin(cards.size(), 300); + int max = qMin(cards.size(), CACHED_CARD_PER_DECK_MAX); for (int i = 0; i < max; ++i) { CardInfo * card = cards.at(i); diff --git a/cockatrice/src/pictureloader.h b/cockatrice/src/pictureloader.h index 0cdad4d5..9a93a1c3 100644 --- a/cockatrice/src/pictureloader.h +++ b/cockatrice/src/pictureloader.h @@ -14,7 +14,7 @@ class QThread; class PictureToLoad { private: - class EnabledAndKeyCompareFunctor; + class SetDownloadPriorityComparator; CardInfo *card; QList sortedSets; @@ -57,6 +57,8 @@ private: void startNextPicDownload(); void imageLoaded(CardInfo *card, const QImage &image); QString getPicUrl(); + bool cardImageExistsOnDisk(QString & setName, QString & correctedCardname); + bool imageIsBlackListed(const QByteArray &picData); public: void enqueueImageLoad(CardInfo *card); static void getPixmap(QPixmap &pixmap, CardInfo *card, QSize size); From 4b1c59424e1cd1feed4d838ad3e7baee18a376df Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sat, 26 Dec 2015 19:57:20 +0100 Subject: [PATCH 43/49] Implement reverse-related tag for cards --- cockatrice/src/carddatabase.cpp | 33 +++++++++++++++++++++++++++++++-- cockatrice/src/carddatabase.h | 12 ++++++++++++ cockatrice/src/main.cpp | 3 +++ cockatrice/src/player.cpp | 28 ++++++++++++++++++++++++++-- cockatrice/src/window_main.cpp | 1 + doc/cards.xsd | 1 + oracle/src/oracleimporter.cpp | 9 ++++++--- oracle/src/oracleimporter.h | 2 +- 8 files changed, 81 insertions(+), 8 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 05fca91d..41a4976e 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -167,6 +167,7 @@ CardInfo::CardInfo(CardDatabase *_db, const QString &_text, const QStringList &_colors, const QStringList &_relatedCards, + const QStringList &_reverseRelatedCards, bool _upsideDownArt, int _loyalty, bool _cipt, @@ -186,6 +187,7 @@ CardInfo::CardInfo(CardDatabase *_db, text(_text), colors(_colors), relatedCards(_relatedCards), + reverseRelatedCards(_reverseRelatedCards), upsideDownArt(_upsideDownArt), loyalty(_loyalty), customPicURLs(_customPicURLs), @@ -298,6 +300,10 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) for (int i = 0; i < related.size(); i++) xml.writeTextElement("related", related[i]); + const QStringList &reverseRelated = info->getReverseRelatedCards(); + for (int i = 0; i < reverseRelated.size(); i++) + xml.writeTextElement("reverse-related", reverseRelated[i]); + xml.writeTextElement("manacost", info->getManaCost()); xml.writeTextElement("cmc", info->getCmc()); xml.writeTextElement("type", info->getCardType()); @@ -447,7 +453,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) break; if (xml.name() == "card") { QString name, manacost, cmc, type, pt, text; - QStringList colors, relatedCards; + QStringList colors, relatedCards, reverseRelatedCards; QStringMap customPicURLs; MuidMap muids; SetList sets; @@ -485,6 +491,8 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) colors << xml.readElementText(); else if (xml.name() == "related") relatedCards << xml.readElementText(); + else if (xml.name() == "reverse-related") + reverseRelatedCards << xml.readElementText(); else if (xml.name() == "tablerow") tableRow = xml.readElementText().toInt(); else if (xml.name() == "cipt") @@ -498,7 +506,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) } if (isToken == tokens) { - addCard(new CardInfo(this, name, isToken, manacost, cmc, type, pt, text, colors, relatedCards, upsideDown, loyalty, cipt, tableRow, sets, customPicURLs, muids)); + addCard(new CardInfo(this, name, isToken, manacost, cmc, type, pt, text, colors, relatedCards, reverseRelatedCards, upsideDown, loyalty, cipt, tableRow, sets, customPicURLs, muids)); } } } @@ -642,6 +650,27 @@ void CardDatabase::loadCustomCardDatabases(const QString &path) } } +void CardDatabase::refreshCachedReverseRelatedCards() +{ + foreach(CardInfo * card, cards) + card->resetReverseRelatedCards2Me(); + + foreach(CardInfo * card, cards) + { + if(card->getReverseRelatedCards().isEmpty()) + continue; + + QString relatedCardName = card->getName(); + foreach(QString targetCard, card->getReverseRelatedCards()) + { + if (!cards.contains(targetCard)) + continue; + + cards.value(targetCard)->addReverseRelatedCards2Me(relatedCardName); + } + } +} + QStringList CardDatabase::getAllColors() const { QSet colors; diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index ed06f64d..1cb8544a 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -78,7 +78,12 @@ private: QString powtough; QString text; QStringList colors; + // the cards i'm related to QStringList relatedCards; + // the card i'm reverse-related to + QStringList reverseRelatedCards; + // the cards thare are reverse-related to me + QStringList reverseRelatedCardsToMe; bool upsideDownArt; int loyalty; QStringMap customPicURLs; @@ -86,6 +91,7 @@ private: bool cipt; int tableRow; QString pixmapCacheKey; + public: CardInfo(CardDatabase *_db, const QString &_name = QString(), @@ -97,6 +103,7 @@ public: const QString &_text = QString(), const QStringList &_colors = QStringList(), const QStringList &_relatedCards = QStringList(), + const QStringList &_reverseRelatedCards = QStringList(), bool _upsideDownArt = false, int _loyalty = 0, bool _cipt = false, @@ -126,6 +133,10 @@ public: void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); } const QStringList &getColors() const { return colors; } const QStringList &getRelatedCards() const { return relatedCards; } + const QStringList &getReverseRelatedCards() const { return reverseRelatedCards; } + const QStringList &getReverseRelatedCards2Me() const { return reverseRelatedCardsToMe; } + void resetReverseRelatedCards2Me() { reverseRelatedCardsToMe = QStringList(); } + void addReverseRelatedCards2Me(QString & cardName) { reverseRelatedCardsToMe.append(cardName); } bool getUpsideDownArt() const { return upsideDownArt; } QString getCustomPicURL(const QString &set) const { return customPicURLs.value(set); } int getMuId(const QString &set) const { return muIds.value(set); } @@ -216,6 +227,7 @@ public: LoadStatus getLoadStatus() const { return loadStatus; } bool getLoadSuccess() const { return loadStatus == Ok; } bool hasDetectedFirstRun(); + void refreshCachedReverseRelatedCards(); public slots: LoadStatus loadCardDatabase(const QString &path, bool tokens = false); void loadCustomCardDatabases(const QString &path); diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index cf4adb0b..10d01bfc 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -200,6 +200,9 @@ int main(int argc, char *argv[]) qDebug() << "Could not create " + dataDir + "/customsets folder."; } + // when all the cards have been loaded, resolve the reverse-related tags + db->refreshCachedReverseRelatedCards(); + if (settingsValid()) { qDebug("main(): starting main program"); diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index ce33e24a..135b4056 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -1146,7 +1146,8 @@ void Player::actCreateRelatedCard() cmd.set_annotation(settingsCache->getAnnotateTokens() ? cardInfo->getText().toStdString() : QString().toStdString()); cmd.set_destroy_on_zone_change(true); cmd.set_target_zone(sourceCard->getZone()->getName().toStdString()); - cmd.set_target_card_id(sourceCard->getId()); + if(!cardInfo->getIsToken()) + cmd.set_target_card_id(sourceCard->getId()); sendGameCommand(cmd); } @@ -2362,7 +2363,8 @@ void Player::updateCardMenu(CardItem *card) cardMenu->addAction(aPeek); QStringList relatedCards = card->getInfo()->getRelatedCards(); - if(relatedCards.size()) + QStringList reverserelatedCards2Me = card->getInfo()->getReverseRelatedCards2Me(); + if(relatedCards.size() || reverserelatedCards2Me.size()) { QMenu * createRelatedCardMenu = cardMenu->addMenu(tr("Cr&eate related card")); @@ -2370,6 +2372,11 @@ void Player::updateCardMenu(CardItem *card) QAction *a = createRelatedCardMenu->addAction(relatedCards.at(i)); connect(a, SIGNAL(triggered()), this, SLOT(actCreateRelatedCard())); } + + for (int i = 0; i < reverserelatedCards2Me.size(); ++i) { + QAction *a = createRelatedCardMenu->addAction(reverserelatedCards2Me.at(i)); + connect(a, SIGNAL(triggered()), this, SLOT(actCreateRelatedCard())); + } } cardMenu->addSeparator(); cardMenu->addAction(aAttach); @@ -2393,6 +2400,23 @@ void Player::updateCardMenu(CardItem *card) } else if (card->getZone()->getName() == "stack") { cardMenu->addAction(aDrawArrow); cardMenu->addMenu(moveMenu); + + QStringList relatedCards = card->getInfo()->getRelatedCards(); + QStringList reverserelatedCards2Me = card->getInfo()->getReverseRelatedCards2Me(); + if(relatedCards.size() || reverserelatedCards2Me.size()) + { + QMenu * createRelatedCardMenu = cardMenu->addMenu(tr("Cr&eate related card")); + + for (int i = 0; i < relatedCards.size(); ++i) { + QAction *a = createRelatedCardMenu->addAction(relatedCards.at(i)); + connect(a, SIGNAL(triggered()), this, SLOT(actCreateRelatedCard())); + } + + for (int i = 0; i < reverserelatedCards2Me.size(); ++i) { + QAction *a = createRelatedCardMenu->addAction(reverserelatedCards2Me.at(i)); + connect(a, SIGNAL(triggered()), this, SLOT(actCreateRelatedCard())); + } + } } else { cardMenu->addAction(aPlay); cardMenu->addAction(aPlayFacedown); diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 17eaf5ba..91a52223 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -798,6 +798,7 @@ void MainWindow::cardUpdateFinished(int, QProcess::ExitStatus) // this will force a database reload settingsCache->setCardDatabasePath(settingsCache->getCardDatabasePath()); + settingsCache->setTokenDatabasePath(settingsCache->getTokenDatabasePath()); } void MainWindow::refreshShortcuts() diff --git a/doc/cards.xsd b/doc/cards.xsd index 11a51307..1e145e32 100644 --- a/doc/cards.xsd +++ b/doc/cards.xsd @@ -35,6 +35,7 @@ + diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 8eef9a2f..7659f465 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -67,6 +67,7 @@ CardInfo *OracleImporter::addCard(const QString &setName, const QString &cardText, const QStringList & colors, const QStringList & relatedCards, + const QStringList & reverseRelatedCards, bool upsideDown ) { @@ -95,7 +96,7 @@ CardInfo *OracleImporter::addCard(const QString &setName, bool cipt = cardText.contains("Hideaway") || (cardText.contains(cardName + " enters the battlefield tapped") && !cardText.contains(cardName + " enters the battlefield tapped unless")); // insert the card and its properties - card = new CardInfo(this, cardName, isToken, cardCost, cmc, cardType, cardPT, cardText, colors, relatedCards, upsideDown, cardLoyalty, cipt); + card = new CardInfo(this, cardName, isToken, cardCost, cmc, cardType, cardPT, cardText, colors, relatedCards, reverseRelatedCards, upsideDown, cardLoyalty, cipt); int tableRow = 1; QString mainCardType = card->getMainCardType(); if ((mainCardType == "Land") || mArtifact) @@ -146,6 +147,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) QString cardText; QStringList colors; QStringList relatedCards; + QStringList reverseRelatedCards; // dummy int cardId; int cardLoyalty; bool upsideDown = false; @@ -192,7 +194,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) colors.clear(); extractColors(map.value("colors").toStringList(), colors); - CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cmc, cardType, cardPT, cardLoyalty, cardText, colors, relatedCards, upsideDown); + CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cmc, cardType, cardPT, cardLoyalty, cardText, colors, relatedCards, reverseRelatedCards, upsideDown); if (!set->contains(card)) { card->addToSet(set); @@ -276,10 +278,11 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) colors.removeDuplicates(); relatedCards = QStringList(); + reverseRelatedCards = QStringList(); upsideDown = false; // add the card - CardInfo *card = addCard(set->getShortName(), cardName, false, muid, cardCost, cmc, cardType, cardPT, cardLoyalty, cardText, colors, relatedCards, upsideDown); + CardInfo *card = addCard(set->getShortName(), cardName, false, muid, cardCost, cmc, cardType, cardPT, cardLoyalty, cardText, colors, relatedCards, reverseRelatedCards, upsideDown); if (!set->contains(card)) { card->addToSet(set); diff --git a/oracle/src/oracleimporter.h b/oracle/src/oracleimporter.h index 17092ea5..6a30e65b 100644 --- a/oracle/src/oracleimporter.h +++ b/oracle/src/oracleimporter.h @@ -30,7 +30,7 @@ private: QVariantMap setsMap; QString dataDir; - CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, QString &cardCost, QString &cmc, const QString &cardType, const QString &cardPT, int cardLoyalty, const QString &cardText, const QStringList & colors, const QStringList & relatedCards, bool upsideDown); + CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, QString &cardCost, QString &cmc, const QString &cardType, const QString &cardPT, int cardLoyalty, const QString &cardText, const QStringList & colors, const QStringList & relatedCards, const QStringList & reverseRelatedCards, bool upsideDown); signals: void setIndexChanged(int cardsImported, int setIndex, const QString &setName); void dataReadProgress(int bytesRead, int totalBytes); From 49b22b7d2fc5018b22b7668f6157c7d7d9ff66f7 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 27 Dec 2015 15:06:37 +0100 Subject: [PATCH 44/49] Try to fix package creation --- servatrice/CMakeLists.txt | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index eb30b011..d3522fd3 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -83,6 +83,28 @@ endif() SET(QT_DONT_USE_QTGUI TRUE) +# Mysql connector +if(UNIX) + if(APPLE) + SET(MYSQLCLIENT_DEFAULT_PATHS "/usr/local/lib" "/opt/local/lib/mysql55/mysql/" "/opt/local/lib/mysql56/mysql/") + else() + SET(MYSQLCLIENT_DEFAULT_PATHS "/usr/lib" "/usr/local/lib") + endif() +elseif(WIN32) + SET(MYSQLCLIENT_DEFAULT_PATHS "C:\\Program Files\\MySQL\\MySQL Server 5.5\\lib" "C:\\Program Files\\MySQL\\MySQL Server 5.6\\lib") +endif() + +find_library(MYSQLCLIENT_LIBRARIES NAMES mysqlclient PATHS ${MYSQLCLIENT_DEFAULT_PATHS} PATH_SUFFIXES mysql mariadb) +if(${MYSQLCLIENT_LIBRARIES} MATCHES "NOTFOUND") + set(MYSQLCLIENT_FOUND FALSE CACHE INTERNAL "") + MESSAGE(STATUS "Mysql connector NOT FOUND: servatrice won't be able to connect to a mysql server") + unset(MYSQLCLIENT_LIBRARIES) +else() + set(MYSQLCLIENT_FOUND TRUE CACHE INTERNAL "") + get_filename_component(MYSQLCLIENT_LIBRARY_DIR ${MYSQLCLIENT_LIBRARIES} PATH) + MESSAGE(STATUS "Mysql connector found at: ${MYSQLCLIENT_LIBRARY_DIR}") +endif() + # Declare path variables set(ICONDIR share/icons CACHE STRING "icon dir") set(DESKTOPDIR share/applications CACHE STRING "desktop file destination") @@ -152,7 +174,7 @@ if(APPLE) # qt5: platforms, sqldrivers install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime - FILES_MATCHING REGEX "(codecs|platforms|sqldrivers)/.*\\.dylib" + FILES_MATCHING REGEX "(codecs/.*|platforms/.*|sqldrivers/libqsqlmysql)\\.dylib" REGEX ".*_debug\\.dylib" EXCLUDE) install(CODE " @@ -166,7 +188,7 @@ Translations = Resources/translations\") \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\") set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/servatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\") + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/servatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\") " COMPONENT Runtime) endif() @@ -179,7 +201,8 @@ if(WIN32) # qt5: platforms, sqldrivers install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime - FILES_MATCHING REGEX "(codecs|platforms|sqldrivers)/.*[^d]\\.dll") + FILES_MATCHING REGEX "(codecs/.*|platforms/.*|sqldrivers/libqsqlmysql)\\.dll" + REGEX ".*d\\.dll" EXCLUDE) install(CODE " file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] @@ -192,7 +215,7 @@ Translations = Resources/translations\") \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dll\") set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/servatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\") + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/servatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\") " COMPONENT Runtime) endif() #Compile a portable version, default off From 137d7328b59c2ce0c057632a8344885334eeaefe Mon Sep 17 00:00:00 2001 From: Zach H Date: Sat, 26 Dec 2015 21:07:33 -0500 Subject: [PATCH 45/49] coin flip remove male/female as it's same male/female fixes translate --- cockatrice/src/messagelogwidget.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/messagelogwidget.cpp b/cockatrice/src/messagelogwidget.cpp index 8f28e4e3..ac5b5a32 100644 --- a/cockatrice/src/messagelogwidget.cpp +++ b/cockatrice/src/messagelogwidget.cpp @@ -190,8 +190,15 @@ void MessageLogWidget::logShuffle(Player *player, CardZone *zone) void MessageLogWidget::logRollDie(Player *player, int sides, int roll) { + QString coinOptions[2] = {tr("Heads (1)"), tr("Tails (2)")}; soundEngine->playSound("roll_dice"); - if (isFemale(player)) + + if (sides == 2) + if (isFemale(player)) + appendHtmlServerMessage(tr("%1 flipped a coin. It landed as %2.", "female").arg(sanitizeHtml(player->getName())).arg("" + coinOptions[roll - 1] + "")); + else + appendHtmlServerMessage(tr("%1 flipped a coin. It landed as %2.", "male").arg(sanitizeHtml(player->getName())).arg("" + coinOptions[roll - 1] + "")); + else if (isFemale(player)) appendHtmlServerMessage(tr("%1 rolls a %2 with a %3-sided die.", "female").arg(sanitizeHtml(player->getName())).arg("" + QString::number(roll) + "").arg("" + QString::number(sides) + "")); else appendHtmlServerMessage(tr("%1 rolls a %2 with a %3-sided die.", "male").arg(sanitizeHtml(player->getName())).arg("" + QString::number(roll) + "").arg("" + QString::number(sides) + "")); From 8a6d63265fe450574defa8e29d230d153c033eda Mon Sep 17 00:00:00 2001 From: Lee Matos Date: Mon, 28 Dec 2015 22:03:38 -0500 Subject: [PATCH 46/49] Enable HiDpiPixmaps for Qt5 --- cockatrice/src/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index 10d01bfc..9d91eb72 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -215,7 +215,9 @@ int main(int argc, char *argv[]) ui.show(); qDebug("main(): ui.show() finished"); - +#if QT_VERSION > 0x050000 + app.setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif app.exec(); } From da0e3292d32779c1842a1cb8897d75bb5f7d6aac Mon Sep 17 00:00:00 2001 From: Zach H Date: Tue, 29 Dec 2015 16:50:50 -0500 Subject: [PATCH 47/49] Token PT shown in create related dialog --- cockatrice/src/carddatabase.cpp | 7 ++++++- cockatrice/src/player.cpp | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 41a4976e..433f1d8b 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -660,7 +660,12 @@ void CardDatabase::refreshCachedReverseRelatedCards() if(card->getReverseRelatedCards().isEmpty()) continue; - QString relatedCardName = card->getName(); + QString relatedCardName; + if (card->getPowTough().size() > 0) + relatedCardName = card->getPowTough() + " " + card->getName(); // "n/n name" + else + relatedCardName = card->getName(); // "name" + foreach(QString targetCard, card->getReverseRelatedCards()) { if (!cards.contains(targetCard)) diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 135b4056..47b27703 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -1135,7 +1135,14 @@ void Player::actCreateRelatedCard() // get the target card name QAction *action = static_cast(sender()); - CardInfo *cardInfo = db->getCard(action->text()); + + // removes p/t from tokens (and leading space)) + QStringList spaces = action->text().split(" "); + + if (spaces.at(0).indexOf("/") != -1) // Strip space from creatures + spaces.removeFirst(); + + CardInfo *cardInfo = db->getCard(spaces.join(" ")); // create the token for the related card Command_CreateToken cmd; From 703ff7905c5413e40de055aa30146fd778b98ffc Mon Sep 17 00:00:00 2001 From: Zach H Date: Tue, 29 Dec 2015 23:40:20 -0500 Subject: [PATCH 48/49] token coordinates --- cockatrice/src/player.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 135b4056..1f3b8856 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -1136,6 +1136,9 @@ void Player::actCreateRelatedCard() // get the target card name QAction *action = static_cast(sender()); CardInfo *cardInfo = db->getCard(action->text()); + + // get the target token's location + QPoint gridPoint = QPoint(-1, table->clampValidTableRow(2 - cardInfo->getTableRow())); // create the token for the related card Command_CreateToken cmd; @@ -1146,6 +1149,9 @@ void Player::actCreateRelatedCard() cmd.set_annotation(settingsCache->getAnnotateTokens() ? cardInfo->getText().toStdString() : QString().toStdString()); cmd.set_destroy_on_zone_change(true); cmd.set_target_zone(sourceCard->getZone()->getName().toStdString()); + cmd.set_x(gridPoint.x()); + cmd.set_y(gridPoint.y()); + if(!cardInfo->getIsToken()) cmd.set_target_card_id(sourceCard->getId()); From f568a4e865beed1b68ae64aa6e124fd19734ebf6 Mon Sep 17 00:00:00 2001 From: Zach H Date: Wed, 30 Dec 2015 11:51:53 -0500 Subject: [PATCH 49/49] todo --- cockatrice/src/player.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 1f3b8856..8d54aa1b 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -1138,6 +1138,7 @@ void Player::actCreateRelatedCard() CardInfo *cardInfo = db->getCard(action->text()); // get the target token's location + // TODO: Define this QPoint into its own function along with the one below QPoint gridPoint = QPoint(-1, table->clampValidTableRow(2 - cardInfo->getTableRow())); // create the token for the related card