diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c50deabae0..99725433b6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,13 +7,15 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up JDK 17 - uses: actions/setup-java@v1 + - uses: actions/checkout@v3 + - name: Set up JDK 20 + uses: actions/setup-java@v2 with: - java-version: 17 + distribution: 'zulu' + java-version: '20' + java-package: jdk - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 2d18a89596..d0d24478d6 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -18,11 +18,11 @@ jobs: id: check-if-changed with: paths: .github/workflows src/ res/ pom.xml .install4j/ .mvn/ - - name: Set up JDK 17 + - name: Set up JDK 20 if: steps.check-if-changed.outputs.changed == 'true' uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 20 - name: Cache local Maven repository if: steps.check-if-changed.outputs.changed == 'true' @@ -56,27 +56,27 @@ jobs: include: - os: linux architecture: 64bit - maven_profiles: "linux,64bit,install4j" + maven_profiles: "linux_64bit,install4j" - os: arm - architecture: 32bit - maven_profiles: "arm,!64bit,32bit,install4j,!linux" - - os: windows architecture: 64bit - maven_profiles: "windows,64bit,install4j,!linux" + maven_profiles: "!linux_64bit,linux_arm_64bit,install4j" - os: windows - architecture: 32bit - maven_profiles: "windows32,!64bit,32bit,install4j,!linux" + architecture: 64bit + maven_profiles: "!linux_64bit,windows_64bit,install4j" + # - os: windows + # architecture: 32bit + # maven_profiles: "windows32,!64bit,32bit,install4j,!linux" - os: macOS architecture: 64bit - maven_profiles: "mac,!linux" + maven_profiles: "!linux_64bit,mac_intel" steps: - uses: actions/checkout@v2 with: ref: develop - - name: Set up JDK 17 + - name: Set up JDK 20 uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 20 - uses: AdoptOpenJDK/install-jdk@v1 with: version: '8' @@ -85,7 +85,7 @@ jobs: - name: Download install4j uses: wei/curl@v1 with: - args: -fsSL 'https://download-gcdn.ej-technologies.com/install4j/install4j_unix_9_0_5.tar.gz' --output install4j.tar.gz + args: -fsSL 'https://download-gcdn.ej-technologies.com/install4j/install4j_unix_9_0_7.tar.gz' --output install4j.tar.gz - name: Extract install4j run: tar -zxvf install4j.tar.gz diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 83eb83b849..f525af69e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,11 +19,11 @@ jobs: id: check-if-changed with: paths: .github/workflows src/ res/ pom.xml .install4j/ .mvn/ - - name: Set up JDK 17 + - name: Set up JDK 20 if: steps.check-if-changed.outputs.changed == 'true' uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 20 - name: Cache local Maven repository if: steps.check-if-changed.outputs.changed == 'true' @@ -57,28 +57,28 @@ jobs: include: - os: linux architecture: 64bit - maven_profiles: "linux,64bit,install4j" + maven_profiles: "linux_64bit,install4j" - os: arm - architecture: 32bit - maven_profiles: "arm,!64bit,32bit,install4j,!linux" - - os: windows architecture: 64bit - maven_profiles: "windows,64bit,install4j,!linux" + maven_profiles: "!linux_64bit,linux_arm_64bit,install4j" - os: windows - architecture: 32bit - maven_profiles: "windows32,!64bit,32bit,install4j,!linux" - - os: macOS architecture: 64bit - maven_profiles: "mac,!linux" + maven_profiles: "!linux_64bit,windows_64bit,install4j" + # - os: windows + # architecture: 32bit + # maven_profiles: "windows32,!64bit,32bit,install4j,!linux" + # - os: macOS + # architecture: 64bit + # maven_profiles: "mac,!linux" steps: - uses: actions/checkout@v2 with: ref: develop - - name: Set up JDK 17 + - name: Set up JDK 20 uses: actions/setup-java@v2 with: - distribution: 'temurin' - java-version: '16' + #distribution: 'temurin' + java-version: '20' - uses: AdoptOpenJDK/install-jdk@v1 with: version: '8' @@ -87,7 +87,7 @@ jobs: - name: Download install4j uses: wei/curl@v1 with: - args: -fsSL 'https://download-gcdn.ej-technologies.com/install4j/install4j_unix_9_0_5.tar.gz' --output install4j.tar.gz + args: -fsSL 'https://download-gcdn.ej-technologies.com/install4j/install4j_unix_9_0_7.tar.gz' --output install4j.tar.gz - name: Extract install4j run: tar -zxvf install4j.tar.gz @@ -125,10 +125,10 @@ jobs: - uses: actions/checkout@v2 with: ref: develop - - name: Set up JDK 17 + - name: Set up JDK 20 uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 20 - name: Get version run: echo "VERSION=$( ./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout )" >> $GITHUB_ENV - uses: actions/download-artifact@v2 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fe5c88f23c..e47be8125c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ variables: MAVEN_OPTS: "-Djava.awt.headless=true -Dmaven.repo.local=./.m2/repository" MAVEN_CLI_OPTS: "-B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn" -image: maven:3.8-eclipse-temurin-17 +image: maven:3.9-eclipse-temurin-20 cache: @@ -15,32 +15,35 @@ default: - mvrunner1 before_script: - apt-get update -qq - - apt-get install -y -qq openjdk-11-jre-headless ssh lib32ncurses6 lib32z1 wget tar file gnupg2 > /dev/null + - apt-get install -y -qq openjdk-11-jre-headless ssh lib32ncurses6 lib32z1 wget tar file gnupg2 git-lfs > /dev/null - source /private/ENVS - whoami - mkdir ~/.ssh - ssh-keyscan -p 60002 dw2.mvorg.de >> ~/.ssh/known_hosts - ssh-keyscan -p 60002 148.251.176.136 >> ~/.ssh/known_hosts - chmod 644 ~/.ssh/known_hosts - - wget -q https://download-gcdn.ej-technologies.com/install4j/install4j_unix_9_0_5.tar.gz - - tar -zxf install4j_unix_9_0_5.tar.gz + - wget -q https://download-gcdn.ej-technologies.com/install4j/install4j_unix_10_0_5.tar.gz -O install4j.tar.gz + - tar -zxf install4j.tar.gz - java -version - ${INSTALL4J_JAVA_HOME}/bin/java -version - mvn -v - mvn clean - mvn install4j:install-license -Pinstall4j + - git clone https://gitlab.com/mediathekview/mediathekview-buildres.git tmpres + - mv tmpres/bin res/ + - rm -rf tmpres stages: - deploy-nightly-lin - deploy-nightly-linarm - deploy-nightly-win - - deploy-nightly-win32 + # - deploy-nightly-win32 - deploy-nightly-mac # - deploy-nightly-mac-as - deploy-lin - deploy-linarm - deploy-win - - deploy-win32 + # - deploy-win32 Build and Deploy nightly Linux: stage: deploy-nightly-lin @@ -56,7 +59,7 @@ Build and Deploy nightly Linux ARM: stage: deploy-nightly-linarm script: - mvn -B package -P!linux_64bit,linux_arm_64bit,install4j $MAVEN_CLI_OPTS - - /skripte/deploy.sh nightly linux-armhf $CI_COMMIT_SHA + - /skripte/deploy.sh nightly linux-aarch64 $CI_COMMIT_SHA rules: - if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_BRANCH == "develop"' @@ -68,13 +71,13 @@ Build and Deploy nightly Windows: rules: - if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_BRANCH == "develop"' -Build and Deploy nightly Windows 32bit: - stage: deploy-nightly-win32 - script: - - mvn -B package -P!linux_64bit,windows_32bit,install4j $MAVEN_CLI_OPTS - - /skripte/deploy.sh nightly win32 $CI_COMMIT_SHA - rules: - - if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_BRANCH == "develop"' +# Build and Deploy nightly Windows 32bit: +# stage: deploy-nightly-win32 +# script: +# - mvn -B package -P!linux_64bit,windows_32bit,install4j $MAVEN_CLI_OPTS +# - /skripte/deploy.sh nightly win32 $CI_COMMIT_SHA +# rules: +# - if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_BRANCH == "develop"' Build and Deploy nightly Mac Intel: stage: deploy-nightly-mac @@ -108,7 +111,7 @@ Build and Deploy Linux ARM: stage: deploy-linarm script: - mvn -B package -P!linux_64bit,linux_arm_64bit,install4j $MAVEN_CLI_OPTS - - /skripte/deploy.sh release linux-armhf + - /skripte/deploy.sh release linux-aarch64 rules: - if: $CI_COMMIT_TAG @@ -120,10 +123,10 @@ Build and Deploy Windows: rules: - if: $CI_COMMIT_TAG -Build and Deploy Windows 32bit: - stage: deploy-win32 - script: - - mvn -B package -P!linux_64bit,windows_32bit,install4j $MAVEN_CLI_OPTS - - /skripte/deploy.sh release win32 - rules: - - if: $CI_COMMIT_TAG +# Build and Deploy Windows 32bit: +# stage: deploy-win32 +# script: +# - mvn -B package -P!linux_64bit,windows_32bit,install4j $MAVEN_CLI_OPTS +# - /skripte/deploy.sh release win32 +# rules: +# - if: $CI_COMMIT_TAG diff --git a/.install4j/mediathekview_arm.install4j b/.install4j/mediathekview_arm.install4j index 99420d9f84..8fc123f70f 100644 --- a/.install4j/mediathekview_arm.install4j +++ b/.install4j/mediathekview_arm.install4j @@ -1,11 +1,11 @@ - + - + - + @@ -33,7 +33,7 @@ - + @@ -48,7 +48,7 @@ - + @@ -61,7 +61,7 @@ - + @@ -1004,22 +1004,22 @@ return true; - - + + - - + + - - + + diff --git a/.install4j/mediathekview_linux.install4j b/.install4j/mediathekview_linux.install4j index 14026c7cdc..1cef1ad589 100644 --- a/.install4j/mediathekview_linux.install4j +++ b/.install4j/mediathekview_linux.install4j @@ -1,1037 +1,1037 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Categories=Network;FileTransfer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sys.installationDir - - - context.getBooleanVariable("sys.confirmedUpdateInstallation") - - - - - - ${form:welcomeMessage} - - !context.isConsole() - - - - - - String message = context.getMessage("ConsoleWelcomeLabel", context.getApplicationName()); -return console.askOkCancel(message, true); - - - - - - - - updateCheck - - - - - ${i18n:ClickNext} - - - - - - !context.getBooleanVariable("sys.confirmedUpdateInstallation") - - - - - sys.installationDir - - - context.getVariable("sys.responseFile") == null - - - - - - ${i18n:SelectDirLabel(${compiler:sys.fullName})} - - - - - - - - suggestAppDir - validateApplicationId - existingDirWarning - checkWritable - manualEntryAllowed - checkFreeSpace - showRequiredDiskSpace - showFreeDiskSpace - allowSpacesOnUnix - validationScript - standardValidation - - - - - - - - - - ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})} - - !context.getBooleanVariable("sys.programGroupDisabled") - - - - ${compiler:sys.fullName} ${compiler:sys.version} - - - - - - - ${i18n:WizardPreparing} - - - - - - - - - ${form:finishedMessage} - - - - - - - - - ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})} - - - - - - - - - - - - - - - - ${form:welcomeMessage} - - !context.isConsole() - - - - - - String message = context.getMessage("ConfirmUninstall", context.getApplicationName()); -return console.askYesNo(message, true); - - - - - - - - - - - - - - - ${i18n:UninstallerPreparing} - - - - - - - - - - ${form:successMessage} - - - - - - - - - - - - ${compiler:sys.install4jHome}/resource/updater_16.png - - - - - ${compiler:sys.install4jHome}/resource/updater_32.png - - - - - ${compiler:sys.install4jHome}/resource/updater_48.png - - - - - ${compiler:sys.install4jHome}/resource/updater_128.png - - - - - ${compiler:sys.install4jHome}/resource/updater_256.png - - - - update - - ${i18n:updater.WindowTitle("${compiler:sys.fullName}")} - - - - - - - - - - - - - ${installer:updatesUrl?:${compiler:sys.updatesUrl}} - updateDescriptor - - - - - - - - ((UpdateDescriptor)context.getVariable("updateDescriptor")).getPossibleUpdateEntry() - - - updateDescriptorEntry - - - - - - - context.getVariable("updateDescriptorEntry") != null - - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getNewVersion() - - - updaterNewVersion - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileSizeVerbose() - - - updaterDownloadSize - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getComment() - - - updaterComment - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getURL().toExternalForm() - - - updaterDownloadUrl - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).isArchive() ? Boolean.TRUE : Boolean.FALSE - - - isArchive - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName().toLowerCase().endsWith(".dmg") - - - isDmg - - - - - - - - - - - - - context.getVariable("updateDescriptorEntry") != null - - - - - - - ${i18n:updater.NewVersionAvailableSubtitle("${compiler:sys.fullName}")} - ${i18n:updater.NewVersionAvailableTitle} - - - - - ${i18n:updater.CurrentVersionLabel} - - - 128 - 0 - 0 - 255 - - - - - ${installer:sys.version} - - - - - - - ${i18n:updater.NewVersionLabel} - - - 0 - 128 - 0 - 255 - - - - - ${installer:updaterNewVersion} - - - - - - - context.goForward(1, false, false); - - - ${i18n:updater.ShowComments} - - ((String)context.getVariable("updaterComment")).length() > 0 - - - - - - - ${i18n:updater.DownloadLocationLabel} - - - - - ${installer:sys.downloadsDir} - ${i18n:updater.DownloadToLabel} - - updaterDownloadLocation - - - - - ${i18n:updater.DownloadSizeLabel} - ${installer:updaterDownloadSize} - - - - - - - - ${i18n:updater.CommentsSubTitle} - ${i18n:updater.CommentsTitle} - - false // This screen is only shown if the user clicks the "Show comments" hyperlink label in the previous screen. - - if (context.isConsole()) { - context.goBackInHistory(1); -} -return true; - WizardContext wizardContext = context.getWizardContext(); -wizardContext.setControlButtonVisible(ControlButtonType.NEXT, false); -wizardContext.setControlButtonVisible(ControlButtonType.CANCEL, false); - - - - - ${i18n:updater.CommentsLabel} - - !context.isConsole() - - labelText - - - - - ${installer:updaterComment} - - - - - textSource - displayedText - displayedTextFile - variableName - - - - - - - console.waitForEnter(); -return true; - - - - - - - - - - ${i18n:updater.DownloadSubTitle} - ${i18n:updater.DownloadTitle} - - context.getWizardContext().setControlButtonVisible(ControlButtonType.NEXT, false); -context.getWizardContext().setControlButtonVisible(ControlButtonType.PREVIOUS, false); -context.goForward(1, true, true); - - - - - - - context.getVariable("updaterDownloadLocation") + File.separator + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName() - - - updaterDownloadFile - - - - - - - ${installer:updaterDownloadFile} - - - ${installer:updaterDownloadUrl} - - - - - - - - ${installer:updaterDownloadFile} - - - - 755 - - - - - - - statusVisible - initialStatusMessage - - - - - - - - - ${i18n:updater.FinishTitle} - - !(context.getBooleanVariable("isArchive") && context.getBooleanVariable("isDmg")) - - - - - - !context.getBooleanVariable("isArchive") && ((Integer)context.getVariable("updaterLaunchSelection")).intValue() == 0 - - - - - - - - - import com.install4j.runtime.installer.frontend.SplashProgressInterface; - -List<String> args = new ArrayList<String>(); -String installationDirectory = context.getInstallationDirectory().getPath(); -if (context.isUnattended()) { - args.add("-q"); - args.add("-wait"); - args.add("20"); - if (context.getProgressInterface() instanceof SplashProgressInterface) { - args.add("-splash"); - args.add("Installing"); - } -} else if (context.isConsole()) { - args.add("-c"); -} - args.add("-dir"); - args.add(installationDirectory); - - return args.toArray(new String[args.size()]); - - - - installerArguments - - - - - - - ${installer:installerArguments} - - - - ${installer:updaterDownloadFile} - - - - - ${installer:updaterDownloadLocation} - - - - - - - - - - - - - !context.isConsole() - - labelText - - - - - - ${i18n:updater.FinishInfoText("${compiler:sys.fullName}")} - - !context.isConsole() - - labelText - - - - - ${i18n:updater.LaunchUpdaterQuestion} - - - - - - - - - - - ${i18n:updater.LaunchUpdaterLabel} - ${i18n:updater.DoNotLaunchUpdaterLabel} - - updaterLaunchSelection - - !context.getBooleanVariable("isArchive") - - - - - - Util.showPath((String)context.getVariable("updaterDownloadFile")); - - - ${i18n:updater.OpenContainingFolderLabel} - - !context.isConsole() - - - - - - - - - - - - ${i18n:updater.FinishTitle} - - context.getBooleanVariable("isArchive") && context.getBooleanVariable("isDmg") - - - - - - context.getBooleanVariable("updaterOpenDmg") - - - - - - - - - - Util.showPath((String)context.getVariable("updaterDownloadFile")); -return true; - - - - - - - - - - - - - !context.isConsole() - - labelText - - - - - - ${i18n:updater.FinishInfoText("${compiler:sys.fullName}")} - - !context.isConsole() - - labelText - - - - - ${i18n:updater.LaunchUpdaterQuestion} - - - - - - - - - - ${i18n:updater.OpenContainingFolderLabel} - - updaterOpenDmg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Categories=Network;FileTransfer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sys.installationDir + + + context.getBooleanVariable("sys.confirmedUpdateInstallation") + + + + + + ${form:welcomeMessage} + + !context.isConsole() + + + + + + String message = context.getMessage("ConsoleWelcomeLabel", context.getApplicationName()); +return console.askOkCancel(message, true); + + + + + + + + updateCheck + + + + + ${i18n:ClickNext} + + + + + + !context.getBooleanVariable("sys.confirmedUpdateInstallation") + + + + + sys.installationDir + + + context.getVariable("sys.responseFile") == null + + + + + + ${i18n:SelectDirLabel(${compiler:sys.fullName})} + + + + + + + + suggestAppDir + validateApplicationId + existingDirWarning + checkWritable + manualEntryAllowed + checkFreeSpace + showRequiredDiskSpace + showFreeDiskSpace + allowSpacesOnUnix + validationScript + standardValidation + + + + + + + + + + ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})} + + !context.getBooleanVariable("sys.programGroupDisabled") + + + + ${compiler:sys.fullName} ${compiler:sys.version} + + + + + + + ${i18n:WizardPreparing} + + + + + + + + + ${form:finishedMessage} + + + + + + + + + ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})} + + + + + + + + + + + + + + + + ${form:welcomeMessage} + + !context.isConsole() + + + + + + String message = context.getMessage("ConfirmUninstall", context.getApplicationName()); +return console.askYesNo(message, true); + + + + + + + + + + + + + + + ${i18n:UninstallerPreparing} + + + + + + + + + + ${form:successMessage} + + + + + + + + + + + + ${compiler:sys.install4jHome}/resource/updater_16.png + + + + + ${compiler:sys.install4jHome}/resource/updater_32.png + + + + + ${compiler:sys.install4jHome}/resource/updater_48.png + + + + + ${compiler:sys.install4jHome}/resource/updater_128.png + + + + + ${compiler:sys.install4jHome}/resource/updater_256.png + + + + update + + ${i18n:updater.WindowTitle("${compiler:sys.fullName}")} + + + + + + + + + + + + + ${installer:updatesUrl?:${compiler:sys.updatesUrl}} + updateDescriptor + + + + + + + + ((UpdateDescriptor)context.getVariable("updateDescriptor")).getPossibleUpdateEntry() + + + updateDescriptorEntry + + + + + + + context.getVariable("updateDescriptorEntry") != null + + + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getNewVersion() + + + updaterNewVersion + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileSizeVerbose() + + + updaterDownloadSize + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getComment() + + + updaterComment + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getURL().toExternalForm() + + + updaterDownloadUrl + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).isArchive() ? Boolean.TRUE : Boolean.FALSE + + + isArchive + + + + + + + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName().toLowerCase().endsWith(".dmg") + + + isDmg + + + + + + + + + + + + + context.getVariable("updateDescriptorEntry") != null + + + + + + + ${i18n:updater.NewVersionAvailableSubtitle("${compiler:sys.fullName}")} + ${i18n:updater.NewVersionAvailableTitle} + + + + + ${i18n:updater.CurrentVersionLabel} + + + 128 + 0 + 0 + 255 + + + + + ${installer:sys.version} + + + + + + + ${i18n:updater.NewVersionLabel} + + + 0 + 128 + 0 + 255 + + + + + ${installer:updaterNewVersion} + + + + + + + context.goForward(1, false, false); + + + ${i18n:updater.ShowComments} + + ((String)context.getVariable("updaterComment")).length() > 0 + + + + + + + ${i18n:updater.DownloadLocationLabel} + + + + + ${installer:sys.downloadsDir} + ${i18n:updater.DownloadToLabel} + + updaterDownloadLocation + + + + + ${i18n:updater.DownloadSizeLabel} + ${installer:updaterDownloadSize} + + + + + + + + ${i18n:updater.CommentsSubTitle} + ${i18n:updater.CommentsTitle} + + false // This screen is only shown if the user clicks the "Show comments" hyperlink label in the previous screen. + + if (context.isConsole()) { + context.goBackInHistory(1); +} +return true; + WizardContext wizardContext = context.getWizardContext(); +wizardContext.setControlButtonVisible(ControlButtonType.NEXT, false); +wizardContext.setControlButtonVisible(ControlButtonType.CANCEL, false); + + + + + ${i18n:updater.CommentsLabel} + + !context.isConsole() + + labelText + + + + + ${installer:updaterComment} + + + + + textSource + displayedText + displayedTextFile + variableName + + + + + + + console.waitForEnter(); +return true; + + + + + + + + + + ${i18n:updater.DownloadSubTitle} + ${i18n:updater.DownloadTitle} + + context.getWizardContext().setControlButtonVisible(ControlButtonType.NEXT, false); +context.getWizardContext().setControlButtonVisible(ControlButtonType.PREVIOUS, false); +context.goForward(1, true, true); + + + + + + + context.getVariable("updaterDownloadLocation") + File.separator + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName() + + + updaterDownloadFile + + + + + + + ${installer:updaterDownloadFile} + + + ${installer:updaterDownloadUrl} + + + + + + + + ${installer:updaterDownloadFile} + + + + 755 + + + + + + + statusVisible + initialStatusMessage + + + + + + + + + ${i18n:updater.FinishTitle} + + !(context.getBooleanVariable("isArchive") && context.getBooleanVariable("isDmg")) + + + + + + !context.getBooleanVariable("isArchive") && ((Integer)context.getVariable("updaterLaunchSelection")).intValue() == 0 + + + + + + + + + import com.install4j.runtime.installer.frontend.SplashProgressInterface; + +List<String> args = new ArrayList<String>(); +String installationDirectory = context.getInstallationDirectory().getPath(); +if (context.isUnattended()) { + args.add("-q"); + args.add("-wait"); + args.add("20"); + if (context.getProgressInterface() instanceof SplashProgressInterface) { + args.add("-splash"); + args.add("Installing"); + } +} else if (context.isConsole()) { + args.add("-c"); +} + args.add("-dir"); + args.add(installationDirectory); + + return args.toArray(new String[args.size()]); + + + + installerArguments + + + + + + + ${installer:installerArguments} + + + + ${installer:updaterDownloadFile} + + + + + ${installer:updaterDownloadLocation} + + + + + + + + + + + + + !context.isConsole() + + labelText + + + + + + ${i18n:updater.FinishInfoText("${compiler:sys.fullName}")} + + !context.isConsole() + + labelText + + + + + ${i18n:updater.LaunchUpdaterQuestion} + + + + + + + + + + + ${i18n:updater.LaunchUpdaterLabel} + ${i18n:updater.DoNotLaunchUpdaterLabel} + + updaterLaunchSelection + + !context.getBooleanVariable("isArchive") + + + + + + Util.showPath((String)context.getVariable("updaterDownloadFile")); + + + ${i18n:updater.OpenContainingFolderLabel} + + !context.isConsole() + + + + + + + + + + + + ${i18n:updater.FinishTitle} + + context.getBooleanVariable("isArchive") && context.getBooleanVariable("isDmg") + + + + + + context.getBooleanVariable("updaterOpenDmg") + + + + + + + + + + Util.showPath((String)context.getVariable("updaterDownloadFile")); +return true; + + + + + + + + + + + + + !context.isConsole() + + labelText + + + + + + ${i18n:updater.FinishInfoText("${compiler:sys.fullName}")} + + !context.isConsole() + + labelText + + + + + ${i18n:updater.LaunchUpdaterQuestion} + + + + + + + + + + ${i18n:updater.OpenContainingFolderLabel} + + updaterOpenDmg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.install4j/mediathekview_windows.install4j b/.install4j/mediathekview_windows.install4j index 2bb29188ad..66611995ca 100644 --- a/.install4j/mediathekview_windows.install4j +++ b/.install4j/mediathekview_windows.install4j @@ -1,5 +1,5 @@ - + diff --git a/.install4j/mediathekview_windows32.install4j b/.install4j/mediathekview_windows32.install4j deleted file mode 100644 index 08d2dac76f..0000000000 --- a/.install4j/mediathekview_windows32.install4j +++ /dev/null @@ -1,1069 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sys.installationDir - - - context.getBooleanVariable("sys.confirmedUpdateInstallation") - - - - - - ${form:welcomeMessage} - - !context.isConsole() - - - - - - String message = context.getMessage("ConsoleWelcomeLabel", - context.getApplicationName()); - return console.askOkCancel(message, true); - - - - - - - - updateCheck - - - - - ${i18n:ClickNext} - - - - - - !context.getBooleanVariable("sys.confirmedUpdateInstallation") - - - - - sys.installationDir - - - context.getVariable("sys.responseFile") == null - - - - - - ${i18n:SelectDirLabel(${compiler:sys.fullName})} - - - - - - - - suggestAppDir - validateApplicationId - existingDirWarning - checkWritable - manualEntryAllowed - checkFreeSpace - showRequiredDiskSpace - showFreeDiskSpace - allowSpacesOnUnix - validationScript - standardValidation - - - - - - - - - - - ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})} - - - !context.getBooleanVariable("sys.programGroupDisabled") - - - - ${compiler:sys.fullName} ${compiler:sys.version} - - - - - - - ${i18n:WizardPreparing} - - - - - - - - - ${form:finishedMessage} - - - - - - - - - - ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})} - - - - - - - - - - - - - - - - - ${form:welcomeMessage} - - !context.isConsole() - - - - - - String message = context.getMessage("ConfirmUninstall", - context.getApplicationName()); - return console.askYesNo(message, true); - - - - - - - - - - - - - - - ${i18n:UninstallerPreparing} - - - - - - - - - - ${form:successMessage} - - - - - - - - - - - - ${compiler:sys.install4jHome}/resource/updater_16.png - - - - - ${compiler:sys.install4jHome}/resource/updater_32.png - - - - - ${compiler:sys.install4jHome}/resource/updater_48.png - - - - - ${compiler:sys.install4jHome}/resource/updater_128.png - - - - - ${compiler:sys.install4jHome}/resource/updater_256.png - - - - update - - ${i18n:updater.WindowTitle("${compiler:sys.fullName}")} - - - - - - - - - - - - - ${installer:updatesUrl?:${compiler:sys.updatesUrl}} - updateDescriptor - - - - - - - - - ((UpdateDescriptor)context.getVariable("updateDescriptor")).getPossibleUpdateEntry() - - - - updateDescriptorEntry - - - - - - - context.getVariable("updateDescriptorEntry") != null - - - - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getNewVersion() - - - - updaterNewVersion - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileSizeVerbose() - - - - updaterDownloadSize - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getComment() - - - - updaterComment - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getURL().toExternalForm() - - - - updaterDownloadUrl - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).isArchive() ? - Boolean.TRUE : Boolean.FALSE - - - - isArchive - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName().toLowerCase().endsWith(".dmg") - - - - isDmg - - - - - - - - - - - - - context.getVariable("updateDescriptorEntry") != null - - - - - - - - ${i18n:updater.NewVersionAvailableSubtitle("${compiler:sys.fullName}")} - - ${i18n:updater.NewVersionAvailableTitle} - - - - - ${i18n:updater.CurrentVersionLabel} - - - 128 - 0 - 0 - 255 - - - - - ${installer:sys.version} - - - - - - - ${i18n:updater.NewVersionLabel} - - - 0 - 128 - 0 - 255 - - - - - ${installer:updaterNewVersion} - - - - - - - context.goForward(1, false, false); - - - ${i18n:updater.ShowComments} - - ((String)context.getVariable("updaterComment")).length() > 0 - - - - - - - - ${i18n:updater.DownloadLocationLabel} - - - - - ${installer:sys.downloadsDir} - ${i18n:updater.DownloadToLabel} - - updaterDownloadLocation - - - - - ${i18n:updater.DownloadSizeLabel} - ${installer:updaterDownloadSize} - - - - - - - - ${i18n:updater.CommentsSubTitle} - ${i18n:updater.CommentsTitle} - - false // This screen is only shown if the user clicks the "Show comments" hyperlink label in the previous screen. - - if (context.isConsole()) { - context.goBackInHistory(1); - } - return true; - - WizardContext wizardContext = context.getWizardContext(); - wizardContext.setControlButtonVisible(ControlButtonType.NEXT, false); - wizardContext.setControlButtonVisible(ControlButtonType.CANCEL, false); - - - - - - ${i18n:updater.CommentsLabel} - - !context.isConsole() - - labelText - - - - - ${installer:updaterComment} - - - - - textSource - displayedText - displayedTextFile - variableName - - - - - - - console.waitForEnter(); - return true; - - - - - - - - - - ${i18n:updater.DownloadSubTitle} - ${i18n:updater.DownloadTitle} - - context.getWizardContext().setControlButtonVisible(ControlButtonType.NEXT, false); - context.getWizardContext().setControlButtonVisible(ControlButtonType.PREVIOUS, false); - context.goForward(1, true, true); - - - - - - - context.getVariable("updaterDownloadLocation") + - File.separator + - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName() - - - - updaterDownloadFile - - - - - - - ${installer:updaterDownloadFile} - - - ${installer:updaterDownloadUrl} - - - - - - - - ${installer:updaterDownloadFile} - - - - 755 - - - - - - - statusVisible - initialStatusMessage - - - - - - - - - ${i18n:updater.FinishTitle} - - !(context.getBooleanVariable("isArchive") && - context.getBooleanVariable("isDmg")) - - - - - - - !context.getBooleanVariable("isArchive") && - ((Integer)context.getVariable("updaterLaunchSelection")).intValue() == 0 - - - - - - - - - - import com.install4j.runtime.installer.frontend.SplashProgressInterface; - - List<String> args = new ArrayList<String>(); - String installationDirectory = context.getInstallationDirectory().getPath(); - if (context.isUnattended()) { - args.add("-q"); - args.add("-wait"); - args.add("20"); - if (context.getProgressInterface() instanceof SplashProgressInterface) { - args.add("-splash"); - args.add("Installing"); - } - } else if (context.isConsole()) { - args.add("-c"); - } - args.add("-dir"); - args.add(installationDirectory); - - return args.toArray(new String[args.size()]); - - - - installerArguments - - - - - - - ${installer:installerArguments} - - - - ${installer:updaterDownloadFile} - - - - - ${installer:updaterDownloadLocation} - - - - - - - - - - - - - !context.isConsole() - - labelText - - - - - - - ${i18n:updater.FinishInfoText("${compiler:sys.fullName}")} - - - !context.isConsole() - - labelText - - - - - ${i18n:updater.LaunchUpdaterQuestion} - - - - - - - - - - - ${i18n:updater.LaunchUpdaterLabel} - ${i18n:updater.DoNotLaunchUpdaterLabel} - - updaterLaunchSelection - - !context.getBooleanVariable("isArchive") - - - - - - - Util.showPath((String)context.getVariable("updaterDownloadFile")); - - - - ${i18n:updater.OpenContainingFolderLabel} - - - !context.isConsole() - - - - - - - - - - - - ${i18n:updater.FinishTitle} - - context.getBooleanVariable("isArchive") && context.getBooleanVariable("isDmg") - - - - - - - context.getBooleanVariable("updaterOpenDmg") - - - - - - - - - - - - Util.showPath((String)context.getVariable("updaterDownloadFile")); - return true; - - - - - - - - - - - - - - !context.isConsole() - - labelText - - - - - - - ${i18n:updater.FinishInfoText("${compiler:sys.fullName}")} - - - !context.isConsole() - - labelText - - - - - ${i18n:updater.LaunchUpdaterQuestion} - - - - - - - - - - ${i18n:updater.OpenContainingFolderLabel} - - - updaterOpenDmg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index fa4f7b499f..0000000000 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,110 +0,0 @@ -/* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -*/ - -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = - "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: : " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar index 01e6799737..cb28b0e37c 100644 Binary files a/.mvn/wrapper/maven-wrapper.jar and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index eed391842a..d8b2495a1e 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1,18 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.1/apache-maven-3.9.1-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/CHANGELOG.md b/CHANGELOG.md index a2a6a622f0..a15f96b49a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,74 @@ +# **14.0.0** +- Es wird nun Java 20 verwendet. +- User Interface wurde primär für neue macOS-Versionen überarbeitet. +- Die *macOS Intel* Version nutzt nun Liberica JDK full mit integriertem JavaFX. Dadurch kann MediathekView nun auch wieder fehlerfrei mit Apple Silicon Macs gebaut werden. +- Das Programm überprüft beim Start ob die JVM-Parameter korrekt sind und gibt ansonsten eine Warnung aus. Dies kann über die Paramter `-nj` oder `--no-jvm-param-checks` deaktiviert werden. +- Das Programm prüft unter Linux ob der `sun.java2d.uiScale`-Parameter korrekt angegeben wurde. Im Fehlerfall wird ein Warndialog angezeigt. +- Der PSet-Parameter `%i` liefert anstatt der internen Filmnummer nun die aktuelle Zeit seit 1970 in Millisekunden. +- *Globale Schriftgröße ändern...*-Befehl wurde unter Linux entfernt da es keine Auswirkungen mehr hat. +- Im Tab "Filme" wurden *"Abo löschen"* und *"Abo ändern"* aufgrund der teilweise langen Ausführungszeiten und dem damit zusammenhängenden Einfrieren der Oberfläche entfernt. Bitte nutzen Sie wie dafür vorgesehen den "Abos verwalten" Dialog. +- Im Tab "Downloads" wurde die Textanzeige zu laufenden Downloads entfernt. Die Informationen werden nun in der globalen Statuszeile der App bei Bedarf angezeigt. +- Die Spalten `HQ`, `UT` und `Geo` sind aufgrund der neuen Icons grundsätzlich überflüssig. +- Die Option "Programm nicht beenden" im Beenden-Dialog bei laufenden Downloads wurde entfernt, das sie redundant ist und durch den Button "Abbrechen" erzielt wird. +- Frankreich kann nun als Standort für das Geoblocking gesetzt werden. +- verwendete Bibliotheken wurden aktualisiert. +- Bandbreitendialog wurde zurück nach Swing portiert. +- **BUGFIX:** Updater-Konfigurationsmöglichkeit wird nur noch in den Einstellungen aktiviert dargestellt, wenn sie auch Auswirkungen auf den Update-Mechanismus hat. +- **BUGFIX:** Filter-Fenster überlagert nicht mehr Tooltip-Anzeigen. +- **BUGFIX:** Die Oberfläche gibt nun verbesserte Hinweise, wenn Abos verarbeitet werden (nur im "Abo verwalten" Dialog) +- **BUGFIX:** Fehler in der Anzeige im Statusfeld des Download-Tab wurde behoben. +- **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. +- **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. +- **BUGFIX:** Sendericons werden nun vollständig und hochauflösend gerendert. +- **BUGFIX:** Update-Möglichkeit innerhalb des Programms wird nun richtig ein- und ausgeblendet, je nach verwendetem Installationstyp. +- **BUGFIX:** Potentieller Absturz beim Einlesen der Abohistorie wurde behoben. +- **BUGFIX:** Das Hinzufügen großer Mengen an Downloads blockiert nun nicht mehr das Programm. +- **BUGFIX:** Die Dateigrößen von manuellen Downloads werden nun - sofern vorhanden - richtig verarbeitet. +- **BUGFIX:** geo-geblockte Filmeinträge verwenden nun bei der Auswahl die Farben des Themes anstatt einer eigens definierten. +- **BUGFIX:** Menüpunkt *"Downloads/Aktion nach abgeschlossenen Downloads..."* wurde entfernt da es in der Funktion dem regulären Beenden der Applikation entspricht. +- **BUGFIX:** Bei zeitgesteuerten Downloads wurde der Rechner manchmal nicht wie gewünscht heruntergefahren. +- **BUGFIX:** Nutzung der aktuellsten Apache Commons Configuration2 Bibliothek zur Behebung der bekannten CVEs. +- **BUGFIX:** Duplikate in der Themaliste werden nun (noch weiter) reduziert als bisher. +- **BUGFIX:** Abo verwalten Dialog konnte manchmal Höhe und Breite 0 haben. +- **BUGFIX:** Bei geänderter Senderauswahl wird nun eine vollständige Filmliste geladen, solange die Filmliste **nicht** erweitert wird. +- **BUGFIX:** Bei Übergabe von Downloads an JDownloader kann nun die Qualität bestimmt werden. +- **BUGFIX:** Filterpanel im Tab "Downloads" wird nun nicht mehr zu klein dargestellt beim ersten Start. +- **BUGFIX:** *Abo ändern*-Button kann nicht mehr aktiviert werden wenn mehrere Abos in der Liste ausgewählt sind. +- **BUGFIX:** Frankreich wurde beim Geoblocking nicht berücksichtigt. +- **BUGFIX:** Filme mit *EU* blocking code wurden teilweise falsch als geblockt angezeigt. +- **BUGFIX:** Beim Export von Filmlisten wird nun vorher geprüft, ob noch mindestesns 1 GB freier Speicher vorhanden ist. +- **BUGFIX:** Behebung diverser Probleme in *Downloads/Alle Downloads zeitverzögert starten...*. +- **BUGFIX:** *Download weiterführen* Dialog wird nun etwas tiefer auf dem Bildschirm als normal dargestellt um ungewollte Buttonclicks zu verhindern. +- **BUGFIX:** *Download editieren*-Dialog wurde konform zum Kontextmenü in *Download ändern* umbenannt. +- **BUGFIX:** *Download ändern*-Dialog zeigte manchmal Daten fehlerhaft an. +- **FEATURE:** MediathekView kann sich nun beim Beenden das zuletzt sichtbare Tab merken und dieses beim Start wiederherstellen. Das Verhalten lässt sich in den Einstellungen konfigurieren. +- **FEATURE:** Experimenteller Schalter zur Unterstützung eines **Dark Mode** implementiert. Störende Farbschemata müssen in den Einstellungen manuell geändert werden! Nicht alle Dialoge unterstützen dies derzeit. +- **FEATURE:** Abfragen im Tab "Filme" können nun mit Hilfe der *Lucene* Suchmaschine durchgeführt werden. Dies ist nur für erfahrene Nutzer sinnvoll! Sie wird in *Einstellungen/Allgemein* aktiviert. +- **FEATURE:** Livestreams werden mit einem Icon markiert anstatt einer dunklen Farbe. +- **FEATURE:** Die Toolbars können nun zum Schweben in allen Tabs abgetrennt werden. +- **FEATURE:** Tab Filmbeschreibung wurde neu designed und stellt nun auch das Sendericon dar. +- **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. +- **FEATURE:** Neues Suchfeld inklusive Suchhistorie. +- **FEATURE:** Geoinformationen in der Spalte `Geo` im Tab "Filme" werden nun als *gesperrt/entsperrt* Icons dargestellt. Detailinformationen werden über Tooltip-Text eingeblendet. +- **FEATURE:** Untertitelverfügbarkeit wird nun über ein `cc`-Icon an Anfang des Titels angezeigt. Die Spalte `UT` wird in einer späteren Programmversion entfernt werden. +- **FEATURE:** Inhalte in HQ werden mit einem `HQ` Icon im Titel im Tab "Filme" dargestellt. +- **FEATURE:** Geogesperrte Inhalte werden mit einem Schloß versehen. +- **FEATURE:** Ein manchmal unsichtbarer Filter-Dialog kann nun über das Menü *Hilfe/Filterdialog-Position zurücksetzen* auf den sichtbaren Monitorbereich zurückgesetzt werden. +- **FEATURE:** Im Tab Download werden die Dialoge bzgl. Neustarts fertiger Downloads nun automatisch nach 10 Sekunden geschlossen wenn kein Userinput passiert. Es wird dann jeweils die "Nein"-Option ausgewählt um bereits vorhandene Downloads zu schützen. +- **FEATURE:** Untertitel eines Films können über das Kontextmenü im Film-Tab nun manuell geladen werden. Dabei erfolgt automatisch eine Konvertierung in das SRT-Format soweit notwendig. +- **FEATURE(Windows/Linux):** Schriftgröße und -art kann für das gesamte Programm über das Menü "*Schrift*" geändert werden. +- **FEATURE(Windows/Linux):** Die Breite der Scrollbars wurde etwas erhöht um die Sichtbarkeit zu verbessern. +- **FEATURE:** Bandbreitenbegrenzung kann im Tab *"Downloads"* nun über eine CheckBox aktiviert/deaktiviert werden. +- **FEATURE:** Der Splash-Screen beim Start lässt sich mittels `-ns` oder `--no-splash` deaktivieren. +- **FEATURE(Linux):** interne Nutzung von `xdg-user-dir` um sich besser in die vorhandene Desktops zu integrieren. Dies ist v.a. für flatpack-Installationen notwendig. +- **FEATURE(macOS App):** ffmpeg auf Version 5.11 aktualisiert. +- **FEATURE:** Abos können per Doppelklick im *Abo verwalten*-Dialog editiert werden. +- **FEATURE:** Länderflaggen werden in *Einstellungen/Standort & Geoblocking* dargestellt. +- **FEATURE:** *Export/Dekomprimierte Filmliste* speichert eine Filmliste mit vollständigen Sender/Thema Tags ab. +- **FEATURE:** *Download ändern*-Dialog speichert seine Größe und Position und stellt diese wieder her. + **13.9.1** + - Die Binaries für macOS waren beschädigt und mussten erneuert werden. **13.9.0** diff --git a/README.md b/README.md index fbf1783b01..ef4962658c 100644 --- a/README.md +++ b/README.md @@ -66,3 +66,6 @@ See / Siehe [Contribution Guide](https://github.com/mediathekview/MediathekView/ - [Forum](https://forum.mediathekview.de/) - [Anleitung](https://mediathekview.de/anleitung/) - [FAQ](https://mediathekview.de/faq/) + +# Credits +- Die dargestellten Länderflaggen wurden von [Freepik](https://www.flaticon.com/authors/freepik) auf [www.flaticon.com](https://www.flaticon.com) erstellt. \ No newline at end of file diff --git a/lucene_help.md b/lucene_help.md new file mode 100644 index 0000000000..63df0d0ebe --- /dev/null +++ b/lucene_help.md @@ -0,0 +1,27 @@ +MediathekView verwendet im **Experten-Modus** zur Suche nach Filmtiteln nun die Volltextsuche [Apache Lucene](https://lucene.apache.org). Damit einhergehend kann die bisher verwendete Suchfunktionalität nicht mehr genutzt werden, jedoch erschließen sich durch die neu integrierte professionelle Suche viel mehr Möglichkeiten, die Ergebnisse einzugrenzen. + +MediathekView bietet die folgenden *tags* für die Formulierung von Suchabfragen an: + +| Name | Typ | Beschreibung | +| ------------- | ------- | ------------------------------------------------------------ | +| sender | String | Dieser entspricht einem der Werte, die im Filter-Dialog in der Senderliste hinterlegt sind. | +| titel | String | Titel wie in der Tabelle dargestellt. | +| thema | String | Thema wie in der Tabelle dargestellt. | +| beschreibung | String | Die im Programm dargestellte Filmbeschreibung. Vollindiziert. | +| livestream | Boolean | *true* wenn es sich um einen Livestream handelt, ansonsten nicht definiert. | +| highquality | Boolean | *true* wenn HQ Variante des Films verfügbar, ansonsten nicht definiert. | +| untertitel | Boolean | *true* wenn Untertitel für Download verfügbar, ansonsten nicht definiert. | +| trailerteaser | Boolean | *true*, wenn es sich um einen Trailer, Teaser oder eine Vorschau handelt, ansonsten nicht definiert. | +| audioversion | Boolean | *true*, wenn es sich um eine Hörfassung handelt, ansonsten nicht definiert. | +| signlanguage | Boolean | *true*, wenn es sich um eine Gebärdensprache-Version handelt. | +| sendedatum | Date | Sendedatum im Format *YYYYMMDD*, wenn keines vorhanden wird **19000101** gesetzt. | +| neu | Boolean | *true*, wenn der Film neu in die Liste aufgenommen wurde, ansonsten nicht definiert. | +| länge | int | Filmlänge in Sekunden, **0** wenn keine Information vorhanden ist. | +| größe | int | Filmgröße in Megabytes, **0** wenn keine Information vorhanden ist. | + +Es ist nicht möglich, Filme anhand von URLs bzw. Teilsegmenten davon zu suchen. + +Um sich mit der Abfragesyntax vertraut zu machen sind nachfolgende Links empfohlen: + +1. [Lucene Query Syntax](https://ci-builds.apache.org/job/Lucene/job/Lucene-Artifacts-main/javadoc/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package.description) +2. [Lucene Tutorial](http://lucenetutorial.com/lucene-query-syntax.html) \ No newline at end of file diff --git a/mvnw b/mvnw index 5551fde8e7..8d937f4c14 100755 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script +# Apache Maven Wrapper startup batch script, version 3.2.0 # # Required ENV vars: # ------------------ @@ -27,7 +27,6 @@ # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @@ -36,6 +35,10 @@ if [ -z "$MAVEN_SKIP_RC" ] ; then + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi @@ -50,7 +53,7 @@ fi cygwin=false; darwin=false; mingw=false -case "`uname`" in +case "$(uname)" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true @@ -58,9 +61,9 @@ case "`uname`" in # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME else - export JAVA_HOME="/Library/Java/Home" + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME fi fi ;; @@ -68,69 +71,38 @@ esac if [ -z "$JAVA_HOME" ] ; then if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` + JAVA_HOME=$(java-config --jre-home) fi fi -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") fi # For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" fi if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" else - javaExecutable="`readlink -f \"$javaExecutable\"`" + javaExecutable="$(readlink -f "\"$javaExecutable\"")" fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') JAVA_HOME="$javaHome" export JAVA_HOME fi @@ -146,7 +118,7 @@ if [ -z "$JAVACMD" ] ; then JAVACMD="$JAVA_HOME/bin/java" fi else - JAVACMD="`which java`" + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" fi fi @@ -160,12 +132,9 @@ if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { - if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" @@ -181,76 +150,99 @@ find_maven_basedir() { fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` + wdir=$(cd "$wdir/.." || exit 1; pwd) fi # end of workaround done - echo "${basedir}" + printf '%s' "$(cd "$basedir" || exit 1; pwd)" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" fi } -BASE_DIR=`find_maven_basedir "$(pwd)"` +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") if [ -z "$BASE_DIR" ]; then exit 1; fi +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. ########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" fi - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" fi - wget "$jarUrl" -O "$wrapperJarPath" elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" fi - curl -o "$wrapperJarPath" "$jarUrl" else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" fi fi fi @@ -259,28 +251,58 @@ fi # End of extension ########################################################################################## -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi fi + MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") fi +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +# shellcheck disable=SC2086 # safe args exec "$JAVACMD" \ $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 48363fa60b..f80fbad3e7 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -18,15 +18,14 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script +@REM Apache Maven Wrapper startup batch script, version 3.2.0 @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @@ -37,7 +36,7 @@ @echo off @REM set title of command window title %0 -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME @@ -46,8 +45,8 @@ if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* :skipRcPre @setlocal @@ -120,24 +119,69 @@ SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" -FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B ) @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central @REM This allows using the maven wrapper in projects that prohibit checking in binary data. if exist %WRAPPER_JAR% ( - echo Found %WRAPPER_JAR% + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) ) else ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" - echo Finished downloading %WRAPPER_JAR% + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) ) @REM End of extension -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end @@ -147,15 +191,15 @@ set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause +if "%MAVEN_BATCH_PAUSE%"=="on" pause -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% -exit /B %ERROR_CODE% +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index 1617cd6fb5..64b6c44354 100755 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ de.mediathekview MediathekView - 13.9.1 + 14.0.0 jar ${project.groupId}:${project.artifactId} @@ -61,6 +61,11 @@ file:///${project.basedir}/maven-repository + + install4j repo + https://maven.ej-technologies.com/repository + + oss.sonatype.org-snapshot https://oss.sonatype.org/content/repositories/snapshots @@ -75,29 +80,28 @@ UTF-8 - 17 - 17 - 16 - 1.6.21 + 20 + 20 + 19 + 1.8.21 ${jdk.language.version} ${jdk.language.version} - 22.0.0 - 3.21.0 - 2.7 + 23.1.0 + 3.23.1 + 2.9.0 2.11.0 3.12.0 - 11.1.1 - 1.0.1 - 2.4-SNAPSHOT + 11.1.2 + 3.1.1 31.1-jre 1.1.2 - 2.13.3 - 19-ea+7 + 2.14.1 + 20.0.1 1.3 - 5.11.0 - 5.8.2 - 2.17.2 + 5.13.0 + 5.9.0 + 2.20.0 3.3.0 3.1.0 3.8.1 @@ -110,58 +114,98 @@ 3.2.1 3.0.0-M5 11.0 - 4.2.0 - 4.9.3 - 3.0.0 - 4.6.3 - 3.36.0.3 - 17.1.6 + 4.10.0 + 5.0.0-alpha.10 + 4.7.0 + 3.41.2.1 1.9 mediathek.Main - 17/jdk-17.0.2+8 - - 17/17.0.2+9 - 17 + 20/jdk-20.0.1+9 + + 20/20.0.1 + 20 -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact -XX:MaxRAMPercentage=50.0 -XX:+UseStringDeduplication -Dfile.encoding=UTF-8 -DexternalUpdateCheck - + -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact -XX:MaxRAMPercentage=50.0 -XX:+UseStringDeduplication --add-opens java.desktop/sun.awt.X11=ALL-UNNAMED -Dfile.encoding=UTF-8 -DexternalUpdateCheck - - -Xmx1G -Xss1048576 -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact -XX:MaxRAMPercentage=100.0 - -XX:+UseStringDeduplication -Dfile.encoding=UTF-8 -DexternalUpdateCheck - --add-exports javafx.controls/com.sun.javafx.scene.control.inputmap=ALL-UNNAMED - --add-exports javafx.base/com.sun.javafx.event=ALL-UNNAMED - --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED - --add-exports javafx.graphics/com.sun.javafx.scene.traversal=ALL-UNNAMED - - - -Xmx1280M -XX:+UseG1GC -XX:+UseStringDeduplication -Dfile.encoding=UTF-8 - -DexternalUpdateCheck --add-exports javafx.controls/com.sun.javafx.scene.control.inputmap=ALL-UNNAMED - --add-exports javafx.base/com.sun.javafx.event=ALL-UNNAMED --add-exports - javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED --add-exports - javafx.graphics/com.sun.javafx.scene.traversal=ALL-UNNAMED --add-opens java.desktop/sun.awt.X11=ALL-UNNAMED - - ./install4j9.0.5 + ./install4j${install4j.version} - ${env.LICENSE_KEY_9} + ${env.LICENSE_KEY_10} + 10.0.5 + 9.5.0 + + com.github.lgooddatepicker + LGoodDatePicker + 11.2.1 + + + + org.apache.lucene + lucene-core + ${lucene.version} + + + org.apache.lucene + lucene-queryparser + ${lucene.version} + + + + com.install4j + install4j-runtime + ${install4j.version} + + + + org.jfree + jfreechart + 1.5.3 + + + + org.swinglabs.swingx + swingx-all + 1.6.5-1 + + com.formdev flatlaf ${flatlaf.version} + + com.formdev + flatlaf-extras + ${flatlaf.version} + + + com.formdev + flatlaf-swingx + ${flatlaf.version} + + + com.formdev + flatlaf-jide-oss + ${flatlaf.version} + + + com.formdev + jide-oss + 3.7.12 + es.blackleg @@ -185,31 +229,15 @@ ${picocli.version} - - eu.hansolo - tilesfx - ${tilesfx.version} - - - org.openjfx - * - - - junit - junit - - - - net.java.dev.jna - jna + jna-jpms ${jna.version} net.java.dev.jna - jna-platform + jna-platform-jpms ${jna.version} @@ -328,11 +356,6 @@ - - com.github.jiconfont - jiconfont-swing - 1.0.1 - com.github.jiconfont jiconfont-javafx @@ -350,24 +373,6 @@ 1.3.2 - - com.squareup.okio - okio - ${okio.version} - - - org.jetbrains.kotlin - kotlin-stdlib-common - - - - - - com.squareup.okio - okio-jvm - ${okio.version} - - com.squareup.okhttp3 okhttp @@ -461,11 +466,6 @@ kotlin-stdlib ${kotlin.version} - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - ${kotlin.version} - org.jetbrains.kotlin kotlin-test-junit @@ -550,9 +550,6 @@ target/generated-sources/annotations ${kotlin.language.target} - - -Xuse-ir - @@ -566,9 +563,6 @@ ${project.basedir}/src/test/kotlin ${kotlin.language.target} - - -Xuse-ir - @@ -718,12 +712,6 @@ ${javafx.version} ${javafx.platform} - - org.openjfx - javafx-media - ${javafx.version} - ${javafx.platform} - org.openjfx javafx-swing @@ -746,27 +734,20 @@ - com.coderplus.maven.plugins - copy-rename-maven-plugin - ${copy-rename-maven-plugin.version} + com.googlecode.maven-download-plugin + download-maven-plugin + 1.7.0 - copy-file - generate-sources + download-ffmpeg-exe-from-server + package - copy + wget - - - res/bin/ffmpeg.exe - target/res/bin/ffmpeg.exe - - - res/bin/ffmpeg.txt - target/res/bin/ffmpeg.txt - - + https://s3.mvorg.de/res/dev/windows/64/bin/ffmpeg.exe + ${project.build.directory}/res/bin + c075ca784ad26b3ad3f8f49474ee5187 @@ -810,89 +791,22 @@ - windows_32bit + + windows_arm64 windows - x86 + aarch64 win - windows32 + windows - - - org.openjfx - javafx-base - ${javafx.version} - ${javafx.platform} - provided - - - org.openjfx - javafx-controls - ${javafx.version} - ${javafx.platform} - provided - - - org.openjfx - javafx-media - ${javafx.version} - ${javafx.platform} - provided - - - org.openjfx - javafx-swing - ${javafx.version} - ${javafx.platform} - provided - - - org.openjfx - javafx-fxml - ${javafx.version} - ${javafx.platform} - provided - - - org.openjfx - javafx-graphics - ${javafx.version} - ${javafx.platform} - provided - - - - com.coderplus.maven.plugins - copy-rename-maven-plugin - ${copy-rename-maven-plugin.version} - - - copy-file - generate-sources - - copy - - - - - res/bin/ffmpeg32.exe - target/res/bin/ffmpeg.exe - - - res/bin/ffmpeg32.txt - target/res/bin/ffmpeg.txt - - - - - - org.apache.maven.plugins maven-shade-plugin @@ -957,12 +871,6 @@ ${javafx.version} ${javafx.platform} - - org.openjfx - javafx-media - ${javafx.version} - ${javafx.platform} - org.openjfx javafx-swing @@ -1026,7 +934,7 @@ - linux + linux-aarch64 arm @@ -1035,42 +943,30 @@ javafx-base ${javafx.version} ${javafx.platform} - provided org.openjfx javafx-controls ${javafx.version} ${javafx.platform} - provided - - - org.openjfx - javafx-media - ${javafx.version} - ${javafx.platform} - provided org.openjfx javafx-swing ${javafx.version} ${javafx.platform} - provided org.openjfx javafx-fxml ${javafx.version} ${javafx.platform} - provided org.openjfx javafx-graphics ${javafx.version} ${javafx.platform} - provided @@ -1125,58 +1021,66 @@ javafx-base ${javafx.version} ${javafx.platform} + provided org.openjfx javafx-controls ${javafx.version} ${javafx.platform} - - - org.openjfx - javafx-media - ${javafx.version} - ${javafx.platform} + provided org.openjfx javafx-swing ${javafx.version} ${javafx.platform} + provided org.openjfx javafx-fxml ${javafx.version} ${javafx.platform} + provided org.openjfx javafx-graphics ${javafx.version} ${javafx.platform} + provided - maven-resources-plugin - ${maven-resources-plugin.version} + com.googlecode.maven-download-plugin + download-maven-plugin + 1.7.0 - copy-mac-binaries - validate + download-shutdown-helper-from-server + package - copy-resources + wget + https://s3.mvorg.de/res/dev/mac/bin/common/mv_shutdown_helper ${project.build.directory}/bin - - - ${project.basedir}/res/macOS/bin - false - - + 3c8254ce318ef41da00517199c5a5e85 + + + + download-intel-ffmpeg-from-server + package + + wget + + + https://s3.mvorg.de/res/dev/mac/bin/intel/ffmpeg + ${project.build.directory}/bin + 9de5ebaae8f7ad4024a10345ee8d01d5 @@ -1257,11 +1161,6 @@ javafx-controls ${javafx.version} - - org.openjfx - javafx-media - ${javafx.version} - org.openjfx javafx-swing @@ -1283,23 +1182,32 @@ - maven-resources-plugin - ${maven-resources-plugin.version} + com.googlecode.maven-download-plugin + download-maven-plugin + 1.7.0 - copy-mac-binaries - validate + download-shutdown-helper-from-server + package + + wget + + + https://s3.mvorg.de/res/dev/mac/bin/common/mv_shutdown_helper + ${project.build.directory}/bin + 3c8254ce318ef41da00517199c5a5e85 + + + + download-apple-silicon-ffmpeg-from-server + package - copy-resources + wget + https://s3.mvorg.de/res/dev/mac/bin/apple_silicon/ffmpeg ${project.build.directory}/bin - - - ${project.basedir}/res/macOS/bin - false - - + 7d60eebebb96059d2d3c3a0599cdefd2 diff --git a/res/.resinfo b/res/.resinfo new file mode 100644 index 0000000000..3611f599ac --- /dev/null +++ b/res/.resinfo @@ -0,0 +1,7 @@ +Die Ressourcen, wie ffmpeg und Shellskripte aus dem bin Ordner findest du hier: +https://gitlab.com/mediathekview/mediathekview-buildres + +Im Repo benutze folgende Befehle um die Ressourcen zu erhalten: +git clone https://gitlab.com/mediathekview/mediathekview-buildres.git tmpres +mv tmpres/bin res/ +rm -rf tmpres diff --git a/res/bin/aria2-remote.sh b/res/bin/aria2-remote.sh deleted file mode 100755 index 987d49285c..0000000000 --- a/res/bin/aria2-remote.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# Das Skript muss evtl. noch -# "ausführbar" gemacht werden!!! - -# Programmparameter in MediathekView -# %f ** - -# oder ohne gesetztes Passwort -# %f ** - -# Start von aria2 auf dem Server -# aria2c --enable-rpc --rpc-listen-all --dir=/ZIELPFAD/FILME - - -log=/tmp/MediathekView-aria2.log -echo "Running $0 $*" >$log - -url=$1 -filename=$(basename "$2") - -aria2_server_url=$3 -aria2_server_secret=$4 - -#echo >>$log -#echo "MediathekView-aria2 parameters:" >>$log -#while [ -n "$1" ] -#do -# echo "$1" >>$log -# shift -#done - -echo >>$log -echo "url: " $url >>$log -echo "filename: " $filename >>$log -echo "aria2_server_url: " $aria2_server_url >>$log -echo "aria2_server_secret: " $aria2_server_secret >>$log - -id=medview -method=aria2.addUri - -if [ -n "$filename" ] -then - options=",{\"out\":\"${filename}\"}" -else - options="" -fi - -params="[\"token:${aria2_server_secret}\",[\"${url}\"]${options}]" -params_base64enc=$(echo "${params}" | base64 -w 0 -) -params_base64enc_urlenc=${params_base64enc//=/%3D} - -get="${aria2_server_url}?id=${id}&method=${method}¶ms=${params_base64enc_urlenc}" - -echo >>$log -echo "executing: $get" >>$log - -result=$(curl -S -k "$get") - -echo >>$log -echo "result: $result" >>$log - -if [[ $result =~ '"result":' ]] -then - exit 0 -else - exit 1 -fi - diff --git a/res/bin/ffmpeg.exe b/res/bin/ffmpeg.exe deleted file mode 100644 index d6f10660c0..0000000000 --- a/res/bin/ffmpeg.exe +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0557f26bb233a46339cd54c18887eb115cafec8d22ff1f67ac2681ed3a4f5e3f -size 121831424 diff --git a/res/bin/ffmpeg.txt b/res/bin/ffmpeg.txt deleted file mode 100644 index a1966c9ddc..0000000000 --- a/res/bin/ffmpeg.txt +++ /dev/null @@ -1,743 +0,0 @@ -FFmpeg 64-bit static Windows build from www.gyan.dev - -Version: 5.0.1-full_build-www.gyan.dev - -License: GPL v3 - -Source Code: https://github.com/FFmpeg/FFmpeg/commit/9687cae2b4 - -release-full build configuration: - -ARCH x86 (generic) -big-endian no -runtime cpu detection yes -standalone assembly yes -x86 assembler nasm -MMX enabled yes -MMXEXT enabled yes -3DNow! enabled yes -3DNow! extended enabled yes -SSE enabled yes -SSSE3 enabled yes -AESNI enabled yes -AVX enabled yes -AVX2 enabled yes -AVX-512 enabled yes -XOP enabled yes -FMA3 enabled yes -FMA4 enabled yes -i686 features enabled yes -CMOV is fast yes -EBX available yes -EBP available yes -debug symbols yes -strip symbols yes -optimize for size no -optimizations yes -static yes -shared no -postprocessing support yes -network support yes -threading support pthreads -safe bitstream reader yes -texi2html enabled no -perl enabled yes -pod2man enabled yes -makeinfo enabled yes -makeinfo supports HTML yes -xmllint enabled yes - -External libraries: -avisynth libilbc libtheora -bzlib liblensfun libtwolame -chromaprint libmodplug libuavs3d -frei0r libmp3lame libvidstab -gmp libmysofa libvmaf -gnutls libopencore_amrnb libvo_amrwbenc -iconv libopencore_amrwb libvorbis -ladspa libopenjpeg libvpx -libaom libopenmpt libwebp -libass libopus libx264 -libbluray libplacebo libx265 -libbs2b librav1e libxavs2 -libcaca librist libxml2 -libcdio librubberband libxvid -libdav1d libshaderc libzimg -libdavs2 libshine libzmq -libflite libsnappy libzvbi -libfontconfig libsoxr lzma -libfreetype libspeex mediafoundation -libfribidi libsrt sdl2 -libgme libssh zlib -libgsm libsvtav1 - -External libraries providing hardware acceleration: -amf d3d11va nvdec -cuda dxva2 nvenc -cuda_llvm ffnvcodec opencl -cuvid libmfx vulkan - -Libraries: -avcodec avformat swresample -avdevice avutil swscale -avfilter postproc - -Programs: -ffmpeg ffplay ffprobe - -Enabled decoders: -aac fourxm pcm_u32be -aac_fixed fraps pcm_u32le -aac_latm frwu pcm_u8 -aasc g2m pcm_vidc -ac3 g723_1 pcx -ac3_fixed g729 pfm -acelp_kelvin gdv pgm -adpcm_4xm gem pgmyuv -adpcm_adx gif pgssub -adpcm_afc gremlin_dpcm pgx -adpcm_agm gsm photocd -adpcm_aica gsm_ms pictor -adpcm_argo h261 pixlet -adpcm_ct h263 pjs -adpcm_dtk h263i png -adpcm_ea h263p ppm -adpcm_ea_maxis_xa h264 prores -adpcm_ea_r1 h264_cuvid prosumer -adpcm_ea_r2 h264_qsv psd -adpcm_ea_r3 hap ptx -adpcm_ea_xas hca qcelp -adpcm_g722 hcom qdm2 -adpcm_g726 hevc qdmc -adpcm_g726le hevc_cuvid qdraw -adpcm_ima_acorn hevc_qsv qpeg -adpcm_ima_alp hnm4_video qtrle -adpcm_ima_amv hq_hqa r10k -adpcm_ima_apc hqx r210 -adpcm_ima_apm huffyuv ra_144 -adpcm_ima_cunning hymt ra_288 -adpcm_ima_dat4 iac ralf -adpcm_ima_dk3 idcin rasc -adpcm_ima_dk4 idf rawvideo -adpcm_ima_ea_eacs iff_ilbm realtext -adpcm_ima_ea_sead ilbc rl2 -adpcm_ima_iss imc roq -adpcm_ima_moflex imm4 roq_dpcm -adpcm_ima_mtf imm5 rpza -adpcm_ima_oki indeo2 rscc -adpcm_ima_qt indeo3 rv10 -adpcm_ima_rad indeo4 rv20 -adpcm_ima_smjpeg indeo5 rv30 -adpcm_ima_ssi interplay_acm rv40 -adpcm_ima_wav interplay_dpcm s302m -adpcm_ima_ws interplay_video sami -adpcm_ms ipu sanm -adpcm_mtaf jacosub sbc -adpcm_psx jpeg2000 scpr -adpcm_sbpro_2 jpegls screenpresso -adpcm_sbpro_3 jv sdx2_dpcm -adpcm_sbpro_4 kgv1 sga -adpcm_swf kmvc sgi -adpcm_thp lagarith sgirle -adpcm_thp_le libaom_av1 sheervideo -adpcm_vima libdav1d shorten -adpcm_xa libdavs2 simbiosis_imx -adpcm_yamaha libgsm sipr -adpcm_zork libgsm_ms siren -agm libilbc smackaud -aic libopencore_amrnb smacker -alac libopencore_amrwb smc -alias_pix libopenjpeg smvjpeg -als libopus snow -amrnb libspeex sol_dpcm -amrwb libuavs3d sonic -amv libvorbis sp5x -anm libvpx_vp8 speedhq -ansi libvpx_vp9 speex -ape libzvbi_teletext srgc -apng loco srt -aptx lscr ssa -aptx_hd m101 stl -arbc mace3 subrip -argo mace6 subviewer -ass magicyuv subviewer1 -asv1 mdec sunrast -asv2 metasound svq1 -atrac1 microdvd svq3 -atrac3 mimic tak -atrac3al mjpeg targa -atrac3p mjpeg_cuvid targa_y216 -atrac3pal mjpeg_qsv tdsc -atrac9 mjpegb text -aura mlp theora -aura2 mmvideo thp -av1 mobiclip tiertexseqvideo -av1_cuvid motionpixels tiff -av1_qsv movtext tmv -avrn mp1 truehd -avrp mp1float truemotion1 -avs mp2 truemotion2 -avui mp2float truemotion2rt -ayuv mp3 truespeech -bethsoftvid mp3adu tscc -bfi mp3adufloat tscc2 -bink mp3float tta -binkaudio_dct mp3on4 twinvq -binkaudio_rdft mp3on4float txd -bintext mpc7 ulti -bitpacked mpc8 utvideo -bmp mpeg1_cuvid v210 -bmv_audio mpeg1video v210x -bmv_video mpeg2_cuvid v308 -brender_pix mpeg2_qsv v408 -c93 mpeg2video v410 -cavs mpeg4 vb -ccaption mpeg4_cuvid vble -cdgraphics mpegvideo vc1 -cdtoons mpl2 vc1_cuvid -cdxl msa1 vc1_qsv -cfhd mscc vc1image -cinepak msmpeg4v1 vcr1 -clearvideo msmpeg4v2 vmdaudio -cljr msmpeg4v3 vmdvideo -cllc msnsiren vmnc -comfortnoise msp2 vorbis -cook msrle vp3 -cpia mss1 vp4 -cri mss2 vp5 -cscd msvideo1 vp6 -cyuv mszh vp6a -dca mts2 vp6f -dds mv30 vp7 -derf_dpcm mvc1 vp8 -dfa mvc2 vp8_cuvid -dirac mvdv vp8_qsv -dnxhd mvha vp9 -dolby_e mwsc vp9_cuvid -dpx mxpeg vp9_qsv -dsd_lsbf nellymoser vplayer -dsd_lsbf_planar notchlc vqa -dsd_msbf nuv wavpack -dsd_msbf_planar on2avc wcmv -dsicinaudio opus webp -dsicinvideo paf_audio webvtt -dss_sp paf_video wmalossless -dst pam wmapro -dvaudio pbm wmav1 -dvbsub pcm_alaw wmav2 -dvdsub pcm_bluray wmavoice -dvvideo pcm_dvd wmv1 -dxa pcm_f16le wmv2 -dxtory pcm_f24le wmv3 -dxv pcm_f32be wmv3image -eac3 pcm_f32le wnv1 -eacmv pcm_f64be wrapped_avframe -eamad pcm_f64le ws_snd1 -eatgq pcm_lxf xan_dpcm -eatgv pcm_mulaw xan_wc3 -eatqi pcm_s16be xan_wc4 -eightbps pcm_s16be_planar xbin -eightsvx_exp pcm_s16le xbm -eightsvx_fib pcm_s16le_planar xface -escape124 pcm_s24be xl -escape130 pcm_s24daud xma1 -evrc pcm_s24le xma2 -exr pcm_s24le_planar xpm -fastaudio pcm_s32be xsub -ffv1 pcm_s32le xwd -ffvhuff pcm_s32le_planar y41p -ffwavesynth pcm_s64be ylc -fic pcm_s64le yop -fits pcm_s8 yuv4 -flac pcm_s8_planar zero12v -flashsv pcm_sga zerocodec -flashsv2 pcm_u16be zlib -flic pcm_u16le zmbv -flv pcm_u24be -fmvc pcm_u24le - -Enabled encoders: -a64multi jpeg2000 pcm_u16le -a64multi5 jpegls pcm_u24be -aac libaom_av1 pcm_u24le -aac_mf libgsm pcm_u32be -ac3 libgsm_ms pcm_u32le -ac3_fixed libilbc pcm_u8 -ac3_mf libmp3lame pcm_vidc -adpcm_adx libopencore_amrnb pcx -adpcm_argo libopenjpeg pfm -adpcm_g722 libopus pgm -adpcm_g726 librav1e pgmyuv -adpcm_g726le libshine png -adpcm_ima_alp libspeex ppm -adpcm_ima_amv libsvtav1 prores -adpcm_ima_apm libtheora prores_aw -adpcm_ima_qt libtwolame prores_ks -adpcm_ima_ssi libvo_amrwbenc qtrle -adpcm_ima_wav libvorbis r10k -adpcm_ima_ws libvpx_vp8 r210 -adpcm_ms libvpx_vp9 ra_144 -adpcm_swf libwebp rawvideo -adpcm_yamaha libwebp_anim roq -alac libx264 roq_dpcm -alias_pix libx264rgb rpza -amv libx265 rv10 -apng libxavs2 rv20 -aptx libxvid s302m -aptx_hd ljpeg sbc -ass magicyuv sgi -asv1 mjpeg smc -asv2 mjpeg_qsv snow -avrp mlp sonic -avui movtext sonic_ls -ayuv mp2 speedhq -bitpacked mp2fixed srt -bmp mp3_mf ssa -cfhd mpeg1video subrip -cinepak mpeg2_qsv sunrast -cljr mpeg2video svq1 -comfortnoise mpeg4 targa -dca msmpeg4v2 text -dnxhd msmpeg4v3 tiff -dpx msvideo1 truehd -dvbsub nellymoser tta -dvdsub opus ttml -dvvideo pam utvideo -eac3 pbm v210 -exr pcm_alaw v308 -ffv1 pcm_dvd v408 -ffvhuff pcm_f32be v410 -fits pcm_f32le vc2 -flac pcm_f64be vorbis -flashsv pcm_f64le vp9_qsv -flashsv2 pcm_mulaw wavpack -flv pcm_s16be webvtt -g723_1 pcm_s16be_planar wmav1 -gif pcm_s16le wmav2 -h261 pcm_s16le_planar wmv1 -h263 pcm_s24be wmv2 -h263p pcm_s24daud wrapped_avframe -h264_amf pcm_s24le xbm -h264_mf pcm_s24le_planar xface -h264_nvenc pcm_s32be xsub -h264_qsv pcm_s32le xwd -hap pcm_s32le_planar y41p -hevc_amf pcm_s64be yuv4 -hevc_mf pcm_s64le zlib -hevc_nvenc pcm_s8 zmbv -hevc_qsv pcm_s8_planar -huffyuv pcm_u16be - -Enabled hwaccels: -av1_d3d11va hevc_nvdec vc1_nvdec -av1_d3d11va2 mjpeg_nvdec vp8_nvdec -av1_dxva2 mpeg1_nvdec vp9_d3d11va -av1_nvdec mpeg2_d3d11va vp9_d3d11va2 -h264_d3d11va mpeg2_d3d11va2 vp9_dxva2 -h264_d3d11va2 mpeg2_dxva2 vp9_nvdec -h264_dxva2 mpeg2_nvdec wmv3_d3d11va -h264_nvdec mpeg4_nvdec wmv3_d3d11va2 -hevc_d3d11va vc1_d3d11va wmv3_dxva2 -hevc_d3d11va2 vc1_d3d11va2 wmv3_nvdec -hevc_dxva2 vc1_dxva2 - -Enabled parsers: -aac dvbsub mpegvideo -aac_latm dvd_nav opus -ac3 dvdsub png -adx flac pnm -amr g723_1 rv30 -av1 g729 rv40 -avs2 gif sbc -avs3 gsm sipr -bmp h261 tak -cavsvideo h263 vc1 -cook h264 vorbis -cri hevc vp3 -dca ipu vp8 -dirac jpeg2000 vp9 -dnxhd mjpeg webp -dolby_e mlp xbm -dpx mpeg4video xma -dvaudio mpegaudio - -Enabled demuxers: -aa ico pcm_f64le -aac idcin pcm_mulaw -aax idf pcm_s16be -ac3 iff pcm_s16le -ace ifv pcm_s24be -acm ilbc pcm_s24le -act image2 pcm_s32be -adf image2_alias_pix pcm_s32le -adp image2_brender_pix pcm_s8 -ads image2pipe pcm_u16be -adx image_bmp_pipe pcm_u16le -aea image_cri_pipe pcm_u24be -afc image_dds_pipe pcm_u24le -aiff image_dpx_pipe pcm_u32be -aix image_exr_pipe pcm_u32le -alp image_gem_pipe pcm_u8 -amr image_gif_pipe pcm_vidc -amrnb image_j2k_pipe pjs -amrwb image_jpeg_pipe pmp -anm image_jpegls_pipe pp_bnk -apc image_pam_pipe pva -ape image_pbm_pipe pvf -apm image_pcx_pipe qcp -apng image_pgm_pipe r3d -aptx image_pgmyuv_pipe rawvideo -aptx_hd image_pgx_pipe realtext -aqtitle image_photocd_pipe redspark -argo_asf image_pictor_pipe rl2 -argo_brp image_png_pipe rm -argo_cvg image_ppm_pipe roq -asf image_psd_pipe rpl -asf_o image_qdraw_pipe rsd -ass image_sgi_pipe rso -ast image_sunrast_pipe rtp -au image_svg_pipe rtsp -av1 image_tiff_pipe s337m -avi image_webp_pipe sami -avisynth image_xbm_pipe sap -avr image_xpm_pipe sbc -avs image_xwd_pipe sbg -avs2 imf scc -avs3 ingenient scd -bethsoftvid ipmovie sdp -bfi ipu sdr2 -bfstm ircam sds -bink iss sdx -binka iv8 segafilm -bintext ivf ser -bit ivr sga -bitpacked jacosub shorten -bmv jv siff -boa kux simbiosis_imx -brstm kvag sln -c93 libgme smacker -caf libmodplug smjpeg -cavsvideo libopenmpt smush -cdg live_flv sol -cdxl lmlm4 sox -cine loas spdif -codec2 lrc srt -codec2raw luodat stl -concat lvf str -dash lxf subviewer -data m4v subviewer1 -daud matroska sup -dcstr mca svag -derf mcc svs -dfa mgsts swf -dhav microdvd tak -dirac mjpeg tedcaptions -dnxhd mjpeg_2000 thp -dsf mlp threedostr -dsicin mlv tiertexseq -dss mm tmv -dts mmf truehd -dtshd mods tta -dv moflex tty -dvbsub mov txd -dvbtxt mp3 ty -dxa mpc v210 -ea mpc8 v210x -ea_cdata mpegps vag -eac3 mpegts vc1 -epaf mpegtsraw vc1t -ffmetadata mpegvideo vividas -filmstrip mpjpeg vivo -fits mpl2 vmd -flac mpsub vobsub -flic msf voc -flv msnwc_tcp vpk -fourxm msp vplayer -frm mtaf vqf -fsb mtv w64 -fwse musx wav -g722 mv wc3 -g723_1 mvi webm_dash_manifest -g726 mxf webvtt -g726le mxg wsaud -g729 nc wsd -gdv nistsphere wsvqa -genh nsp wtv -gif nsv wv -gsm nut wve -gxf nuv xa -h261 obu xbin -h263 ogg xmv -h264 oma xvag -hca paf xwma -hcom pcm_alaw yop -hevc pcm_f32be yuv4mpegpipe -hls pcm_f32le -hnm pcm_f64be - -Enabled muxers: -a64 h264 pcm_s24be -ac3 hash pcm_s24le -adts hds pcm_s32be -adx hevc pcm_s32le -aiff hls pcm_s8 -alp ico pcm_u16be -amr ilbc pcm_u16le -amv image2 pcm_u24be -apm image2pipe pcm_u24le -apng ipod pcm_u32be -aptx ircam pcm_u32le -aptx_hd ismv pcm_u8 -argo_asf ivf pcm_vidc -argo_cvg jacosub psp -asf kvag rawvideo -asf_stream latm rm -ass lrc roq -ast m4v rso -au matroska rtp -avi matroska_audio rtp_mpegts -avm2 md5 rtsp -avs2 microdvd sap -avs3 mjpeg sbc -bit mkvtimestamp_v2 scc -caf mlp segafilm -cavsvideo mmf segment -chromaprint mov smjpeg -codec2 mp2 smoothstreaming -codec2raw mp3 sox -crc mp4 spdif -dash mpeg1system spx -data mpeg1vcd srt -daud mpeg1video stream_segment -dirac mpeg2dvd streamhash -dnxhd mpeg2svcd sup -dts mpeg2video swf -dv mpeg2vob tee -eac3 mpegts tg2 -f4v mpjpeg tgp -ffmetadata mxf truehd -fifo mxf_d10 tta -fifo_test mxf_opatom ttml -filmstrip null uncodedframecrc -fits nut vc1 -flac obu vc1t -flv oga voc -framecrc ogg w64 -framehash ogv wav -framemd5 oma webm -g722 opus webm_chunk -g723_1 pcm_alaw webm_dash_manifest -g726 pcm_f32be webp -g726le pcm_f32le webvtt -gif pcm_f64be wsaud -gsm pcm_f64le wtv -gxf pcm_mulaw wv -h261 pcm_s16be yuv4mpegpipe -h263 pcm_s16le - -Enabled protocols: -async http rtmpe -bluray httpproxy rtmps -cache https rtmpt -concat icecast rtmpte -concatf librist rtmpts -crypto libsrt rtp -data libssh srtp -ffrtmpcrypt libzmq subfile -ffrtmphttp md5 tcp -file mmsh tee -ftp mmst tls -gopher pipe udp -gophers prompeg udplite -hls rtmp - -Enabled filters: -abench dedot pad_opencl -abitscope deesser pal100bars -acompressor deflate pal75bars -acontrast deflicker palettegen -acopy deinterlace_qsv paletteuse -acrossfade dejudder pan -acrossover delogo perms -acrusher derain perspective -acue deshake phase -addroi deshake_opencl photosensitivity -adeclick despill pixdesctest -adeclip detelecine pixscope -adecorrelate dilation pp -adelay dilation_opencl pp7 -adenorm displace premultiply -aderivative dnn_classify prewitt -adrawgraph dnn_detect prewitt_opencl -adynamicequalizer dnn_processing program_opencl -adynamicsmooth doubleweave pseudocolor -aecho drawbox psnr -aemphasis drawgraph pullup -aeval drawgrid qp -aevalsrc drawtext random -aexciter drmeter readeia608 -afade dynaudnorm readvitc -afftdn earwax realtime -afftfilt ebur128 remap -afifo edgedetect removegrain -afir elbg removelogo -afirsrc entropy repeatfields -aformat epx replaygain -afreqshift eq reverse -afwtdn equalizer rgbashift -agate erosion rgbtestsrc -agraphmonitor erosion_opencl roberts -ahistogram estdif roberts_opencl -aiir exposure rotate -aintegral extractplanes rubberband -ainterleave extrastereo sab -alatency fade scale -alimiter fftdnoiz scale2ref -allpass fftfilt scale_cuda -allrgb field scale_qsv -allyuv fieldhint scale_vulkan -aloop fieldmatch scdet -alphaextract fieldorder scharr -alphamerge fifo scroll -amerge fillborders segment -ametadata find_rect select -amix firequalizer selectivecolor -amovie flanger sendcmd -amplify flip_vulkan separatefields -amultiply flite setdar -anequalizer floodfill setfield -anlmdn format setparams -anlmf fps setpts -anlms framepack setrange -anoisesrc framerate setsar -anull framestep settb -anullsink freezedetect shear -anullsrc freezeframes showcqt -apad frei0r showfreqs -aperms frei0r_src showinfo -aphasemeter fspp showpalette -aphaser gblur showspatial -aphaseshift gblur_vulkan showspectrum -apsyclip geq showspectrumpic -apulsator gradfun showvolume -arealtime gradients showwaves -aresample graphmonitor showwavespic -areverse grayworld shuffleframes -arnndn greyedge shufflepixels -asdr guided shuffleplanes -asegment haas sidechaincompress -aselect haldclut sidechaingate -asendcmd haldclutsrc sidedata -asetnsamples hdcd sierpinski -asetpts headphone signalstats -asetrate hflip signature -asettb hflip_vulkan silencedetect -ashowinfo highpass silenceremove -asidedata highshelf sinc -asoftclip hilbert sine -aspectralstats histeq smartblur -asplit histogram smptebars -ass hqdn3d smptehdbars -astats hqx sobel -astreamselect hstack sobel_opencl -asubboost hsvhold sofalizer -asubcut hsvkey spectrumsynth -asupercut hue speechnorm -asuperpass huesaturation split -asuperstop hwdownload spp -atadenoise hwmap sr -atempo hwupload ssim -atilt hwupload_cuda stereo3d -atrim hysteresis stereotools -avectorscope identity stereowiden -avgblur idet streamselect -avgblur_opencl il subtitles -avgblur_vulkan inflate super2xsai -axcorrelate interlace superequalizer -azmq interleave surround -bandpass join swaprect -bandreject kerndeint swapuv -bass kirsch tblend -bbox ladspa telecine -bench lagfun testsrc -bilateral latency testsrc2 -biquad lenscorrection thistogram -bitplanenoise lensfun threshold -blackdetect libplacebo thumbnail -blackframe libvmaf thumbnail_cuda -blend life tile -bm3d limitdiff tinterlace -boxblur limiter tlut2 -boxblur_opencl loop tmedian -bs2b loudnorm tmidequalizer -bwdif lowpass tmix -cas lowshelf tonemap -cellauto lumakey tonemap_opencl -channelmap lut tpad -channelsplit lut1d transpose -chorus lut2 transpose_opencl -chromaber_vulkan lut3d transpose_vulkan -chromahold lutrgb treble -chromakey lutyuv tremolo -chromanr mandelbrot trim -chromashift maskedclamp unpremultiply -ciescope maskedmax unsharp -codecview maskedmerge unsharp_opencl -color maskedmin untile -colorbalance maskedthreshold v360 -colorchannelmixer maskfun vaguedenoiser -colorcontrast mcompand varblur -colorcorrect median vectorscope -colorhold mergeplanes vflip -colorize mestimate vflip_vulkan -colorkey metadata vfrdet -colorkey_opencl midequalizer vibrance -colorlevels minterpolate vibrato -colormatrix mix vidstabdetect -colorspace monochrome vidstabtransform -colorspectrum morpho vif -colortemperature movie vignette -compand mpdecimate vmafmotion -compensationdelay mptestsrc volume -concat msad volumedetect -convolution negate vpp_qsv -convolution_opencl nlmeans vstack -convolve nlmeans_opencl w3fdif -copy nnedi waveform -cover_rect noformat weave -crop noise xbr -cropdetect normalize xcorrelate -crossfeed null xfade -crystalizer nullsink xfade_opencl -cue nullsrc xmedian -curves openclsrc xstack -datascope oscilloscope yadif -dblur overlay yadif_cuda -dcshift overlay_cuda yaepblur -dctdnoiz overlay_opencl yuvtestsrc -deband overlay_qsv zmq -deblock overlay_vulkan zoompan -decimate owdenoise zscale -deconvolve pad - -Enabled bsfs: -aac_adtstoasc hapqa_extract pcm_rechunk -av1_frame_merge hevc_metadata prores_metadata -av1_frame_split hevc_mp4toannexb remove_extradata -av1_metadata imx_dump_header setts -chomp mjpeg2jpeg text2movsub -dca_core mjpega_dump_header trace_headers -dump_extradata mov2textsub truehd_core -eac3_core mp3_header_decompress vp9_metadata -extract_extradata mpeg2_metadata vp9_raw_reorder -filter_units mpeg4_unpack_bframes vp9_superframe -h264_metadata noise vp9_superframe_split -h264_mp4toannexb null -h264_redundant_pps opus_metadata - -Enabled indevs: -dshow lavfi vfwcap -gdigrab libcdio - -Enabled outdevs: -caca sdl2 diff --git a/res/bin/ffmpeg32.exe b/res/bin/ffmpeg32.exe deleted file mode 100755 index a1d3858960..0000000000 --- a/res/bin/ffmpeg32.exe +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c9d51e085f9c2921616a040554cd3e621c265f01aa2d13fac918dcdc6e9ae3a -size 53479424 diff --git a/res/bin/ffmpeg32.txt b/res/bin/ffmpeg32.txt deleted file mode 100644 index fbcec27d0b..0000000000 --- a/res/bin/ffmpeg32.txt +++ /dev/null @@ -1,114 +0,0 @@ -Zeranoe FFmpeg Builds - -Build: ffmpeg-4.3-win32-static - -Configuration: - --enable-gpl - --enable-version3 - --enable-sdl2 - --enable-fontconfig - --enable-gnutls - --enable-iconv - --enable-libass - --enable-libdav1d - --enable-libbluray - --enable-libfreetype - --enable-libmp3lame - --enable-libopencore-amrnb - --enable-libopencore-amrwb - --enable-libopenjpeg - --enable-libopus - --enable-libshine - --enable-libsnappy - --enable-libsoxr - --enable-libsrt - --enable-libtheora - --enable-libtwolame - --enable-libvpx - --enable-libwavpack - --enable-libwebp - --enable-libx264 - --enable-libx265 - --enable-libxml2 - --enable-libzimg - --enable-lzma - --enable-zlib - --enable-gmp - --enable-libvidstab - --enable-libvmaf - --enable-libvorbis - --enable-libvo-amrwbenc - --enable-libmysofa - --enable-libspeex - --enable-libxvid - --enable-libaom - --enable-libgsm - --disable-w32threads - --enable-libmfx - --enable-ffnvcodec - --enable-cuda-llvm - --enable-cuvid - --enable-d3d11va - --enable-nvenc - --enable-nvdec - --enable-dxva2 - --enable-avisynth - --enable-libopenmpt - --enable-amf - -Libraries: - SDL 20200619-fb6395c - Fontconfig 2.13.92 - GnuTLS 3.6.14 - libiconv 1.16 - libass 20200615-7959e61 - dav1d 20200621-54f9206 - libbluray 20200517-245baa7 - FreeType 2.10.2 - LAME 3.100 - OpenCORE AMR 20170731-07a5be4 - OpenJPEG 20200610-25fb144 - Opus 20200618-f8ed894 - shine 20190420-76ea4f0 - Snappy 1.1.8 - libsoxr 20180224-945b592 - SRT 20200520-9f7068d - Theora 20200618-f98989a - TwoLAME 0.4.0 - vpx 20200619-d9a69a1 - WavPack 5.3.0 - WebP 20200505-e3c259a - x264 20200615-4c9b076 - x265 20200529-73ca1d7 - libxml2 2.9.10 - z.lib 20200604-6dec143 - XZ Utils 5.2.5 - zlib 1.2.11 - GMP 6.2.0 - vid.stab 20190213-aeabc8d - VMAF 20200617-3ff32b8 - Vorbis 20200616-5fd186e - VisualOn AMR-WB 20141107-3b3fcd0 - libmysofa 20200614-90f0089 - Speex 20191110-7db954e - Xvid 1.3.5 - aom 20200620-8c113ea - GSM 1.0.19 - libmfx 1.28 - nv-codec-headers 20191126-250292d - AviSynth+ 20200619-1eb7ce6 - OpenMPT 20200621-9082512 - AMF 20200515-802f92e - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 4f08d2e715..3dfe146877 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -1,14 +1,11 @@ package mediathek; -import com.formdev.flatlaf.FlatLightLaf; -import com.google.common.base.Stopwatch; +import com.formdev.flatlaf.FlatLaf; import com.sun.jna.platform.win32.VersionHelpers; import javafx.application.Platform; -import javafx.embed.swing.JFXPanel; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.*; import mediathek.controller.history.SeenHistoryMigrator; +import mediathek.daten.IndexedFilmList; import mediathek.gui.dialog.DialogStarteinstellungen; import mediathek.javafx.AustrianVlcCheck; import mediathek.javafx.tool.JFXHiddenApplication; @@ -16,9 +13,7 @@ import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; import mediathek.tool.affinity.Affinity; -import mediathek.tool.javafx.FXErrorDialog; import mediathek.tool.migrator.SettingsMigrator; -import mediathek.tool.swing.SwingUIFontChanger; import mediathek.tool.swing.ThreadCheckingRepaintManager; import mediathek.windows.MediathekGuiWindows; import mediathek.x11.MediathekGuiX11; @@ -89,8 +84,7 @@ private static void removeMediaDb() { logger.info("Moving old unsupported media database to trash."); mediathek.tool.FileUtils.moveToTrash(mediaDbPath); } - } - catch (IOException ignored) { + } catch (IOException ignored) { } } @@ -125,8 +119,7 @@ private static void setupLogging() { final PatternLayout consolePattern; if (Config.isEnhancedLoggingEnabled() || Config.isDebugModeEnabled()) { consolePattern = PatternLayout.newBuilder().withPattern("[%-5level] [%t] %c - %msg%n").build(); - } - else { + } else { consolePattern = PatternLayout.newBuilder().withPattern(". %msg%n").build(); } @@ -219,24 +212,21 @@ private static void printVersionInformation() { */ private static void migrateOldConfigSettings() { var settingsDir = StandardLocations.getSettingsDirectory().toString(); - if (settingsDir != null && !settingsDir.isEmpty()) { + if (!settingsDir.isEmpty()) { Path pSettingsDir = Paths.get(settingsDir); if (Files.exists(pSettingsDir)) { //convert existing settings Path settingsFile = pSettingsDir.resolve(Konstanten.CONFIG_FILE); if (Files.exists(settingsFile)) { - logger.trace("{} exists", Konstanten.CONFIG_FILE); - logger.trace("migrating old config settings"); + logger.trace("migrating old config settings {}", settingsFile.toAbsolutePath().toString()); try { SettingsMigrator migrator = new SettingsMigrator(settingsFile); migrator.migrate(); - } - catch (Exception e) { + } catch (Exception e) { logger.error("settings migration error", e); } } - } - else + } else logger.trace("nothing to migrate"); } } @@ -244,8 +234,7 @@ private static void migrateOldConfigSettings() { private static void printPortableModeInfo() { if (Config.isPortableMode()) { logger.info("Configuring baseFilePath {} for portable mode", Config.baseFilePath); - } - else + } else logger.info("Configuring for non-portable mode"); } @@ -277,133 +266,219 @@ private static void setupDockIcon() { } } - private static final Color JTABLE_ALTERNATE_ROW_COLOR = new Color(247, 247, 247); private static void setupFlatLaf() { - FlatLightLaf.setup(); + var darkMode = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.APPLICATION_DARK_MODE, false); + LookAndFeel laf; - UIManager.put("TabbedPane.showTabSeparators", true ); - // install alternate row color only for windows >8 and macOS, Linux - boolean installAlternateRowColor; - if (SystemUtils.IS_OS_WINDOWS && VersionHelpers.IsWindows8OrGreater()) { - installAlternateRowColor = true; + if (darkMode) { + laf = DarkModeFactory.getLookAndFeel(); + } + else { + laf = LightModeFactory.getLookAndFeel(); } - else installAlternateRowColor = SystemUtils.IS_OS_MAC_OSX || SystemUtils.IS_OS_LINUX; + FlatLaf.setup(laf); + } - if (installAlternateRowColor) - UIManager.put("Table.alternateRowColor", JTABLE_ALTERNATE_ROW_COLOR); + /** + * Check if Shenandoah GC settings are supplied to JVM. + * Otherwise display warning dialog. + */ + private static void checkJVMSettings() { + RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + boolean correctParameters = false; + var paramList = runtimeMXBean.getInputArguments(); + + var useShenandoahGC = paramList.stream().filter(s -> s.equalsIgnoreCase("-XX:+UseShenandoahGC")).findAny().stream().count(); + var shenandoahHeuristics = paramList.stream().filter(s -> s.equalsIgnoreCase("-XX:ShenandoahGCHeuristics=compact")).findAny().stream().count(); + var stringDedup = paramList.stream().filter(s -> s.equalsIgnoreCase("-XX:+UseStringDeduplication")).findAny().stream().count(); + var maxRamPct = paramList.stream().filter(s -> s.startsWith("-XX:MaxRAMPercentage=")).findAny().stream().count(); + var addOpens = paramList.stream().filter(s -> s.equalsIgnoreCase("--add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED")).findAny().stream().count(); + + //Incorrect VM params + var mxParamCount = paramList.stream().filter(s -> s.startsWith("-Xmx")).findAny().stream().count(); + + if ((useShenandoahGC > 0) + && (shenandoahHeuristics > 0) + && (stringDedup > 0) + && (maxRamPct > 0) + && (mxParamCount == 0)) + correctParameters = true; + + if (SystemUtils.IS_OS_LINUX) { + if (addOpens == 0) + correctParameters = false; + } + + if (!correctParameters) { + //show error dialog + logger.warn("Detected incorrect JVM parameters! Please modify your settings"); + var message = "" + + "Inkorrekte/fehlende JVM Parameter erkannt

" + + "Bitte stellen Sie sicher, dass die folgenden Parameter an die JVM übergeben werden:
" + + "
    " + + "
  • -XX:+UseShenandoahGC
  • " + + "
  • -XX:ShenandoahGCHeuristics=compact
  • " + + "
  • -XX:+UseStringDeduplication
  • " + + "
  • -XX:MaxRAMPercentage=XX.X
  • "; + if (SystemUtils.IS_OS_LINUX) { + message += "
  • --add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED
  • "; + } + + message += "

" + + "-Xmx sollte nicht mehr genutzt werden!" + + ""; + + JOptionPane.showMessageDialog(null,message, Konstanten.PROGRAMMNAME, + JOptionPane.WARNING_MESSAGE); + } + } + + /** + * Check if a non-floating point scale factor is set on Linux. + * Java 18 VM does not support fractional scaling. + */ + private static void checkUiScaleSetting() { + var strScale = System.getProperty("sun.java2d.uiScale"); + if (strScale != null) { + try { + Integer.parseInt(strScale); + } + catch (NumberFormatException ex) { + // not an int -> show warning + // fractional scale is NOT supported under Linux, must use integer only. + var scaleFactor = Float.parseFloat(strScale); + System.out.println("uiScale factor: " + scaleFactor); + var newScale = Math.round(scaleFactor); + System.out.println("new scale: " + newScale); + JOptionPane.showMessageDialog(null, + "" + + "Sie verwenden den Parameter -Dsun.java2d.uiScale=" + strScale + ".
" + + "Java unter Linux unterstützt nur ganzzahlige Skalierung!

" + + "Sie sollten -Dsun.java2d.uiScale=" + newScale + " oder größer verwenden falls die Schriftgröße zu klein ist.", + Konstanten.PROGRAMMNAME, JOptionPane.WARNING_MESSAGE); + } + } } /** * @param args the command line arguments */ public static void main(final String... args) { - setupEnvironmentProperties(); - if (GraphicsEnvironment.isHeadless()) { System.err.println("Diese Version von MediathekView unterstützt keine Kommandozeilenausführung."); System.exit(1); } - CommandLine cmd = new CommandLine(Config.class); - try { - var parseResult = cmd.parseArgs(args); - if (parseResult.isUsageHelpRequested()) { - cmd.usage(System.out); - System.exit(cmd.getCommandSpec().exitCodeOnUsageHelp()); - } + setupEnvironmentProperties(); - Config.setPortableMode(parseResult.hasMatchedPositional(0)); - if (Config.isPortableMode()) { - StandardLocations.INSTANCE.setPortableBaseDirectory(Config.baseFilePath); - } + EventQueue.invokeLater(() -> { + + CommandLine cmd = new CommandLine(Config.class); + try { + var parseResult = cmd.parseArgs(args); + if (parseResult.isUsageHelpRequested()) { + cmd.usage(System.out); + System.exit(cmd.getCommandSpec().exitCodeOnUsageHelp()); + } - setupLogging(); - printPortableModeInfo(); + Config.setPortableMode(parseResult.hasMatchedPositional(0)); + if (Config.isPortableMode()) { + StandardLocations.INSTANCE.setPortableBaseDirectory(Config.baseFilePath); + } - setupDockIcon(); - setupFlatLaf(); + setupLogging(); + printPortableModeInfo(); - if (SystemUtils.IS_OS_WINDOWS) { - if (!VersionHelpers.IsWindows10OrGreater()) - logger.warn("This Operating System configuration is too old and will be unsupported in the next updates."); - } + setupDockIcon(); + setupFlatLaf(); - setupCpuAffinity(); + if (SystemUtils.IS_OS_LINUX) + checkUiScaleSetting(); - initializeJavaFX(); + if (!Config.isDisableJvmParameterChecks()) + checkJVMSettings(); - removeMediaDb(); + if (SystemUtils.IS_OS_WINDOWS) { + if (!VersionHelpers.IsWindows10OrGreater()) + logger.warn("This Operating System configuration is too old and will be unsupported in the next updates."); + } - JFXHiddenApplication.launchApplication(); - checkMemoryRequirements(); + setupCpuAffinity(); - installSingleInstanceHandler(); + Platform.setImplicitExit(false); - printVersionInformation(); + removeMediaDb(); + deleteOldFilmDatabaseFiles(); + deleteOldUserAgentsDatabase(); - printJvmParameters(); - printArguments(args); - } catch (CommandLine.ParameterException ex) { - try (var err = cmd.getErr()) { - err.println(ex.getMessage()); - if (!CommandLine.UnmatchedArgumentException.printSuggestions(ex, err)) { - ex.getCommandLine().usage(err); - } - System.exit(cmd.getCommandSpec().exitCodeOnInvalidInput()); - } - } catch (Exception ex) { - logger.error("Command line parse error:", ex); - System.exit(cmd.getCommandSpec().exitCodeOnExecutionException()); - } + JFXHiddenApplication.launchApplication(); + checkMemoryRequirements(); - printDirectoryPaths(); + installSingleInstanceHandler(); - if (!isDebuggerAttached()) { - splashScreen = Optional.of(new SplashScreen()); - } - else { - logger.warn("Debugger detected -> Splash screen disabled..."); - } - splashScreen.ifPresent(SplashScreen::show); + printVersionInformation(); + + printJvmParameters(); + printArguments(args); + } catch (CommandLine.ParameterException ex) { + try (var err = cmd.getErr()) { + var errStr = ex.getMessage() + "\n\n" + ex.getCommandLine().getUsageMessage(); + JOptionPane.showMessageDialog(null, + errStr, + "Fehlerhafte Kommandozeilenparameter", + JOptionPane.ERROR_MESSAGE); + err.println(ex.getMessage()); + if (!CommandLine.UnmatchedArgumentException.printSuggestions(ex, err)) { + ex.getCommandLine().usage(err); + } + System.exit(cmd.getCommandSpec().exitCodeOnInvalidInput()); + } + } catch (Exception ex) { + logger.error("Command line parse error:", ex); + System.exit(cmd.getCommandSpec().exitCodeOnExecutionException()); + } - migrateOldConfigSettings(); + printDirectoryPaths(); - //register FontAwesome font here as it may already be used at first start dialog.. - IconFontSwing.register(FontAwesome.getIconFont()); + if (!isDebuggerAttached()) { + if (!Config.isSplashScreenDisabled()) { + splashScreen = Optional.of(new SplashScreen()); + } + else { + logger.warn("Splash screen disabled..."); + } + } + else { + logger.warn("Debugger detected -> Splash screen disabled..."); + } + splashScreen.ifPresent(splash -> splash.setVisible(true)); - loadConfigurationData(); + migrateOldConfigSettings(); - migrateSeenHistory(); - Daten.getInstance().launchHistoryDataLoading(); - - Daten.getInstance().loadBookMarkData(); + loadConfigurationData(); - if (SystemUtils.IS_OS_LINUX) - changeGlobalFontSize(); + migrateSeenHistory(); + Daten.getInstance().launchHistoryDataLoading(); + Daten.getInstance().loadBookMarkData(); + // enable modern search on demand + var useModernSearch = ApplicationConfiguration.getConfiguration() + .getBoolean(ApplicationConfiguration.APPLICATION_USE_MODERN_SEARCH, false); + if (useModernSearch) + Daten.getInstance().setListeFilmeNachBlackList(new IndexedFilmList()); - startGuiMode(); + startGuiMode(); + }); } /** * Checks if the application has an debugger attached to it. + * * @return true if debugger was detected, false othewise. */ private static boolean isDebuggerAttached() { return ManagementFactory.getRuntimeMXBean().getInputArguments().toString().indexOf("-agentlib:jdwp") > 0; } - private static void changeGlobalFontSize() { - try { - var size = ApplicationConfiguration.getConfiguration().getFloat(ApplicationConfiguration.APPLICATION_UI_FONT_SIZE); - logger.info("Custom font size found, changing global UI settings"); - SwingUIFontChanger fc = new SwingUIFontChanger(); - fc.changeFontSize(size); - } - catch (Exception e) { - logger.info("No custom font size found."); - } - } - /** * Migrate the old text file history to new database format */ @@ -412,30 +487,19 @@ private static void migrateSeenHistory() { if (migrator.needsMigration()) { migrator.migrate(); } - } - catch (Exception e) { + } catch (Exception e) { logger.error("migrateSeenHistory", e); splashScreen.ifPresent(SplashScreen::close); - FXErrorDialog.showErrorDialogWithoutParent(Konstanten.PROGRAMMNAME, - "Migration fehlgeschlagen", - """ - Bei der Migration der Historie der Filme ist ein Fehler aufgetreten. - Das Programm kann nicht fortfahren und wird beendet. - - Bitte überprüfen Sie die Fehlermeldung und suchen Sie Hilfe im Forum. - """, e); + SwingErrorDialog.showExceptionMessage(null, + """ + Bei der Migration der Historie der Filme ist ein Fehler aufgetreten.
+ Das Programm kann nicht fortfahren und wird beendet.

+ Bitte überprüfen Sie die Fehlermeldung und suchen Sie Hilfe im Forum. + """, e); System.exit(99); } } - @SuppressWarnings("unused") - private static void initializeJavaFX() { - //JavaFX stuff - Platform.setImplicitExit(false); - //necessary to init JavaFX before loading config data - var dummy = new JFXPanel(); - } - private static void loadConfigurationData() { var daten = Daten.getInstance(); @@ -445,8 +509,7 @@ private static void loadConfigurationData() { Main.splashScreen.ifPresent(SplashScreen::close); var dialog = new DialogStarteinstellungen(null); - if (dialog.showDialog() == DialogStarteinstellungen.ResultCode.CANCELLED) - { + if (dialog.showDialog() == DialogStarteinstellungen.ResultCode.CANCELLED) { //show termination dialog JOptionPane.showMessageDialog(null, "Sie haben die Einrichtung des Programms abgebrochen.
" + @@ -460,14 +523,45 @@ private static void loadConfigurationData() { } } - private static void deleteSettingsDirectory() { - try (var walk = Files.walk(StandardLocations.getSettingsDirectory())) { + private static void deleteOldUserAgentsDatabase() { + try { + var settingsPath = StandardLocations.getSettingsDirectory(); + var agentDb = settingsPath.resolve("user_agents.mv.db"); + Files.deleteIfExists(agentDb); + } + catch (IOException e) { + logger.error("Error deleting old user agent database occured", e); + } + } + + private static void deleteOldFilmDatabaseFiles() { + var settingsPath = StandardLocations.getSettingsDirectory(); + var dbFolder = settingsPath.resolve("database"); + var traceFile = settingsPath.resolve("databasemediathekview.trace.db"); + + if (!Files.exists(dbFolder)) + return; + + try (var walk = Files.walk(dbFolder)) { walk.sorted(Comparator.reverseOrder()) .map(Path::toFile) //.peek(System.out::println) .forEach(File::delete); + + Files.deleteIfExists(traceFile); } catch (Exception ex) { + logger.error("Got an error deleting old database directory", ex); + } + } + @SuppressWarnings("ResultOfMethodCallIgnored") + private static void deleteSettingsDirectory() { + try (var walk = Files.walk(StandardLocations.getSettingsDirectory())) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + //.peek(System.out::println) + .forEach(File::delete); + } catch (Exception ex) { logger.error("Got an error deleting settings directory", ex); } } @@ -513,56 +607,58 @@ private static void checkMemoryRequirements() { } private static void startGuiMode() { - SwingUtilities.invokeLater(() -> - { - splashScreen.ifPresent(s -> s.update(UIProgressState.INIT_FX)); - - splashScreen.ifPresent(s -> s.update(UIProgressState.FILE_CLEANUP)); - if (SystemUtils.IS_OS_MAC_OSX) { - checkForOfficialOSXAppUse(); - System.setProperty(MAC_SYSTEM_PROPERTY_APPLE_LAF_USE_SCREEN_MENU_BAR, Boolean.TRUE.toString()); - cleanupOsxFiles(); - } + splashScreen.ifPresent(s -> s.update(UIProgressState.INIT_FX)); - if (Config.isDebugModeEnabled() || Config.isInstallThreadCheckingRepaintManager()) { - // use for debugging EDT violations - RepaintManager.setCurrentManager(new ThreadCheckingRepaintManager()); - logger.info("Swing Thread checking repaint manager installed."); - } + splashScreen.ifPresent(s -> s.update(UIProgressState.FILE_CLEANUP)); + if (SystemUtils.IS_OS_MAC_OSX) { + checkForOfficialOSXAppUse(); + System.setProperty(MAC_SYSTEM_PROPERTY_APPLE_LAF_USE_SCREEN_MENU_BAR, Boolean.TRUE.toString()); + cleanupOsxFiles(); + } - splashScreen.ifPresent(s -> s.update(UIProgressState.START_UI)); - var window = getPlatformWindow(); - splashScreen.ifPresent(SplashScreen::close); - window.setVisible(true); + if (Config.isDebugModeEnabled() || Config.isInstallThreadCheckingRepaintManager()) { + // use for debugging EDT violations + RepaintManager.setCurrentManager(new ThreadCheckingRepaintManager()); + logger.info("Swing Thread checking repaint manager installed."); + } + + splashScreen.ifPresent(s -> s.update(UIProgressState.START_UI)); + var window = getPlatformWindow(); + splashScreen.ifPresent(SplashScreen::close); + window.setVisible(true); /* - on windows there is a strange behaviour that the main window gets sent behind + on windows and linux there is a strange behaviour that the main window gets sent behind other open windows after the splash screen is closed. */ - if (SystemUtils.IS_OS_WINDOWS) { - window.toFront(); - window.requestFocus(); - } - //show a link to tutorial if we are in Austria and have never used MV before... - AustrianVlcCheck vlcCheck = new AustrianVlcCheck(); - vlcCheck.perform(); - }); + if (!SystemUtils.IS_OS_MAC_OSX) { + window.toFront(); + window.requestFocus(); + } + //show a link to tutorial if we are in Austria and have never used MV before... + AustrianVlcCheck vlcCheck = new AustrianVlcCheck(); + vlcCheck.perform(); } private static MediathekGui getPlatformWindow() { - MediathekGui window; - Stopwatch watch = Stopwatch.createStarted(); + MediathekGui window = null; if (SystemUtils.IS_OS_MAC_OSX) { window = new MediathekGuiMac(); } else if (SystemUtils.IS_OS_WINDOWS) { window = new MediathekGuiWindows(); - } else if (SystemUtils.IS_OS_UNIX) { + } else if (SystemUtils.IS_OS_LINUX) { window = new MediathekGuiX11(); - } else - throw new IllegalStateException("Unknown operating system detected! Cannot create main window"); - - watch.stop(); - logger.trace("getPlatformWindow(): {}", watch); + } else { + JOptionPane.showMessageDialog(null, + """ + Sie führen MediathekView auf einem nicht unterstützten Betriebssystem aus. + Es werden nur macOS, Windows und Linux unterstützt. + + Das Programm wird beendet, da die Funktionsfähigkeit nicht gewährleistet werden kann.""", + Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE); + System.exit(2); + } return window; } diff --git a/src/main/java/mediathek/SplashScreen.java b/src/main/java/mediathek/SplashScreen.java deleted file mode 100644 index 1927bf6895..0000000000 --- a/src/main/java/mediathek/SplashScreen.java +++ /dev/null @@ -1,103 +0,0 @@ -package mediathek; - -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressBar; -import javafx.scene.image.Image; -import javafx.stage.Stage; -import javafx.stage.StageStyle; -import mediathek.config.Konstanten; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.tool.TimerPool; -import mediathek.tool.UIProgressState; -import org.apache.commons.lang3.SystemUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.IOException; -import java.net.URL; -import java.util.EnumSet; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -public class SplashScreen { - private static final Logger LOG = LogManager.getLogger(SplashScreen.class); - private static final double MAXIMUM_STEPS = EnumSet.allOf(UIProgressState.class).size() - 1d; - - @FXML - private Label appName; - - @FXML - private Label appVersion; - - @FXML - private Label progressText; - - @FXML - private ProgressBar progressBar; - - private double curSteps; - private Stage window; - - /** - * Return "modern" macOS string for mac instead of legacy "Mac OS X". - * According to apple dev docs even "old" 10.6 is now named macOS. - * @return "macOS" for mac otherwise the java OS name - */ - private String getOsName() { - final String osName; - if (SystemUtils.IS_OS_MAC_OSX) - osName = "macOS"; - else - osName = SystemUtils.OS_NAME; - - return osName; - } - - public void show() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - window = new Stage(StageStyle.UNDECORATED); - window.getIcons().add(new Image("/mediathek/res/MediathekView.png")); - - URL url = getClass().getResource("/mediathek/res/programm/fxml/splashscreen.fxml"); - - FXMLLoader fxmlLoader = new FXMLLoader(); - fxmlLoader.setLocation(url); - fxmlLoader.setController(this); - - try { - Scene scene = new Scene(fxmlLoader.load()); - window.setScene(scene); - - appName.setText(Konstanten.PROGRAMMNAME); - appVersion.setText("Version: " + Konstanten.MVVERSION + " (" + getOsName() + ")"); - progressBar.prefWidthProperty().bind(scene.widthProperty()); - - window.setScene(scene); - window.show(); - window.centerOnScreen(); - } catch (IOException ioException) { - LOG.error("Can't find/load the splash screen FXML description!", ioException); - } - }); - } - - public void close() { - TimerPool.getTimerPool().schedule(() -> JavaFxUtils.invokeInFxThreadAndWait(() -> { - window.close(); - Main.splashScreen = Optional.empty(); // delete reference as we are not working anymore - }),2, TimeUnit.SECONDS); - } - - public void update(UIProgressState state) { - Platform.runLater(() -> { - curSteps++; - final double p = (curSteps / MAXIMUM_STEPS); - progressBar.setProgress(p); - progressText.setText(state.toString()); - }); - } -} diff --git a/src/main/java/mediathek/config/ApplicationType.kt b/src/main/java/mediathek/config/ApplicationType.kt new file mode 100644 index 0000000000..f3a4da5b4c --- /dev/null +++ b/src/main/java/mediathek/config/ApplicationType.kt @@ -0,0 +1,6 @@ +package mediathek.config + +enum class ApplicationType { + NIGHTLY, + PRODUCTION +} diff --git a/src/main/java/mediathek/config/Config.java b/src/main/java/mediathek/config/Config.java index bd9250c23f..b34b55529a 100644 --- a/src/main/java/mediathek/config/Config.java +++ b/src/main/java/mediathek/config/Config.java @@ -52,10 +52,25 @@ public class Config { private static boolean portableMode; @CommandLine.Option(names = {"-m", "--maximized"}, description = "Programmfenster beim Start maximieren") private static boolean startMaximized; // Fenster maximieren + @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "Hilfe anzeigen") private static boolean helpRequested; @CommandLine.Option(names = {"-f", "--disable-file-logging"}, description = "Speichern des Log output in Datei deaktivieren") private static boolean fileLoggingDisabled; + /** + * Disable JVM parameter checks on startup. + */ + @CommandLine.Option(names = {"-nj", "--no-jvm-param-checks"}, description = "JVM Parameter-Prüfung deaktivieren") + private static boolean disableJvmParameterChecks; + + @CommandLine.Option(names = {"-ns", "--no-splash"}, description = "Splash-Screen nicht anzeigen") + private static boolean disableSplashScreen; + + public static boolean isSplashScreenDisabled() { return disableSplashScreen;} + + public static boolean isDisableJvmParameterChecks() { + return disableJvmParameterChecks; + } public static boolean isInstallThreadCheckingRepaintManager() { return installThreadCheckingRepaintManager; diff --git a/src/main/java/mediathek/config/Daten.java b/src/main/java/mediathek/config/Daten.java index 7ae696b3f8..aa22b4d59f 100644 --- a/src/main/java/mediathek/config/Daten.java +++ b/src/main/java/mediathek/config/Daten.java @@ -1,9 +1,6 @@ package mediathek.config; import com.google.common.util.concurrent.*; -import javafx.scene.control.Alert; -import javafx.scene.control.ButtonBar; -import javafx.scene.control.ButtonType; import mediathek.Main; import mediathek.SplashScreen; import mediathek.controller.IoXmlLesen; @@ -14,9 +11,6 @@ import mediathek.daten.blacklist.ListeBlacklist; import mediathek.filmlisten.FilmeLaden; import mediathek.javafx.bookmark.BookmarkDataList; -import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.mainwindow.MediathekGui; import mediathek.tool.ReplaceList; import mediathek.tool.notification.INotificationCenter; import org.apache.logging.log4j.LogManager; @@ -24,6 +18,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.NotNull; +import javax.swing.*; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -36,7 +31,6 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicBoolean; @@ -55,18 +49,20 @@ public class Daten { /** * "source" list of all entries, contains everything */ - private final ListeFilme listeFilme; - /** - * "the" final list of films after all filtering is done - */ - private final ListeFilme listeFilmeNachBlackList; + private final ListeFilme listeFilme = new ListeFilme(); private final ListeDownloads listeDownloads; // Filme die als "Download: Tab Download" geladen werden sollen private final ListeDownloads listeDownloadsButton; // Filme die über "Tab Filme" als Button/Film abspielen gestartet werden - private final ListeBlacklist listeBlacklist; + private final ListeBlacklist listeBlacklist = new ListeBlacklist(); private final BookmarkDataList listeBookmarkList; private final ListeAbo listeAbo; - private final DownloadInfos downloadInfos; + private final DownloadInfos downloadInfos = new DownloadInfos(); private final StarterClass starterClass; // Klasse zum Ausführen der Programme (für die Downloads): VLC, flvstreamer, ... + private final ListeningExecutorService decoratedPool = MoreExecutors.listeningDecorator(ForkJoinPool.commonPool()); + /** + * "the" final list of films after all filtering is done. + * Defaults to no lucene index unless changed at startup. + */ + private ListeFilme listeFilmeNachBlackList = new ListeFilme(); private INotificationCenter notificationCenter; /** * erfolgreich geladene Abos. @@ -74,13 +70,9 @@ public class Daten { private AboHistoryController erledigteAbos; private boolean alreadyMadeBackup; private ListenableFuture aboHistoryFuture; - private Daten() { - listeFilme = new ListeFilme(); filmeLaden = new FilmeLaden(this); - listeFilmeNachBlackList = new ListeFilme(); - listeBlacklist = new ListeBlacklist(); listeBookmarkList = BookmarkDataList.getInstance(this); listePset = new ListePset(); @@ -90,7 +82,6 @@ private Daten() { listeDownloads = new ListeDownloads(this); listeDownloadsButton = new ListeDownloads(this); - downloadInfos = new DownloadInfos(); starterClass = new StarterClass(this); } @@ -129,15 +120,6 @@ private static List getMediathekXmlCopyFilePath() { return xmlFilePath; } - /** - * Return the path to "bookmarks.json" - * - * @return Path object of bookmark file - */ - public static Path getBookmarkFilePath() { - return StandardLocations.getSettingsDirectory().resolve(Konstanten.BOOKMARK_FILE); - } - public StarterClass getStarterClass() { return starterClass; } @@ -147,9 +129,9 @@ public StarterClass getStarterClass() { * into memory */ public void loadBookMarkData() { - listeBookmarkList.loadFromFile(getBookmarkFilePath()); + listeBookmarkList.loadFromFile(StandardLocations.getBookmarkFilePath()); } - + /** * Return the number of milliseconds from today´s midnight. * @@ -161,7 +143,7 @@ private long getHeute_0Uhr() { return zdt.toInstant().toEpochMilli(); } - + public INotificationCenter notificationCenter() { return notificationCenter; } @@ -195,8 +177,6 @@ public ListeningExecutorService getDecoratedPool() { return decoratedPool; } - private final ListeningExecutorService decoratedPool = MoreExecutors.listeningDecorator(ForkJoinPool.commonPool()); - public void launchHistoryDataLoading() { logger.trace("launching async history data loading"); aboHistoryFuture = launchAboHistoryController(decoratedPool); @@ -257,6 +237,21 @@ private boolean load() { return ret; } + private boolean askForBackupRestore() { + var text = """ + Die Einstellungen sind beschädigt und können nicht geladen werden. + Soll versucht werden diese aus einem Backup wiederherzustellen? + """; + int answer = JOptionPane.showConfirmDialog(null, text, + Konstanten.PROGRAMMNAME, JOptionPane.YES_NO_OPTION); + if (answer == JOptionPane.YES_OPTION) + return true; + else { + logger.info("User will kein Backup laden."); + return false; + } + } + private boolean loadBackup() { boolean ret = false; @@ -270,27 +265,7 @@ private boolean loadBackup() { // dann gibts ein Backup logger.info("Es gibt ein Backup"); - var loadBackup = JavaFxUtils.invokeInFxThreadAndWait(() -> { - ButtonType btnYes = new ButtonType("Ja", ButtonBar.ButtonData.OK_DONE); - ButtonType btnNo = new ButtonType("Nein", ButtonBar.ButtonData.CANCEL_CLOSE); - Alert alert = new Alert(Alert.AlertType.WARNING, - "Die Einstellungen sind beschädigt und können nicht geladen werden. " - + "Soll versucht werden diese aus einem Backup wiederherzustellen?", - btnYes, - btnNo); - - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText("Gesicherte Einstellungen laden"); - Optional result = alert.showAndWait(); - if (result.orElse(btnNo) == btnNo) { - logger.info("User will kein Backup laden."); - return false; - } else - return true; - } - ); - - if (loadBackup) { + if (askForBackupRestore()) { for (Path p : path) { // teils geladene Reste entfernen clearKonfig(); @@ -330,18 +305,13 @@ public void allesSpeichern() { Files.deleteIfExists(path1); } catch (IOException e) { logger.error("Die Einstellungen konnten nicht zurückgesetzt werden.", e); - if (MediathekGui.ui() != null) { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setHeaderText("Fehler beim Zurücksetzen der Einstellungen"); - alert.setContentText("Die Einstellungen konnten nicht zurückgesetzt werden.\n" - + "Sie müssen jetzt das Programm beenden und dann den Ordner:\n" - + StandardLocations.getSettingsDirectory() + '\n' - + "von Hand löschen und dann das Programm wieder starten.\n\n" - + "Im Forum erhalten Sie weitere Hilfe."); - JFXHiddenApplication.showAlert(alert, MediathekGui.ui()); - }); - } + var msg = "Die Einstellungen konnten nicht zurückgesetzt werden.\n" + + "Sie müssen jetzt das Programm beenden und dann den Ordner:\n" + + StandardLocations.getSettingsDirectory() + '\n' + + "von Hand löschen und dann das Programm wieder starten.\n\n" + + "Im Forum erhalten Sie weitere Hilfe."; + JOptionPane.showMessageDialog(null, Konstanten.PROGRAMMNAME, + msg, JOptionPane.ERROR_MESSAGE); } } } @@ -403,6 +373,10 @@ public ListeFilme getListeFilmeNachBlackList() { return listeFilmeNachBlackList; } + public void setListeFilmeNachBlackList(ListeFilme listeFilmeNachBlackList) { + this.listeFilmeNachBlackList = listeFilmeNachBlackList; + } + public ListeDownloads getListeDownloads() { return listeDownloads; } @@ -418,7 +392,7 @@ public ListeBlacklist getListeBlacklist() { public BookmarkDataList getListeBookmarkList() { return listeBookmarkList; } - + public ListeAbo getListeAbo() { return listeAbo; } diff --git a/src/main/java/mediathek/config/Icons.java b/src/main/java/mediathek/config/Icons.java index 078756ac04..f8ea356c16 100644 --- a/src/main/java/mediathek/config/Icons.java +++ b/src/main/java/mediathek/config/Icons.java @@ -25,19 +25,6 @@ import java.awt.*; public class Icons { - public static final ImageIcon ICON_DIALOG_EIN_SW = GetIcon.getProgramIcon("dialog-ein-sw.png", 16, 16); - - public static final ImageIcon ICON_MENUE_VORZIEHEN = GetIcon.getProgramIcon("menue-vorziehen.png", 16, 16); - - public static final ImageIcon ICON_TABELLE_EIN = GetIcon.getProgramIcon("tabelle-ein.png", 16, 16); - public static final ImageIcon ICON_TABELLE_AUS = GetIcon.getProgramIcon("tabelle-aus.png", 5, 5); - - public static final ImageIcon ICON_BUTTON_REMOVE = GetIcon.getProgramIcon("button-remove.png", 16, 16); - public static final ImageIcon ICON_BUTTON_ADD = GetIcon.getProgramIcon("button-add.png", 16, 16); - public static final ImageIcon ICON_BUTTON_MOVE_DOWN = GetIcon.getProgramIcon("button-move-down.png", 16, 16); - public static final ImageIcon ICON_BUTTON_MOVE_UP = GetIcon.getProgramIcon("button-move-up.png", 16, 16); - public static final ImageIcon ICON_BUTTON_CLEAR = GetIcon.getProgramIcon("button-clear.png", 16, 16); - // Icons TABBED_PANE public static final ImageIcon ICON_TAB_FILM = GetIcon.getProgramIcon("tab-film.png", 32, 32); public static final ImageIcon ICON_TAB_DOWNLOAD = GetIcon.getProgramIcon("tab-download.png", 32, 32); diff --git a/src/main/java/mediathek/config/Konstanten.java b/src/main/java/mediathek/config/Konstanten.java index 5d4bf84dc6..906f0a64f4 100644 --- a/src/main/java/mediathek/config/Konstanten.java +++ b/src/main/java/mediathek/config/Konstanten.java @@ -23,19 +23,14 @@ import mediathek.tool.Version; import okhttp3.HttpUrl; -import java.net.URL; import java.util.concurrent.TimeUnit; public class Konstanten { public static final long MINIMUM_MEMORY_THRESHOLD = 768 * FileUtils.ONE_MB; - public static final Version MVVERSION = new Version(13,9,1); - /** - * Is this a nightly or a production build? - */ - public static final boolean APP_IS_NIGHTLY = false; - public static final String EXTERNAL_UPDATE_PROPERTY = "externalUpdateCheck"; + public static final Version MVVERSION = new Version(14,0,0); + + public static final ApplicationType APPLICATION_TYPE = ApplicationType.PRODUCTION; public static final String MACOS_OFFICIAL_APP = "OSX_OFFICIAL_APP"; - public static final URL FXML_FILM_DESCRIPTION_PANEL_URL = Konstanten.class.getResource("/mediathek/res/programm/fxml/filmdescription.fxml"); public static final String FORMAT_ZIP = ".zip"; public static final String FORMAT_XZ = ".xz"; @@ -56,6 +51,7 @@ public class Konstanten { public static final byte DOWNLOAD_ERROR_DISPLAY_DURATION = 60; // MediathekView URLs + public static final HttpUrl LUCENE_CLIENT_HELP_URL = HttpUrl.get("https://github.com/mediathekview/MediathekView/blob/develop/lucene_help.md"); public static final HttpUrl ROUTER_BASE_URL = HttpUrl.get("https://liste.mediathekview.de"); public static final HttpUrl WEBSITE_BASE_URL = HttpUrl.get("https://mediathekview.de"); public static final HttpUrl URL_MEDIATHEKVIEW_RESOURCES = HttpUrl.get("https://res.mediathekview.de"); @@ -83,7 +79,6 @@ public class Konstanten { public static final int MAX_SENDER_FILME_LADEN = 2; //es können maximal soviele Filme eines Senders/Servers gleichzeitig geladen werden public static final int MAX_PFADE_DIALOG_DOWNLOAD = 15; - public static final int GUIDOWNLOAD_DIVIDER_LOCATION = 200; public static final int LAENGE_DATEINAME = 25; // Standardwert für die Länge des Zieldateinamens public static final int LAENGE_FELD = 10; // Standardwert für die Länge des Feldes des Zieldateinamens zB. %T diff --git a/src/main/java/mediathek/config/MVColor.java b/src/main/java/mediathek/config/MVColor.java index b608db0a35..822c3e8dff 100644 --- a/src/main/java/mediathek/config/MVColor.java +++ b/src/main/java/mediathek/config/MVColor.java @@ -19,25 +19,23 @@ */ package mediathek.config; +import com.formdev.flatlaf.FlatLaf; import mediathek.tool.MVC; +import org.jetbrains.annotations.NotNull; +import javax.swing.*; import java.awt.*; import java.util.ArrayList; public class MVColor { // Tabelle Filme - public static final MVC FILM_LIVESTREAM = new MVC(MVConfig.Configs.FARBE__FILM_LIVESTREAM, new Color(130, 0, 0), "Filme, Livestreams"); public static final MVC FILM_HISTORY = new MVC(MVConfig.Configs.FARBE__FILM_HISTORY, new Color(225, 225, 225), "Filme, gesehen"); - public static final MVC FILM_NEU = new MVC(MVConfig.Configs.FARBE__FILM_NEU, new Color(0, 0, 240), "Filme, neue"); public static final MVC FILM_BOOKMARKED = new MVC(MVConfig.Configs.FARBE__FILM_BOOKMARKED, new Color(204, 238, 255), "Filme, gemerkt"); - public static final MVC FILM_GEOBLOCK_BACKGROUND = new MVC(MVConfig.Configs.FARBE__FILM_GEOBLOCK_BACKGROUND, new Color(255, 254, 230), "Film, geogeblockt"); - public static final MVC FILM_GEOBLOCK_BACKGROUND_SEL = new MVC(MVConfig.Configs.FARBE__FILM_GEOBLOCK_BACKGROUND_SEL, new Color(255, 251, 179), "Film, geogeblockt, selektiert"); // Tabelle Downloads public static final MVC DOWNLOAD_IST_ABO = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_IST_ABO, new Color(138, 67, 0), "Download ist ein Abo"); public static final MVC DOWNLOAD_IST_DIREKTER_DOWNLOAD = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_IST_DIREKTER_DOWNLOAD, new Color(0, 72, 138), "Download ist ein direkter Download"); - public static final MVC DOWNLOAD_ANSEHEN = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_ANSEHEN, new Color(0, 125, 0), "Download kann schon angesehen werden"); // status Downloads public static final MVC DOWNLOAD_WAIT = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_WAIT, new Color(239, 244, 255), "Download, noch nicht gestartet"); public static final MVC DOWNLOAD_WAIT_SEL = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_WAIT_SEL, new Color(199, 206, 222), "Download, noch nicht gestartet, selektiert"); @@ -48,33 +46,21 @@ public class MVColor { public static final MVC DOWNLOAD_FEHLER = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_FEHLER, new Color(241, 188, 221), "Download, fehlerhaft"); public static final MVC DOWNLOAD_FEHLER_SEL = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_FEHLER_SEL, new Color(206, 92, 128), "Download, fehlerhaft, selektiert"); - // Filter wenn RegEx - public static final MVC FILTER_REGEX = new MVC(MVConfig.Configs.FARBE__FILTER_REGEX, new Color(153, 214, 255), "Filter ist RegEx"); - public static final MVC FILTER_REGEX_FEHLER = new MVC(MVConfig.Configs.FARBE__FILTER_REGEX_FEHLER, Color.RED, "Filter ist Regex, fehlerhaft"); - // ProgrammGui - public static final MVC BUTTON_SET_ABSPIELEN = new MVC(MVConfig.Configs.FARBE__BUTTON_SET_ABSPIELEN, new Color(205, 255, 191), "Einstellungen Sets, Button Abspielen"); - // DialogDownload - public static final MVC DOWNLOAD_DATEINAME_EXISTIERT = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_DATEINAME_EXISTIERT, new Color(190, 0, 0), "Download, Dateiname existiert schon"); public static final MVC DOWNLOAD_DATEINAME_NEU = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_DATEINAME_NEU, new Color(0, 140, 0), "Download, Dateiname ist neu"); public static final MVC DOWNLOAD_DATEINAME_ALT = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_DATEINAME_ALT, new Color(0, 0, 200), "Download, Dateiname ist der alte"); - - public ArrayList liste = new ArrayList<>(); public static final int MVC_TEXT = 0; public static final int MVC_COLOR = 1; public static final int MVC_MAX = 2; + private static final Color JTABLE_ALTERNATE_ROW_COLOR = new Color(247, 247, 247); + public ArrayList liste = new ArrayList<>(); public MVColor() { - liste.add(FILM_LIVESTREAM); liste.add(FILM_HISTORY); - liste.add(FILM_NEU); liste.add(FILM_BOOKMARKED); - liste.add(FILM_GEOBLOCK_BACKGROUND); - liste.add(FILM_GEOBLOCK_BACKGROUND_SEL); liste.add(DOWNLOAD_IST_ABO); liste.add(DOWNLOAD_IST_DIREKTER_DOWNLOAD); - liste.add(DOWNLOAD_ANSEHEN); liste.add(DOWNLOAD_WAIT); liste.add(DOWNLOAD_WAIT_SEL); liste.add(DOWNLOAD_RUN); @@ -83,14 +69,66 @@ public MVColor() { liste.add(DOWNLOAD_FERTIG_SEL); liste.add(DOWNLOAD_FEHLER); liste.add(DOWNLOAD_FEHLER_SEL); - liste.add(FILTER_REGEX); - liste.add(FILTER_REGEX_FEHLER); - liste.add(BUTTON_SET_ABSPIELEN); - liste.add(DOWNLOAD_DATEINAME_EXISTIERT); liste.add(DOWNLOAD_DATEINAME_NEU); liste.add(DOWNLOAD_DATEINAME_ALT); } + /** + * Get the pattern text color based on L&F dark mode. + * + * @return adjusted color for current L&F + */ + public static Color getRegExPatternColor() { + Color color; + if (FlatLaf.isLafDark()) { + color = UIManager.getColor("Hyperlink.linkColor"); + } else + color = Color.BLUE; + + return color; + } + + public static Color getNewColor() { + return getRegExPatternColor(); + } + + public static Color getBlueColor() { + return getRegExPatternColor(); + } + + public static Color getSelectedColor() { + return getRegExPatternColor(); + } + + /** + * Return the alternating row color based on L&F setting. + * + * @return alternating color for dark or light L&Fs. + */ + public static Color getAlternatingRowColor() { + Color color; + + if (!FlatLaf.isLafDark()) { + return JTABLE_ALTERNATE_ROW_COLOR; + } else { + var tableBg = UIManager.getColor("Table.background"); + color = brightenColor(tableBg, 0.25f); + } + return color; + } + + /** + * Calculate a brighter color by factor based on HSB values. + * + * @param originalColor the original color. + * @param factor the factor to brighten. + * @return the new brighter color. + */ + private static @NotNull Color brightenColor(@NotNull Color originalColor, float factor) { + float[] hsb = Color.RGBtoHSB(originalColor.getRed(), originalColor.getGreen(), originalColor.getBlue(), null); + return Color.getHSBColor(hsb[0], hsb[1], factor * (1f + hsb[2])); + } + public final void load() { liste.stream().filter(mvc -> !MVConfig.get(mvc.configs).isEmpty()).forEach(mvc -> { try { diff --git a/src/main/java/mediathek/config/StandardLocations.kt b/src/main/java/mediathek/config/StandardLocations.kt index cda2f5eddc..40cd374ce6 100644 --- a/src/main/java/mediathek/config/StandardLocations.kt +++ b/src/main/java/mediathek/config/StandardLocations.kt @@ -9,6 +9,7 @@ import java.nio.file.Files import java.nio.file.InvalidPathException import java.nio.file.Path import java.nio.file.Paths +import java.util.Optional object StandardLocations { /** @@ -47,6 +48,16 @@ object StandardLocations { return baseDirectoryPath } + /** + * Return the path to "bookmarks.json" + * + * @return Path object of bookmark file + */ + @JvmStatic + fun getBookmarkFilePath(): Path { + return getSettingsDirectory().resolve(Konstanten.BOOKMARK_FILE) + } + /** * Return the path to "mediathek.xml" * @@ -58,6 +69,21 @@ object StandardLocations { return getSettingsDirectory().resolve(Konstanten.CONFIG_FILE) } + @JvmStatic + @Throws(InvalidPathException::class) + fun getXDGDownloadDirectory(): Optional { + return try { + val process = ProcessBuilder("xdg-user-dir", "DOWNLOAD") + .directory(File(SystemUtils.USER_HOME)) + .redirectOutput(ProcessBuilder.Redirect.PIPE) + .start() + val line = process.inputReader().use { reader -> reader.readLine() } + Optional.of(line).filter { s -> s.isNotEmpty() }.map { s -> Paths.get(s) } + } catch (_: IOException) { + Optional.empty() + } + } + /** * Return the standard path to downloads. * @@ -69,6 +95,8 @@ object StandardLocations { val userHome = SystemUtils.USER_HOME val path = if (SystemUtils.IS_OS_MAC_OSX) Paths.get(userHome, "Downloads") + else if (SystemUtils.IS_OS_LINUX) + getXDGDownloadDirectory().orElse(Paths.get(userHome, Konstanten.VERZEICHNIS_DOWNLOADS)) else Paths.get(userHome, Konstanten.VERZEICHNIS_DOWNLOADS) return path.toAbsolutePath().toString() @@ -97,7 +125,7 @@ object StandardLocations { * @return the path as String. */ @JvmStatic - fun getFilmlistFilePath(): String { + fun getFilmlistFilePathString(): String { val filePart = File.separator + Konstanten.JSON_DATEI_FILME return if (Config.isPortableMode()) getSettingsDirectory().toString() + filePart @@ -111,6 +139,24 @@ object StandardLocations { } } + /** + * Return the location of the lucene film index. + */ + @JvmStatic + fun getFilmIndexPath(): Path { + val indexDirectory = "mv_index" + + return if (Config.isPortableMode()) + getSettingsDirectory().resolve(indexDirectory) + else { + if (SystemUtils.IS_OS_MAC_OSX) { + val base = Paths.get(SystemUtils.USER_HOME + File.separator + OSX_CACHE_DIRECTORY_NAME) + base.resolve(indexDirectory) + } else { + getSettingsDirectory().resolve(indexDirectory) + } + } + } /** * Return the path to the lockfile. * On macOS we do not support roaming settings with the official app, therefore keep the old temp dir convention. diff --git a/src/main/java/mediathek/controller/IoXmlLesen.java b/src/main/java/mediathek/controller/IoXmlLesen.java index 316c5baa71..da62ebf8f5 100644 --- a/src/main/java/mediathek/controller/IoXmlLesen.java +++ b/src/main/java/mediathek/controller/IoXmlLesen.java @@ -133,7 +133,7 @@ private void readBlacklistRuleEntry(XMLStreamReader parser) { private void readDownloadEntry(XMLStreamReader parser) { try { - var dl = DatenDownload.getFromConfig(parser); + var dl = DatenDownload.readFromConfig(parser); // abo entries will be generated...but we need this for CLI so far if (!dl.isFromAbo()) daten.getListeDownloads().add(dl); diff --git a/src/main/java/mediathek/controller/history/MVUsedUrl.java b/src/main/java/mediathek/controller/history/MVUsedUrl.java index 2e0f759deb..34510baee1 100644 --- a/src/main/java/mediathek/controller/history/MVUsedUrl.java +++ b/src/main/java/mediathek/controller/history/MVUsedUrl.java @@ -23,6 +23,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.time.DateTimeException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -41,7 +42,16 @@ public class MVUsedUrl { private final String url; public MVUsedUrl(String date, String thema, String title, String url) { - this.datum = LocalDate.parse(date, DATE_TIME_FORMATTER); + LocalDate tempDate; + try { + tempDate = LocalDate.parse(date, DATE_TIME_FORMATTER); + } + catch (DateTimeException ex) { + logger.error("Failed to parse date: \"{}\" for thema: \"{}\", titel: \"{}\" and URL: \"{}\"", date, thema, title, url); + logger.error("Resetting date to current date..."); + tempDate = LocalDate.now(); + } + this.datum = tempDate; this.thema = thema; this.titel = title; this.url = url; diff --git a/src/main/java/mediathek/controller/starter/DirectHttpDownload.java b/src/main/java/mediathek/controller/starter/DirectHttpDownload.java index 009768c58d..e98c892d45 100644 --- a/src/main/java/mediathek/controller/starter/DirectHttpDownload.java +++ b/src/main/java/mediathek/controller/starter/DirectHttpDownload.java @@ -82,30 +82,38 @@ public DirectHttpDownload(Daten daten, DatenDownload d) { * @param evt the new limit */ @Handler - private void handleRateLimitChanged(DownloadRateLimitChangedEvent evt) { - final long limit = calculateDownloadLimit(evt.newLimit); + private void handleRateLimitChanged(@NotNull DownloadRateLimitChangedEvent evt) { + final long limit = calculateDownloadLimit(evt); logger.info("thread changing download speed limit to {} KB", limit); rateLimiter.setRate(limit); } - private long calculateDownloadLimit(long limit) { + private long calculateDownloadLimit(@NotNull DownloadRateLimitChangedEvent evt) { + return calcLimit(evt.newLimit, evt.active); + } + + private long calcLimit(long limit, boolean active) { long newLimit; - if (limit <= 0) - newLimit = 10 * FileUtils.ONE_GB; + if (limit <= 0 || !active) + newLimit = Long.MAX_VALUE; else newLimit = limit * FileUtils.ONE_KB; return newLimit; } + private long calculateDownloadLimit(long limit) { + var active = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.DownloadRateLimiter.ACTIVE, false); + return calcLimit(limit, active); + } /** * Try to read the download limit from config file, other set to artificial limit 1GB/s! * * @return the limit in KB/s */ private long getDownloadLimit() { - final long downloadLimit = ApplicationConfiguration.getConfiguration().getLong(ApplicationConfiguration.DOWNLOAD_RATE_LIMIT, 0); + final long downloadLimit = ApplicationConfiguration.getConfiguration().getLong(ApplicationConfiguration.DownloadRateLimiter.LIMIT, 0); return calculateDownloadLimit(downloadLimit); } @@ -222,9 +230,6 @@ private void downloadContent(InputStream inputStream) throws IOException { final var diffZeit = Duration.between(start.startTime, LocalDateTime.now()).toSeconds(); final long restProzent = 1000L - p; start.restSekunden = (diffZeit * restProzent / (p - startProzent)); - // anfangen zum Schauen kann man, wenn die Restzeit kürzer ist - // als die bereits geladene Speilzeit des Films - bereitsAnschauen(datenDownload); } melden = true; } @@ -427,22 +432,6 @@ private boolean abbrechen_() { return result; } - private void bereitsAnschauen(DatenDownload datenDownload) { - if (datenDownload.film != null && datenDownload.start != null) { - final long filmLength = datenDownload.film.getFilmLength(); - if (filmLength > 0 - && datenDownload.start.restSekunden > 0 - && datenDownload.mVFilmSize.getAktSize() > 0 - && datenDownload.mVFilmSize.getSize() > 0) { - // macht nur dann Sinn - final long zeitGeladen = filmLength * datenDownload.mVFilmSize.getAktSize() / datenDownload.mVFilmSize.getSize(); - if (zeitGeladen > (datenDownload.start.restSekunden * 1.1 /* plus 10% zur Sicherheit*/)) { - datenDownload.start.beginnAnschauen = true; - } - } - } - } - enum HttpDownloadState { CANCEL, ERROR, DOWNLOAD diff --git a/src/main/java/mediathek/controller/starter/Start.java b/src/main/java/mediathek/controller/starter/Start.java index 019d92ac2a..abc7833886 100644 --- a/src/main/java/mediathek/controller/starter/Start.java +++ b/src/main/java/mediathek/controller/starter/Start.java @@ -35,7 +35,6 @@ public class Start { public int percent = -1; // Prozent fertiggestellt: -1=nix, 999=99,9% public long bandbreite = -1; // Downloadbandbreite: bytes per second public boolean stoppen = false; - public boolean beginnAnschauen = false; public int countRestarted = 0; public LocalDateTime startTime; diff --git a/src/main/java/mediathek/daten/Country.java b/src/main/java/mediathek/daten/Country.java new file mode 100644 index 0000000000..fa109c9eb3 --- /dev/null +++ b/src/main/java/mediathek/daten/Country.java @@ -0,0 +1,5 @@ +package mediathek.daten; + +public enum Country { + DE, AT, CH, EU, FR, OTHER +} diff --git a/src/main/java/mediathek/daten/DatenDownload.java b/src/main/java/mediathek/daten/DatenDownload.java index 34c4a6edee..41836fa20e 100644 --- a/src/main/java/mediathek/daten/DatenDownload.java +++ b/src/main/java/mediathek/daten/DatenDownload.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.stream.Collectors; public final class DatenDownload implements Comparable { @@ -118,15 +119,15 @@ public DatenDownload(DatenPset pSet, DatenFilm film, byte quelle, DatenAbo abo, this.film = film; this.pSet = pSet; this.abo = abo; - arr[DOWNLOAD_FILM_NR] = Integer.toString(film.getFilmNr()); + arr[DOWNLOAD_FILM_NR] = Long.toString(film.getFilmNr()); arr[DOWNLOAD_SENDER] = film.getSender(); arr[DOWNLOAD_THEMA] = film.getThema(); arr[DOWNLOAD_TITEL] = film.getTitle(); arr[DOWNLOAD_FILM_URL] = film.getUrlNormalQuality(); - arr[DOWNLOAD_URL_SUBTITLE] = film.getUrlSubtitle(); + arr[DOWNLOAD_URL_SUBTITLE] = film.getSubtitleUrl(); arr[DOWNLOAD_DATUM] = film.getSendeDatum(); arr[DOWNLOAD_ZEIT] = film.getSendeZeit(); - arr[DOWNLOAD_DAUER] = film.getDauer(); + arr[DOWNLOAD_DAUER] = film.getFilmLengthAsString(); arr[DOWNLOAD_HD] = film.isHighQuality() ? "1" : "0"; arr[DOWNLOAD_UT] = film.hasSubtitle() ? "1" : "0"; arr[DOWNLOAD_QUELLE] = String.valueOf(quelle); @@ -139,9 +140,13 @@ public DatenDownload(DatenPset pSet, DatenFilm film, byte quelle, DatenAbo abo, arr[DatenDownload.DOWNLOAD_INFODATEI] = pSet.arr[DatenPset.PROGRAMMSET_INFODATEI]; arr[DatenDownload.DOWNLOAD_SUBTITLE] = pSet.arr[DatenPset.PROGRAMMSET_SUBTITLE]; arr[DatenDownload.DOWNLOAD_SPOTLIGHT] = pSet.arr[DatenPset.PROGRAMMSET_SPOTLIGHT]; - arr[DatenDownload.DOWNLOAD_GEO] = film.getGeo().orElse(""); + if (film.countrySet.isEmpty()) + arr[DatenDownload.DOWNLOAD_GEO] = ""; + else { + arr[DatenDownload.DOWNLOAD_GEO] = film.countrySet.stream().map(Country::toString).collect(Collectors.joining("-")); + } - websiteUrl = film.getWebsiteLink(); + websiteUrl = film.getWebsiteUrl(); // und jetzt noch die Dateigröße für die entsp. URL setGroesse(""); @@ -152,11 +157,12 @@ public DatenDownload(DatenPset pSet, DatenFilm film, byte quelle, DatenAbo abo, /** * Read the download data from config. + * * @param parser The parser for the config file * @return A valid DatenDownload object when everything went smooth. * @throws XMLStreamException when something went wrong */ - public static DatenDownload getFromConfig(XMLStreamReader parser) throws XMLStreamException { + public static DatenDownload readFromConfig(@NotNull XMLStreamReader parser) throws XMLStreamException { DatenDownload dl = new DatenDownload(); final int maxElem = dl.arr.length; @@ -197,24 +203,14 @@ public static boolean anzeigen(int i) { } } - public void startDownload() { - // Start erstellen und zur Liste hinzufügen - this.start = new Start(); - - try (var historyController = new SeenHistoryController()){ - historyController.markSeen(film); - } - - MessageBus.getMessageBus().publishAsync(new StartEvent()); - } - /** * Starts all downloads from list but fire only one update event. + * * @param downloads the list of downloads */ public static void startenDownloads(ArrayList downloads) { // Start erstellen und zur Liste hinzufügen - try (var historyController = new SeenHistoryController()){ + try (var historyController = new SeenHistoryController()) { for (DatenDownload d : downloads) { d.start = new Start(); historyController.markSeen(d.film); @@ -239,15 +235,44 @@ public static String getTextBandbreite(long b) { } } - private enum DMYTag { - DAY, - MONTH, - YEAR + private static String datumDrehen(String datum) { + String ret = ""; + if (!datum.isEmpty()) { + try { + if (datum.length() == 10) { + String tmp = datum.substring(6); // Jahr + tmp += '.' + datum.substring(3, 5); // Monat + tmp += '.' + datum.substring(0, 2); // Tag + ret = tmp; + } + } catch (Exception ex) { + logger.error("Datum: {}", datum, ex); + } + } + return ret; + } + + private static String datumDatumZeitReinigen(String datum) { + String ret = StringUtils.replace(datum, ":", ""); + ret = StringUtils.replace(ret, ".", ""); + return ret; + } + + public void startDownload() { + // Start erstellen und zur Liste hinzufügen + this.start = new Start(); + + try (var historyController = new SeenHistoryController()) { + historyController.markSeen(film); + } + + MessageBus.getMessageBus().publishAsync(new StartEvent()); } /** * Return a specific string based on the specified tag. - * @param tag Specifies what to return from the date string (xx.xx.xxxx) + * + * @param tag Specifies what to return from the date string (xx.xx.xxxx) * @param datum a date string * @return Return the string part specified by tag */ @@ -269,15 +294,10 @@ private String getDMY(DMYTag tag, String datum) { return ret; } - private enum HMSTag { - HOUR, - MINUTE, - SECOND - } - /** * Return a specific string based on the specified tag. - * @param tag Specifies what to return from the time string ("HH:mm:ss") + * + * @param tag Specifies what to return from the time string ("HH:mm:ss") * @param zeit a time string * @return Return the string part specified by tag */ @@ -299,33 +319,18 @@ private String getHMS(HMSTag tag, String zeit) { return ret; } - private static String datumDrehen(String datum) { - String ret = ""; - if (!datum.isEmpty()) { - try { - if (datum.length() == 10) { - String tmp = datum.substring(6); // Jahr - tmp += '.' + datum.substring(3, 5); // Monat - tmp += '.' + datum.substring(0, 2); // Tag - ret = tmp; - } - } catch (Exception ex) { - logger.error("Datum: {}", datum, ex); - } - } - return ret; - } - - private static String datumDatumZeitReinigen(String datum) { - String ret = StringUtils.replace(datum, ":", ""); - ret = StringUtils.replace(ret, ".", ""); - return ret; + private void writeEntry(@NotNull XMLStreamWriter writer, int index, @NotNull String data) throws XMLStreamException { + writer.writeCharacters("\t"); //Tab + writer.writeStartElement(XML_NAMES[index]); + writer.writeCharacters(data); + writer.writeEndElement(); + writer.writeCharacters("\n"); } /** * Store the download data in config file. * - * @param writer the writer to the config file. + * @param writer the writer to the config file. */ public void writeConfigEntry(XMLStreamWriter writer) { final int xmlMax = arr.length; @@ -333,12 +338,17 @@ public void writeConfigEntry(XMLStreamWriter writer) { writer.writeStartElement(TAG); writer.writeCharacters("\n"); for (int i = 0; i < xmlMax; ++i) { - if (!arr[i].isEmpty()) { - writer.writeCharacters("\t"); //Tab - writer.writeStartElement(XML_NAMES[i]); - writer.writeCharacters(arr[i]); - writer.writeEndElement(); - writer.writeCharacters("\n"); + switch (i) { + case DatenDownload.DOWNLOAD_GROESSE -> { + var size = mVFilmSize.getSize(); + size /= FileSize.ONE_MiB; + writeEntry(writer, DatenDownload.DOWNLOAD_GROESSE, Long.toString(size)); + } + default -> { + if (!arr[i].isEmpty()) { + writeEntry(writer, i, arr[i]); + } + } } } writer.writeEndElement(); @@ -351,7 +361,7 @@ public void writeConfigEntry(XMLStreamWriter writer) { public void setGroesseFromFilm() { if (film != null) { if (film.getUrlNormalQuality().equals(arr[DOWNLOAD_URL])) { - mVFilmSize.setSize(film.getSize()); + mVFilmSize.setSize(film.getFileSize().toString()); } else { mVFilmSize.setSize(0); } @@ -362,12 +372,20 @@ public void setGroesse(@NotNull String groesse) { if (film != null) { if (!groesse.isEmpty()) { mVFilmSize.setSize(groesse); - } else { - mVFilmSize.setSize(film.getDateigroesse(arr[DOWNLOAD_URL])); } } } + /** + * Request the file size live from Internet. + * Might cause delays when network is slow. + */ + public void queryLiveSize() { + if (film != null) { + mVFilmSize.setSize(film.getFileSizeForUrl(arr[DOWNLOAD_URL])); + } + } + public void init() { datumFilm = getDatumForObject(); try { @@ -384,6 +402,18 @@ public void init() { arr[DOWNLOAD_HD] = "0"; arr[DOWNLOAD_UT] = "0"; } + + /* + * reader reads only into arr but does not properly fill the mVFilmSize... + * do it manually + */ + if (!arr[DatenDownload.DOWNLOAD_GROESSE].isEmpty()) { + try { + var size = Long.parseLong(arr[DatenDownload.DOWNLOAD_GROESSE]); + mVFilmSize.setSize(size * FileSize.ONE_MiB); + } catch (NumberFormatException ignored) { + } + } } public boolean istZurueckgestellt() { @@ -787,7 +817,7 @@ private String replaceString(String replStr, DatenFilm film) { replStr = StringUtils.replace(replStr, "%5", getHMS(HMSTag.MINUTE, film.getSendeZeit().isEmpty() ? getJetzt_HH_MM_SS() : film.getSendeZeit())); replStr = StringUtils.replace(replStr, "%6", getHMS(HMSTag.SECOND, film.getSendeZeit().isEmpty() ? getJetzt_HH_MM_SS() : film.getSendeZeit())); - replStr = StringUtils.replace(replStr, "%i", String.valueOf(film.getFilmNr())); + replStr = StringUtils.replace(replStr, "%i", String.valueOf(System.currentTimeMillis())); String res = ""; if (arr[DOWNLOAD_URL].equals(film.getUrlFuerAufloesung(FilmResolution.Enum.NORMAL))) { @@ -885,4 +915,16 @@ public int compareTo(DatenDownload arg0) { } return ret; } + + private enum DMYTag { + DAY, + MONTH, + YEAR + } + + private enum HMSTag { + HOUR, + MINUTE, + SECOND + } } diff --git a/src/main/java/mediathek/daten/DatenFilm.java b/src/main/java/mediathek/daten/DatenFilm.java index eee1c1c7d9..4afd304b36 100644 --- a/src/main/java/mediathek/daten/DatenFilm.java +++ b/src/main/java/mediathek/daten/DatenFilm.java @@ -7,23 +7,17 @@ import mediathek.tool.GermanStringSorter; import mediathek.tool.datum.DatumFilm; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DurationFormatUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.EnumMap; import java.util.EnumSet; -import java.util.Optional; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -/* - * TODO: - * - Remove the Database Stuff from this Class to own Classes and a real OR-Mapping - * - Finalize a Real Entity - * - Write test cases for each Method - * - Write JavaDoc for each of the new Methods that were split from this moloch - */ - public class DatenFilm implements Comparable { public static final int FILM_NR = 0; // wird vor dem Speichern gelöscht! public static final int FILM_SENDER = 1; @@ -47,130 +41,138 @@ public class DatenFilm implements Comparable { public static final int FILM_REF = 16; // no getter/setter access // Referenz auf this public static final int MAX_ELEM = 17; /** - * The database instance for all descriptions. + * Compressed URLs are missing the base normal quality URL and are indicated by the pipe-symbol. */ - private final static AtomicInteger FILM_COUNTER = new AtomicInteger(0); + public static final char COMPRESSION_MARKER = '|'; private static final GermanStringSorter sorter = GermanStringSorter.getInstance(); private static final Logger logger = LogManager.getLogger(DatenFilm.class); - private final EnumSet flags = EnumSet.noneOf(DatenFilmFlags.class); - /** - * Internal film number, used for storage in database - */ - private final int databaseFilmNumber; - private DatenAbo abo; - private BookmarkData bookmark; + private final static AtomicInteger FILMNR_GENERATOR = new AtomicInteger(0); /** - * film date stored IN SECONDS!!! + * List of countries which can view this film. */ - private DatumFilm datumFilm = DatumFilm.UNDEFINED_FILM_DATE; + public final EnumSet countrySet = EnumSet.noneOf(Country.class); + private final EnumSet flags = EnumSet.noneOf(DatenFilmFlags.class); /** * File size in MByte */ - private FilmSize filmSize; - /** - * film length in seconds. - */ - private long filmLength; - private String websiteLink; - private String description; + private final FilmSize filmSize = new FilmSize(); /** - * Low quality URL. + * Stores all URLs, some keys may not exist. */ - private String url_low_quality = ""; + private final EnumMap dataMap = new EnumMap<>(MapKeys.class); /** - * High Quality (formerly known as HD) URL if available. + * film date stored IN SECONDS!!! */ - private Optional url_high_quality = Optional.empty(); + private DatumFilm datumFilm = DatumFilm.UNDEFINED_FILM_DATE; + private String description; private String datumLong = ""; private String sender = ""; private String thema = ""; private String titel = ""; - /** - * String of countries where this entry can be viewed, if available. - * Empty means viewable without restrictions. - */ - private Optional availableInCountries = Optional.empty(); - /** - * URL to the subtitle file, if available. - */ - private Optional subtitle_url = Optional.empty(); private String datum = ""; private String sendeZeit = ""; - private String dauer = ""; - private String groesse = ""; - /** - * Normal quality URL. - */ - private String url_normal_quality = ""; /** - * film duration in seconds. - * getDauer() stores the same info as a String + * film duration or film length in seconds. */ - private int duration; + private int filmLength; public DatenFilm() { - filmSize = new FilmSize(0); // Dateigröße in MByte - databaseFilmNumber = FILM_COUNTER.getAndIncrement(); + dataMap.put(MapKeys.FILM_NR, FILMNR_GENERATOR.getAndIncrement()); } public DatenFilm(@NotNull DatenFilm other) { - this.abo = other.abo; - this.bookmark = other.bookmark; this.datumFilm = other.datumFilm; - this.filmSize = other.filmSize; - this.filmLength = other.filmLength; - this.databaseFilmNumber = other.databaseFilmNumber; - this.websiteLink = other.websiteLink; + this.filmSize.setSize(other.filmSize.toString()); this.description = other.description; - this.url_low_quality = other.url_low_quality; - this.url_high_quality = other.url_high_quality; this.datumLong = other.datumLong; this.sender = other.sender; this.thema = other.thema; this.titel = other.titel; - this.availableInCountries = other.availableInCountries; - this.subtitle_url = other.subtitle_url; + this.countrySet.addAll(other.countrySet); + this.dataMap.putAll(other.dataMap); this.datum = other.datum; this.sendeZeit = other.sendeZeit; - this.dauer = other.dauer; - this.groesse = other.groesse; - this.url_normal_quality = other.url_normal_quality; - this.duration = other.duration; + this.filmLength = other.filmLength; } - public int getDuration() { - return duration; + /** + * URLs are considered compressed if they contain a '|'-symbol in the text. + * They need to be decompressed before use. + * @param requestedUrl the string to be checked. + * @return true if url is compressed, false otherwise. + */ + public static boolean isCompressedUrl(@NotNull String requestedUrl) { + final int indexPipe = requestedUrl.indexOf(COMPRESSION_MARKER); + return indexPipe != -1; + } + + /** + * Get the filmlength or duration. + * + * @return filmlength/duration in seconds, or 0. + */ + public int getFilmLength() { + return filmLength; + } + + /** + * Set the film's length or duration. + * + * @param dauer Input string in format "HH:MM:SS". + */ + public void setFilmLength(String dauer) { + //bail out early if there is nothing to split... + if (dauer == null || dauer.isEmpty()) { + filmLength = 0; + } + else { + final String[] split = StringUtils.split(dauer, ':'); + + try { + filmLength += Integer.parseInt(split[0]) * 3600; //hour + filmLength += Integer.parseInt(split[1]) * 60; //minute + filmLength += Integer.parseInt(split[2]); //second + } catch (Exception e) { + filmLength = 0; + } + } } public @Nullable DatenAbo getAbo() { - return abo; + return (DatenAbo) dataMap.getOrDefault(MapKeys.ABO_DATA, null); } - public void setAbo(DatenAbo abo) { - this.abo = abo; + public void setAbo(@Nullable DatenAbo abo) { + if (abo == null) + dataMap.remove(MapKeys.ABO_DATA); + else + dataMap.put(MapKeys.ABO_DATA, abo); } public DatumFilm getDatumFilm() { return datumFilm; } - public String getUrlLowQuality() { - return url_low_quality; + public String getLowQualityUrl() { + return (String)dataMap.getOrDefault(MapKeys.LOW_QUALITY_URL, ""); } - public void setUrlLowQuality(String url_low_quality) { - this.url_low_quality = url_low_quality; + public void setLowQualityUrl(@NotNull String url_low_quality) { + if (url_low_quality.isEmpty()) + dataMap.remove(MapKeys.LOW_QUALITY_URL); + else + dataMap.put(MapKeys.LOW_QUALITY_URL, url_low_quality); } - public String getUrlHighQuality() { - return url_high_quality.orElse(""); + public String getHighQualityUrl() { + return (String)dataMap.getOrDefault(MapKeys.HIGH_QUALITY_URL, ""); } - public void setUrlHighQuality(String urlHd) { - if (!urlHd.isEmpty()) - url_high_quality = Optional.of(urlHd); + public void setHighQualityUrl(@NotNull String urlHd) { + if (urlHd.isEmpty()) + dataMap.remove(MapKeys.HIGH_QUALITY_URL); else - url_high_quality = Optional.empty(); + dataMap.put(MapKeys.HIGH_QUALITY_URL, urlHd); } public String getDatumLong() { @@ -233,11 +235,13 @@ public void setSignLanguage(boolean val) { * Get the film number. * This is used internally for the database id AND * for the old MV code that might access it for various stuff. + *

+ * This number is UNUSED and always returns 1 until it is completely removed. * * @return the original internal film number */ public int getFilmNr() { - return databaseFilmNumber; + return (int)dataMap.get(MapKeys.FILM_NR); } /** @@ -245,7 +249,7 @@ public int getFilmNr() { * * @return The size in MByte */ - public FilmSize getFilmSize() { + public FilmSize getFileSize() { return filmSize; } @@ -269,13 +273,16 @@ public void setDescription(final String desc) { } } - public String getWebsiteLink() { - return StringUtils.defaultString(websiteLink); + public String getWebsiteUrl() { + return (String)dataMap.getOrDefault(MapKeys.WEBSITE_URL, ""); } - public void setWebsiteLink(String link) { - if (link != null && !link.isEmpty()) { - websiteLink = link; + public void setWebsiteUrl(String link) { + if (link == null || link.isEmpty()) { + dataMap.remove(MapKeys.WEBSITE_URL); + } + else { + dataMap.put(MapKeys.WEBSITE_URL, link); } } @@ -325,8 +332,12 @@ public boolean hasBurnedInSubtitles() { return flags.contains(DatenFilmFlags.BURNED_IN_SUBTITLES); } + /** + * Return if the film has a subtitle (URL). + * @return true if a downloadable subtitle is available. + */ public boolean hasSubtitle() { - return subtitle_url.isPresent(); + return dataMap.containsKey(MapKeys.SUBTITLE_URL); } //TODO This function might not be necessary as getUrlNormalOrRequested does almost the same @@ -337,10 +348,11 @@ public String getUrlFuerAufloesung(FilmResolution.Enum resolution) { }; } - public String getDateigroesse(String url) { + public String getFileSizeForUrl(@NotNull String url) { if (url.equalsIgnoreCase(getUrlNormalQuality())) { - return getSize(); + return getFileSize().toString(); } else { + //FIXME this is blocking EDT! return FileSize.getFileLengthFromUrl(url); } } @@ -351,7 +363,7 @@ public String getDateigroesse(String url) { * @return a unique "hash" string */ public String getUniqueHash() { - return (getSender() + getThema()).toLowerCase() + getUrlNormalQuality() + getWebsiteLink(); + return (getSender() + getThema()).toLowerCase() + getUrlNormalQuality() + getWebsiteUrl(); } /** @@ -360,7 +372,7 @@ public String getUniqueHash() { * @return true if HQ url is not empty. */ public boolean isHighQuality() { - return url_high_quality.isPresent(); + return dataMap.containsKey(MapKeys.HIGH_QUALITY_URL); } @Override @@ -372,41 +384,6 @@ public int compareTo(@NotNull DatenFilm other) { return ret; } - /** - * Get the filmlength in seconds. - * - * @return filmlength in seconds, or 0. - */ - public long getFilmLength() { - return filmLength; - } - - /** - * Convert HH:MM:SS string into seconds. - * Or set to 0 in case of error. - * - * @return result in seconds or 0. - */ - private long parseTimeToSeconds() { - long seconds = 0; - final String[] split = StringUtils.split(dauer, ':'); - // if empty, don't try to split and return early... - if (split == null || split.length == 0) { - return 0; - } - else { - try { - seconds += Long.parseLong(split[0]) * 3600; //hour - seconds += Long.parseLong(split[1]) * 60; //minute - seconds += Long.parseLong(split[2]); //second - } catch (Exception e) { - seconds = 0; - } - - return seconds; - } - } - private void setDatum() { if (!getSendeDatum().isEmpty()) { // nur dann gibts ein Datum @@ -423,9 +400,6 @@ private void setDatum() { } public void init() { - filmSize = new FilmSize(this); - filmLength = parseTimeToSeconds(); - setDatum(); } @@ -444,13 +418,10 @@ private String getUrlNormalOrRequested(@NotNull FilmResolution.Enum resolution) ret = getUrlNormalQuality(); else { try { - // check if url contains pipe symbol... - final int indexPipe = requestedUrl.indexOf('|'); - if (indexPipe == -1) { //No + if (isCompressedUrl(requestedUrl)) + ret = decompressUrl(requestedUrl); + else ret = requestedUrl; - } else { //Yes - ret = decompressUrl(requestedUrl, indexPipe); - } } catch (Exception e) { ret = ""; logger.error("getUrlNormalOrRequested(auflösung: {}, requestedUrl: {})", resolution, requestedUrl, e); @@ -460,7 +431,8 @@ private String getUrlNormalOrRequested(@NotNull FilmResolution.Enum resolution) return ret; } - private String decompressUrl(@NotNull final String requestedUrl, final int indexPipe) { + public String decompressUrl(@NotNull final String requestedUrl) throws NumberFormatException, IndexOutOfBoundsException { + final int indexPipe = requestedUrl.indexOf(COMPRESSION_MARKER); final int i = Integer.parseInt(requestedUrl.substring(0, indexPipe)); return getUrlNormalQuality().substring(0, i) + requestedUrl.substring(indexPipe + 1); } @@ -473,8 +445,8 @@ private String decompressUrl(@NotNull final String requestedUrl, final int index */ private String getUrlByResolution(@NotNull final FilmResolution.Enum resolution) { return switch (resolution) { - case HIGH_QUALITY -> getUrlHighQuality(); - case LOW -> getUrlLowQuality(); + case HIGH_QUALITY -> getHighQualityUrl(); + case LOW -> getLowQualityUrl(); default -> getUrlNormalQuality(); }; } @@ -519,64 +491,42 @@ public void setSendeZeit(String sendeZeit) { this.sendeZeit = sendeZeit; } - public String getDauer() { - return dauer; - } - - public void setDauer(String dauer) { - this.dauer = dauer; - - //bail out early if there is nothing to split... - if (dauer == null || dauer.isEmpty()) { - duration = 0; - } + /** + * Get the film's length or duration formatted as a string in "HH:MM:SS" format. + * Similar to {@link DatenFilm#getFilmLength()}. + * + * @return film length or duration as String. + */ + public String getFilmLengthAsString() { + if (filmLength == 0) + return ""; else { - //FIXME gefällt mir nicht - final String[] split = StringUtils.split(this.dauer, ':'); - - try { - duration += Integer.parseInt(split[0]) * 3600; //hour - duration += Integer.parseInt(split[1]) * 60; //minute - duration += Integer.parseInt(split[2]); //second - } catch (Exception e) { - duration = 0; - } + var duration = TimeUnit.MILLISECONDS.convert(filmLength, TimeUnit.SECONDS); + return DurationFormatUtils.formatDuration(duration,"HH:mm:ss", true); } } - public String getSize() { - return groesse; - } - - public void setSize(String size) { - this.groesse = size; - } - public String getUrlNormalQuality() { - return url_normal_quality; + return (String)dataMap.getOrDefault(MapKeys.NORMAL_QUALITY_URL, ""); } - public void setUrlNormalQuality(String url_normal_quality) { - this.url_normal_quality = url_normal_quality; - } - - public String getUrlSubtitle() { - return subtitle_url.orElse(""); - } - - public void setUrlSubtitle(String urlSubtitle) { - if (!urlSubtitle.isEmpty()) - subtitle_url = Optional.of(urlSubtitle); + public void setNormalQualityUrl(@NotNull String url_normal_quality) { + if (url_normal_quality.isEmpty()) + dataMap.remove(MapKeys.NORMAL_QUALITY_URL); else - subtitle_url = Optional.empty(); + dataMap.put(MapKeys.NORMAL_QUALITY_URL, url_normal_quality); } - public Optional getGeo() { - return availableInCountries; + public String getSubtitleUrl() { + return (String)dataMap.getOrDefault(MapKeys.SUBTITLE_URL,""); } - public void setGeo(Optional availableInCountries) { - this.availableInCountries = availableInCountries; + public void setSubtitleUrl(@NotNull String urlSubtitle) { + if (urlSubtitle.isEmpty()) + dataMap.remove(MapKeys.SUBTITLE_URL); + else { + dataMap.put(MapKeys.SUBTITLE_URL, urlSubtitle); + } } /** @@ -584,8 +534,8 @@ public void setGeo(Optional availableInCountries) { * * @return BookmarkData entry */ - public BookmarkData getBookmark() { - return this.bookmark; + public @Nullable BookmarkData getBookmark() { + return (BookmarkData) dataMap.getOrDefault(MapKeys.BOOKMARK_DATA, null); } /** @@ -593,8 +543,11 @@ public BookmarkData getBookmark() { * * @param bookmark Bookmark entry */ - public void setBookmark(BookmarkData bookmark) { - this.bookmark = bookmark; + public void setBookmark(@Nullable BookmarkData bookmark) { + if (bookmark == null) + dataMap.remove(MapKeys.BOOKMARK_DATA); + else + dataMap.put(MapKeys.BOOKMARK_DATA, bookmark); } /** @@ -603,6 +556,8 @@ public void setBookmark(BookmarkData bookmark) { * @return boolean true */ public boolean isBookmarked() { - return this.bookmark != null; + return dataMap.containsKey(MapKeys.BOOKMARK_DATA); } + + enum MapKeys {FILM_NR, SUBTITLE_URL, WEBSITE_URL, LOW_QUALITY_URL, NORMAL_QUALITY_URL, HIGH_QUALITY_URL, BOOKMARK_DATA, ABO_DATA} } diff --git a/src/main/java/mediathek/daten/DownloadInfos.java b/src/main/java/mediathek/daten/DownloadInfos.java index cdfa1549b6..2946f2fcd0 100644 --- a/src/main/java/mediathek/daten/DownloadInfos.java +++ b/src/main/java/mediathek/daten/DownloadInfos.java @@ -106,8 +106,8 @@ private void handleTimerEvent(TimerEvent e) { private void makeDownloadInfos() { resetData(); - final var listeDownloads = Daten.getInstance().getListeDownloads(); - final var aktivDownloads = listeDownloads.getListOfStartsNotFinished(DatenDownload.QUELLE_ALLE); + final var aktivDownloads = Daten.getInstance() + .getListeDownloads().getListOfStartsNotFinished(DatenDownload.QUELLE_ALLE); // Liste gestarteter Downloads for (DatenDownload download : aktivDownloads) { diff --git a/src/main/java/mediathek/daten/DownloadStartInfo.java b/src/main/java/mediathek/daten/DownloadStartInfo.java index 079113dac8..575ef7645a 100644 --- a/src/main/java/mediathek/daten/DownloadStartInfo.java +++ b/src/main/java/mediathek/daten/DownloadStartInfo.java @@ -1,6 +1,10 @@ package mediathek.daten; public class DownloadStartInfo { + /** + * Size of the download list. + */ + public int total_num_download_list_entries = 0; public int total_starts = 0; public int num_abos = 0; public int num_downloads = 0; diff --git a/src/main/java/mediathek/daten/GeoblockingField.java b/src/main/java/mediathek/daten/GeoblockingField.java deleted file mode 100644 index ebec02efe6..0000000000 --- a/src/main/java/mediathek/daten/GeoblockingField.java +++ /dev/null @@ -1,9 +0,0 @@ -package mediathek.daten; - -public class GeoblockingField { - public static final String GEO_DE = "DE"; // nur in .. zu sehen - public static final String GEO_AT = "AT"; - public static final String GEO_CH = "CH"; - public static final String GEO_EU = "EU"; - public static final String GEO_WELT = "WELT"; -} diff --git a/src/main/java/mediathek/daten/IndexedFilmList.java b/src/main/java/mediathek/daten/IndexedFilmList.java new file mode 100644 index 0000000000..df2965371c --- /dev/null +++ b/src/main/java/mediathek/daten/IndexedFilmList.java @@ -0,0 +1,63 @@ +package mediathek.daten; + +import mediathek.config.StandardLocations; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.FSDirectory; +import org.apache.lucene.store.NRTCachingDirectory; + +public class IndexedFilmList extends ListeFilme { + private static final Logger logger = LogManager.getLogger(); + private final StandardAnalyzer analyzer = new StandardAnalyzer(); + private Directory luceneDirectory; + private IndexWriter writer; + private DirectoryReader reader; + private IndexSearcher indexSearcher; + + public IndexedFilmList() { + try { + var fsDir = FSDirectory.open(StandardLocations.getFilmIndexPath()); + luceneDirectory = new NRTCachingDirectory(fsDir, 20.0, 100.0); + + IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer); + indexWriterConfig.setRAMBufferSizeMB(256d); + writer = new IndexWriter(luceneDirectory, indexWriterConfig); + } catch (Exception ex) { + logger.error("Creation of Lucene index failed!", ex); + } + } + + public IndexSearcher getIndexSearcher() { + return indexSearcher; + } + + public void setIndexSearcher(IndexSearcher indexSearcher) { + this.indexSearcher = indexSearcher; + } + + public StandardAnalyzer getAnalyzer() { + return analyzer; + } + + public IndexWriter getWriter() { + return writer; + } + + public DirectoryReader getReader() { + return reader; + } + + public void setReader(DirectoryReader reader) { + this.reader = reader; + } + + public Directory getLuceneDirectory() { + return luceneDirectory; + } +} diff --git a/src/main/java/mediathek/daten/ListeAbo.java b/src/main/java/mediathek/daten/ListeAbo.java index 6ab5e41551..32436e53bb 100644 --- a/src/main/java/mediathek/daten/ListeAbo.java +++ b/src/main/java/mediathek/daten/ListeAbo.java @@ -19,7 +19,6 @@ */ package mediathek.daten; -import com.google.common.base.Stopwatch; import mediathek.config.Daten; import mediathek.config.MVConfig; import mediathek.daten.abo.DatenAbo; @@ -28,8 +27,6 @@ import mediathek.gui.messages.AboListChangedEvent; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -38,7 +35,6 @@ public class ListeAbo extends ArrayList { private static final String[] LEER = {""}; - private static final Logger logger = LogManager.getLogger(); private int nr; private int parseMinSize() { @@ -80,7 +76,7 @@ public void addAbo(String aboname, String filmSender, String filmThema, String f aenderungMelden(); Collections.sort(this); } else { - MVMessageDialog.showMessageDialog(null, "Abo existiert bereits", "Abo anlegen", JOptionPane.INFORMATION_MESSAGE); + MVMessageDialog.showMessageDialog(MediathekGui.ui(), "Abo existiert bereits", "Abo anlegen", JOptionPane.INFORMATION_MESSAGE); } } } @@ -193,13 +189,15 @@ private void assignAboToFilm(@NotNull DatenFilm film) { ifPresentOrElse(film::setAbo, () -> deleteAboInFilm(film)); } + /** + * Hier wird tatsächlich für jeden Film die Liste der Abos durchsucht. + * Braucht länger. + * @param listeFilme Die Filmliste + * @param aboLoeschen abo löschen? + */ public void setAboFuerFilm(ListeFilme listeFilme, boolean aboLoeschen) { - Stopwatch stopwatch = Stopwatch.createStarted(); - // hier wird tatsächlich für jeden Film die Liste der Abos durchsucht - // braucht länger - if (this.isEmpty() && aboLoeschen) { - listeFilme.parallelStream().forEach(this::deleteAboInFilm); + listeFilme.forEach(this::deleteAboInFilm); return; } @@ -218,8 +216,5 @@ public void setAboFuerFilm(ListeFilme listeFilme, boolean aboLoeschen) { datenAbo.setThemaFilterPattern(LEER); datenAbo.setIrgendwoFilterPattern(LEER); }); - - stopwatch.stop(); - logger.debug("setAboFuerFilm: {}", stopwatch); } } diff --git a/src/main/java/mediathek/daten/ListeDownloads.java b/src/main/java/mediathek/daten/ListeDownloads.java index dbb935c782..6aff44927a 100644 --- a/src/main/java/mediathek/daten/ListeDownloads.java +++ b/src/main/java/mediathek/daten/ListeDownloads.java @@ -41,7 +41,6 @@ import java.util.*; import java.util.stream.Collectors; -@SuppressWarnings("serial") public class ListeDownloads extends LinkedList { private final Daten daten; @@ -228,6 +227,7 @@ public synchronized void getModel(TModelDownload tModel, boolean onlyAbos, boole boolean onlyNotStarted, boolean onlyStarted, boolean onlyWaiting, boolean onlyRun, boolean onlyFinished) { Object[] object; tModel.setRowCount(0); + tModel.getDataVector().clear(); for (DatenDownload download : this) { if (download.istZurueckgestellt()) { continue; @@ -415,6 +415,7 @@ public synchronized void listeNummerieren() { public synchronized DownloadStartInfo getStarts() { final DownloadStartInfo info = new DownloadStartInfo(); + info.total_num_download_list_entries = size(); for (DatenDownload download : this) { if (!download.istZurueckgestellt()) { diff --git a/src/main/java/mediathek/daten/ListeFilme.java b/src/main/java/mediathek/daten/ListeFilme.java index 843c24c190..d833ef55a0 100644 --- a/src/main/java/mediathek/daten/ListeFilme.java +++ b/src/main/java/mediathek/daten/ListeFilme.java @@ -4,27 +4,55 @@ import mediathek.tool.GermanStringSorter; import org.jetbrains.annotations.NotNull; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.stream.Collectors; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Stream; public class ListeFilme extends ArrayList { public static final String FILMLISTE = "Filmliste"; - private final FilmListMetaData metaData = new FilmListMetaData(); + private static final String PCS_METADATA = "metaData"; + protected final PropertyChangeSupport pcs = new PropertyChangeSupport(this); public boolean neueFilme; + private FilmListMetaData metaData = new FilmListMetaData(); - public FilmListMetaData metaData() { + public FilmListMetaData getMetaData() { return metaData; } + public void setMetaData(FilmListMetaData meta) { + var oldValue = metaData; + metaData = meta; + this.pcs.firePropertyChange(PCS_METADATA, oldValue, metaData); + } + + public void addMetaDataChangeListener(PropertyChangeListener listener) { + this.pcs.addPropertyChangeListener(PCS_METADATA, listener); + } + + /** + * case-insensitive .distinct() implementation. + * @param keyExtractor the function to be applied to the key + * @return true if it has been seen already + * @param template param + */ + private static Predicate distinctByKey(Function keyExtractor) { + Set seen = ConcurrentHashMap.newKeySet(); + return t -> seen.add(keyExtractor.apply(t)); + } + /** * Search all themas within list based on sender. * If sender is empty, return full list of themas. * * @param sender sender name as String - * @return List of themas as String. + * @return IMMUTABLE List of themas as String. */ public List getThemen(String sender) { Stream mystream = parallelStream(); @@ -33,14 +61,13 @@ public List getThemen(String sender) { mystream = mystream.filter(f -> f.getSender().equals(sender)); return mystream.map(DatenFilm::getThema) - .distinct() - .sorted(GermanStringSorter.getInstance()) - .collect(Collectors.toList()); + .filter(distinctByKey(String::toLowerCase)) + .sorted(GermanStringSorter.getInstance()).toList(); } public synchronized void updateFromFilmList(@NotNull ListeFilme newFilmsList) { // In die vorhandene Liste soll eine andere Filmliste einsortiert werden - // es werden nur Filme die noch nicht vorhanden sind, einsortiert + // es werden nur Filme, die noch nicht vorhanden sind, einsortiert final HashSet hashNewFilms = new HashSet<>(newFilmsList.size() + 1, 1); newFilmsList.forEach(newFilm -> hashNewFilms.add(newFilm.getUniqueHash())); @@ -60,11 +87,6 @@ public synchronized void clear() { neueFilme = false; } - public synchronized void setMetaData(FilmListMetaData meta) { - metaData.setDatum(meta.getDatum()); - metaData.setId(meta.getId()); - } - /** * Find movie with given url and sendername * @param url String wiht URL @@ -100,7 +122,7 @@ public synchronized DatenFilm getFilmByUrl_klein_hoch_hd(String url) { * @return true if we need an update. */ public boolean needsUpdate() { - return (isEmpty()) || (metaData().isOlderThan(Konstanten.ALTER_FILMLISTE_SEKUNDEN_FUER_AUTOUPDATE)); + return (isEmpty()) || (getMetaData().isOlderThan(Konstanten.ALTER_FILMLISTE_SEKUNDEN_FUER_AUTOUPDATE)); } public synchronized long countNewFilms() { diff --git a/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java b/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java index a66a92fb7e..42dc21f97b 100644 --- a/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java +++ b/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java @@ -1,9 +1,10 @@ package mediathek.daten.blacklist; -import com.google.common.base.Stopwatch; import mediathek.config.Daten; import mediathek.config.MVConfig; +import mediathek.daten.Country; import mediathek.daten.DatenFilm; +import mediathek.daten.IndexedFilmList; import mediathek.daten.ListeFilme; import mediathek.gui.messages.BlacklistChangedEvent; import mediathek.javafx.filterpanel.ZeitraumSpinner; @@ -11,17 +12,15 @@ import mediathek.tool.ApplicationConfiguration; import mediathek.tool.Filter; import mediathek.tool.MessageBus; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.function.Predicate; public class ListeBlacklist extends ArrayList { - private static final Logger logger = LogManager.getLogger(ListeBlacklist.class); private final GeoblockingPredicate geoblockingPredicate = new GeoblockingPredicate(); /** * This specifies the lower boundary for all films to be shown or not. @@ -92,7 +91,6 @@ public synchronized void clear() { * Main filtering routine */ public synchronized void filterListe() { - Stopwatch stopwatch = Stopwatch.createStarted(); final Daten daten = Daten.getInstance(); final ListeFilme completeFilmList = daten.getListeFilme(); final ListeFilme filteredList = daten.getListeFilmeNachBlackList(); @@ -102,7 +100,7 @@ public synchronized void filterListe() { loadCurrentFilterSettings(); if (completeFilmList != null && !completeFilmList.isEmpty()) { // Check if there are any movies - filteredList.setMetaData(completeFilmList.metaData()); + filteredList.setMetaData(completeFilmList.getMetaData()); this.parallelStream().forEach(entry -> { entry.convertToLowerCase(); @@ -116,8 +114,6 @@ public synchronized void filterListe() { setupNewEntries(); } - stopwatch.stop(); - logger.trace("Complete filtering took: {}", stopwatch); } /** @@ -127,8 +123,11 @@ public synchronized void filterListe() { */ private Predicate createPredicate() { final List> filterList = new ArrayList<>(); - if (days_lower_boundary != 0) - filterList.add(this::checkDate); + // we must keep it for the "old-style search. for lucene it is useless + if (!(Daten.getInstance().getListeFilmeNachBlackList() instanceof IndexedFilmList)) { + if (days_lower_boundary != 0) + filterList.add(this::checkDate); + } if (blacklistIsActive) { if (doNotShowGeoBlockedFilms) { @@ -191,38 +190,39 @@ public synchronized void filterListAndNotifyListeners() { MessageBus.getMessageBus().publishAsync(new BlacklistChangedEvent()); } - /** - * Convert days to milliseconds - * @param days days - * @return the input converted to milliseconds - */ - private long daysToMilliseconds(int days) { - return 1000L * 60L * 60L * 24L * days; - } - - /** - * Load current filter settings from Config - */ - private void loadCurrentFilterSettings() { + private void calculateZeitraumBoundaries() { try { - final String val = MediathekGui.ui().tabFilme.filmActionPanel.zeitraumProperty.getValue(); - if (val.equals(ZeitraumSpinner.UNLIMITED_VALUE)) + var strZeitraum = MediathekGui.ui().tabFilme.filmActionPanel.zeitraumProperty().get(); + if (strZeitraum.equalsIgnoreCase(ZeitraumSpinner.UNLIMITED_VALUE)) days_lower_boundary = 0; else { - days_lower_boundary = System.currentTimeMillis() - daysToMilliseconds(Integer.parseInt(val)); + var days_ms = TimeUnit.MILLISECONDS.convert(Long.parseLong(strZeitraum), TimeUnit.DAYS); + days_lower_boundary = System.currentTimeMillis() - days_ms; } } catch (Exception ex) { days_lower_boundary = 0; } + } + + private void calculateMinimumFilmLength() { try { - minimumFilmLength = Long.parseLong(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_FILMLAENGE)) * 60; // Minuten + var filmlength_minutes = Long.parseLong(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_FILMLAENGE)); + minimumFilmLength = filmlength_minutes * 60; // convert to seconds } catch (Exception ex) { minimumFilmLength = 0; } + } + + /** + * Load current filter settings from Config + */ + private void loadCurrentFilterSettings() { + calculateZeitraumBoundaries(); + calculateMinimumFilmLength(); + blacklistIsActive = Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_ON)); doNotShowFutureFilms = Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_ZUKUNFT_NICHT_ANZEIGEN)); - var config = ApplicationConfiguration.getConfiguration(); - doNotShowGeoBlockedFilms = config.getBoolean(ApplicationConfiguration.BLACKLIST_DO_NOT_SHOW_GEOBLOCKED_FILMS, false); + doNotShowGeoBlockedFilms = ApplicationConfiguration.getInstance().getBlacklistDoNotShowGeoblockedFilms(); geoblockingPredicate.updateLocation(); } @@ -315,7 +315,7 @@ private boolean checkIfFilmIsInFuture(@NotNull DatenFilm film) { * @return true if film should be displayed */ private boolean checkFilmLength(@NotNull DatenFilm film) { - final long filmLength = film.getFilmLength(); + var filmLength = film.getFilmLength(); return !(filmLength != 0 && minimumFilmLength > filmLength); } @@ -323,27 +323,27 @@ static class GeoblockingPredicate implements Predicate { /** * Stores the current user´s location. Can be modified by another thread. */ - private String geoLocation; + private Country geoLocation; public GeoblockingPredicate() { - setLocationData(); + updateLocationData(); } public void updateLocation() { - setLocationData(); + updateLocationData(); } - private void setLocationData() { - geoLocation = ApplicationConfiguration.getConfiguration().getString(ApplicationConfiguration.GEO_LOCATION); + private void updateLocationData() { + geoLocation = ApplicationConfiguration.getInstance().getGeographicLocation(); } @Override public boolean test(DatenFilm film) { - var geoOpt = film.getGeo(); - if (geoOpt.isEmpty()) + if (film.countrySet.isEmpty()) return true; - else - return geoOpt.orElse("").contains(geoLocation); + else { + return film.countrySet.contains(geoLocation); + } } } } diff --git a/src/main/java/mediathek/filmlisten/FilmeImportierenAutoThread.java b/src/main/java/mediathek/filmlisten/FilmeImportierenAutoThread.java index 35110935fc..c97be8cca5 100644 --- a/src/main/java/mediathek/filmlisten/FilmeImportierenAutoThread.java +++ b/src/main/java/mediathek/filmlisten/FilmeImportierenAutoThread.java @@ -27,7 +27,7 @@ public FilmeImportierenAutoThread(ListeFilme listeFilme, ListeFilme listeFilmeDi @Override public void run() { boolean ret; - if (listeFilme.isEmpty() || !listeFilme.metaData().canUseDiffList()) { + if (listeFilme.isEmpty() || !listeFilme.getMetaData().canUseDiffList()) { // dann eine komplette Liste laden listeFilme.clear(); ret = downloadAction.performDownload(StandardLocations.getFilmListUrl(FilmListDownloadType.FULL), listeFilme, days); diff --git a/src/main/java/mediathek/filmlisten/FilmeLaden.java b/src/main/java/mediathek/filmlisten/FilmeLaden.java index fb737a39be..812e5328a2 100644 --- a/src/main/java/mediathek/filmlisten/FilmeLaden.java +++ b/src/main/java/mediathek/filmlisten/FilmeLaden.java @@ -1,27 +1,22 @@ package mediathek.filmlisten; -import com.google.common.base.Stopwatch; -import javafx.application.Platform; -import javafx.scene.control.Alert; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.StandardLocations; import mediathek.daten.DatenFilm; +import mediathek.daten.IndexedFilmList; import mediathek.daten.ListeFilme; import mediathek.filmeSuchen.ListenerFilmeLaden; import mediathek.filmeSuchen.ListenerFilmeLadenEvent; import mediathek.filmlisten.reader.FilmListReader; -import mediathek.gui.actions.FilmListWriteWorkerTask; -import mediathek.javafx.FilmListFilterTask; -import mediathek.javafx.tool.FXProgressPane; -import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.javafx.tool.JavaFxUtils; +import mediathek.gui.messages.FilmListReadStopEvent; +import mediathek.gui.tasks.BlacklistFilterWorker; +import mediathek.gui.tasks.FilmlistWriterWorker; +import mediathek.gui.tasks.LuceneIndexWorker; +import mediathek.gui.tasks.RefreshAboWorker; import mediathek.mainwindow.MediathekGui; -import mediathek.tool.ApplicationConfiguration; -import mediathek.tool.FilmListUpdateType; -import mediathek.tool.GuiFunktionen; +import mediathek.tool.*; import mediathek.tool.http.MVHttpClient; -import mediathek.tool.javafx.FXErrorDialog; import okhttp3.HttpUrl; import okhttp3.Request; import okhttp3.Response; @@ -32,6 +27,7 @@ import javax.swing.*; import javax.swing.event.EventListenerList; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.UnknownHostException; import java.time.Instant; import java.time.LocalDateTime; @@ -46,7 +42,6 @@ public class FilmeLaden { private static final Logger logger = LogManager.getLogger(FilmeLaden.class); private static final String NETWORK_NOT_AVAILABLE = "Netzwerk nicht verfügbar"; - private static final String DIALOG_TITLE = "Filmliste laden"; private static final String NO_UPDATE_AVAILABLE = "Es ist keine aktuellere Filmliste verfügbar."; /** * HTTP error code for not found. @@ -86,7 +81,7 @@ public synchronized void fertig(ListenerFilmeLadenEvent event) { private void showNoUpdateAvailableDialog() { JOptionPane.showMessageDialog(MediathekGui.ui(), NO_UPDATE_AVAILABLE, - DIALOG_TITLE, JOptionPane.INFORMATION_MESSAGE); + Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE); } /** @@ -98,14 +93,14 @@ private boolean hasNewRemoteFilmlist() { boolean result = false; logger.trace("hasNewRemoteFilmList()"); - final String id = Daten.getInstance().getListeFilme().metaData().getId(); + final String id = Daten.getInstance().getListeFilme().getMetaData().getId(); boolean showDialogs = GuiFunktionen.getFilmListUpdateType() != FilmListUpdateType.AUTOMATIC; HttpUrl filmListUrl = Konstanten.ROUTER_BASE_URL.resolve("filmliste.id"); final Request request = new Request.Builder().url(Objects.requireNonNull(filmListUrl)).build(); try (Response response = MVHttpClient.getInstance().getHttpClient().newCall(request).execute(); ResponseBody body = response.body()) { - if (body != null && response.isSuccessful()) { + if (response.isSuccessful()) { String remoteId = body.string(); if (!remoteId.isEmpty() && !remoteId.equalsIgnoreCase(id)) result = true; // we have an update... @@ -125,23 +120,19 @@ private boolean hasNewRemoteFilmlist() { } } catch (UnknownHostException ex) { logger.debug(ex); - if (showDialogs) - Platform.runLater(() -> - FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, DIALOG_TITLE, NETWORK_NOT_AVAILABLE, ex)); - else + if (showDialogs) { + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), NETWORK_NOT_AVAILABLE, ex); + } else logger.warn(NETWORK_NOT_AVAILABLE); } catch (IOException ex) { logger.error("IOxception:", ex); - Platform.runLater(() -> - FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, DIALOG_TITLE, - "Netzwerkfehler aufgetreten!", ex)); + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), "Netzwerkfehler aufgetreten!", ex); } catch (Exception ex) { logger.error("check for filmliste.id failed", ex); - if (showDialogs) - Platform.runLater(() -> - FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, DIALOG_TITLE, - "Ein unbekannter Fehler ist aufgetreten.", ex)); + if (showDialogs) { + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), "Ein unbekannter Fehler ist aufgetreten.", ex); + } } return result; @@ -167,7 +158,7 @@ private boolean performUpdateCheck(ListeFilme listeFilme, String dateiUrl) { //or somebody put a web adress into the text field if (dateiUrl.isEmpty() || dateiUrl.startsWith("http")) { //perform check only if we don´t want to use DIFF list... - if (!listeFilme.metaData().canUseDiffList()) { + if (!listeFilme.getMetaData().canUseDiffList()) { if (!hasNewRemoteFilmlist()) result = false; } @@ -192,7 +183,7 @@ public boolean loadFilmlist(String dateiUrl, boolean immerNeuLaden) { logger.trace("loadFilmlist(String,boolean)"); logger.info(""); - logger.info("Alte Liste erstellt am: {}", listeFilme.metaData().getGenerationDateTimeAsString()); + logger.info("Alte Liste erstellt am: {}", listeFilme.getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", listeFilme.size()); logger.info(" Anzahl Neue: {}", listeFilme.countNewFilms()); if (!istAmLaufen) { @@ -229,7 +220,7 @@ public void updateFilmlist(String dateiUrl) { // erhalten) UND auch gleich im Konfig-Ordner gespeichert logger.debug("Filme laden (Update), start"); logger.info(""); - logger.info("Alte Liste erstellt am: {}", daten.getListeFilme().metaData().getGenerationDateTimeAsString()); + logger.info("Alte Liste erstellt am: {}", daten.getListeFilme().getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", daten.getListeFilme().size()); logger.info(" Anzahl Neue: {}", daten.getListeFilme().countNewFilms()); if (!istAmLaufen) { @@ -265,16 +256,16 @@ private void undEnde(ListenerFilmeLadenEvent event) { // wenn nur ein Update if (!diffListe.isEmpty()) { logger.info("Liste Diff gelesen am: {}", readDate); - logger.info(" Liste Diff erstellt am: {}", diffListe.metaData().getGenerationDateTimeAsString()); + logger.info(" Liste Diff erstellt am: {}", diffListe.getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", diffListe.size()); listeFilme.updateFromFilmList(diffListe); - listeFilme.setMetaData(diffListe.metaData()); + listeFilme.setMetaData(diffListe.getMetaData()); Collections.sort(listeFilme); diffListe.clear(); } else { logger.info("Liste Kompl. gelesen am: {}", readDate); - logger.info(" Liste Kompl erstellt am: {}", listeFilme.metaData().getGenerationDateTimeAsString()); + logger.info(" Liste Kompl erstellt am: {}", listeFilme.getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", listeFilme.size()); } @@ -287,19 +278,17 @@ private void undEnde(ListenerFilmeLadenEvent event) { if (event.fehler) { logger.info(""); logger.info("Filmliste laden war fehlerhaft, alte Liste wird wieder geladen"); - Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setHeaderText("Fehler"); - alert.setContentText("Das Laden der Filmliste hat nicht geklappt!"); - JFXHiddenApplication.showAlert(alert, ui); - }); + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Das Laden der Filmliste hat nicht geklappt!", + Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE); // dann die alte Liste wieder laden listeFilme.clear(); try (FilmListReader reader = new FilmListReader()) { final int num_days = ApplicationConfiguration.getConfiguration().getInt(ApplicationConfiguration.FilmList.LOAD_NUM_DAYS, 0); - reader.readFilmListe(StandardLocations.getFilmlistFilePath(), listeFilme, num_days); + reader.readFilmListe(StandardLocations.getFilmlistFilePathString(), listeFilme, num_days); } logger.info(""); @@ -309,35 +298,44 @@ private void undEnde(ListenerFilmeLadenEvent event) { } logger.info(""); - logger.info("Jetzige Liste erstellt am: {}", listeFilme.metaData().getGenerationDateTimeAsString()); + logger.info("Jetzige Liste erstellt am: {}", listeFilme.getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", listeFilme.size()); logger.info(" Anzahl Neue: {}", listeFilme.countNewFilms()); logger.info(""); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - FXProgressPane hb = new FXProgressPane(); + MessageBus.getMessageBus().publishAsync(new FilmListReadStopEvent()); + JLabel progLabel = MediathekGui.ui().progressLabel; + JProgressBar progressBar = MediathekGui.ui().progressBar; - FilmListFilterTask task = new FilmListFilterTask(true); - task.setOnRunning(e -> { - ui.getStatusBarController().getStatusBar().getRightItems().add(hb); - hb.lb.textProperty().bind(task.messageProperty()); - hb.prog.progressProperty().bind(task.progressProperty()); + try { + SwingUtilities.invokeAndWait(() -> { + ui.swingStatusBar.add(progLabel); + ui.swingStatusBar.add(progressBar); }); + } catch (InterruptedException | InvocationTargetException e) { + throw new RuntimeException(e); + } + var workerTask = CompletableFuture.runAsync(new RefreshAboWorker(progLabel, progressBar)) + .thenRun(new BlacklistFilterWorker(progLabel, progressBar)); - CompletableFuture workerTask = CompletableFuture.runAsync(task); - if (writeFilmList) { - FilmListWriteWorkerTask writerTask = new FilmListWriteWorkerTask(Daten.getInstance()); - writerTask.setOnRunning(e -> { - hb.lb.textProperty().bind(writerTask.messageProperty()); - hb.prog.progressProperty().bind(writerTask.progressProperty()); - }); - workerTask = workerTask.thenRun(writerTask); - } + if (writeFilmList) { + workerTask = workerTask.thenRun(new FilmlistWriterWorker(progLabel, progressBar)); + } + if (daten.getListeFilmeNachBlackList() instanceof IndexedFilmList) { + workerTask = workerTask.thenRun(new LuceneIndexWorker(progLabel, progressBar)); + } - workerTask.thenRun(() -> JavaFxUtils.invokeInFxThreadAndWait(() -> { - ui.getStatusBarController().getStatusBar().getRightItems().remove(hb); - })); - }); + workerTask.thenRun(() -> SwingUtilities.invokeLater(() -> Daten.getInstance().getFilmeLaden().notifyFertig(new ListenerFilmeLadenEvent("", "", 100, 100, false)))) + .thenRun(() -> { + try { + SwingUtilities.invokeAndWait(() -> { + ui.swingStatusBar.remove(progressBar); + ui.swingStatusBar.remove(progLabel); + }); + } catch (InterruptedException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }); } private void fillHash(ListeFilme listeFilme) { @@ -350,7 +348,6 @@ private void fillHash(ListeFilme listeFilme) { private void findAndMarkNewFilms(ListeFilme listeFilme) { listeFilme.neueFilme = false; - Stopwatch stopwatch = Stopwatch.createStarted(); listeFilme.parallelStream() .peek(film -> film.setNew(false)) .filter(film -> !hashSet.contains(film.getUrlNormalQuality())) @@ -358,8 +355,6 @@ private void findAndMarkNewFilms(ListeFilme listeFilme) { film.setNew(true); listeFilme.neueFilme = true; }); - stopwatch.stop(); - logger.debug("findAndMarkNewFilms() took: {}", stopwatch); hashSet.clear(); } diff --git a/src/main/java/mediathek/filmlisten/reader/FilmListReader.java b/src/main/java/mediathek/filmlisten/reader/FilmListReader.java index 5e95f8ce2c..4cd751e325 100644 --- a/src/main/java/mediathek/filmlisten/reader/FilmListReader.java +++ b/src/main/java/mediathek/filmlisten/reader/FilmListReader.java @@ -6,6 +6,7 @@ import mediathek.config.Config; import mediathek.config.Konstanten; import mediathek.controller.SenderFilmlistLoadApprover; +import mediathek.daten.Country; import mediathek.daten.DatenFilm; import mediathek.daten.ListeFilme; import mediathek.filmeSuchen.ListenerFilmeLaden; @@ -30,6 +31,8 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.NoSuchFileException; @@ -41,7 +44,6 @@ import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; public class FilmListReader implements AutoCloseable { @@ -96,7 +98,7 @@ private void parseNeu(JsonParser jp, DatenFilm datenFilm) throws IOException { protected void parseWebsiteLink(JsonParser jp, DatenFilm datenFilm) throws IOException { final String value = jp.nextTextValue(); if (value != null && !value.isEmpty()) { - datenFilm.setWebsiteLink(value); + datenFilm.setWebsiteUrl(value); } } @@ -109,13 +111,19 @@ private void parseDescription(JsonParser jp, DatenFilm datenFilm) throws IOExcep protected void parseGeo(JsonParser jp, DatenFilm datenFilm) throws IOException { var geoStr = checkedString(jp); - Optional geo; if (geoStr.isEmpty()) - geo = Optional.empty(); - else - geo = Optional.of(geoStr); - - datenFilm.setGeo(geo); + datenFilm.countrySet.clear(); + else { + var split = geoStr.split("-"); + for (var geoItem : split) { + try { + datenFilm.countrySet.add(Country.valueOf(geoItem)); + } + catch (IllegalArgumentException ex) { + logger.error("Unable to parse string {} to Country enum", geoItem); + } + } + } } private void parseSender(JsonParser jp, DatenFilm datenFilm) throws IOException { @@ -163,12 +171,14 @@ private void parseMetaData(JsonParser jp, ListeFilme listeFilme) throws IOExcept break; } if (jp.isExpectedStartArrayToken()) { - var meta = listeFilme.metaData(); + var meta = listeFilme.getMetaData(); jp.nextTextValue(); meta.setDatum(jp.nextTextValue()); jp.nextTextValue(); jp.nextTextValue(); meta.setId(jp.nextTextValue()); + //update to fire pcs + listeFilme.setMetaData(meta); break; } @@ -190,15 +200,15 @@ private void skipFieldDescriptions(JsonParser jp) throws IOException { } private void parseUrlSubtitle(JsonParser jp, DatenFilm datenFilm) throws IOException { - datenFilm.setUrlSubtitle(checkedString(jp)); + datenFilm.setSubtitleUrl(checkedString(jp)); } private void parseUrlKlein(JsonParser jp, DatenFilm datenFilm) throws IOException { - datenFilm.setUrlLowQuality(checkedString(jp)); + datenFilm.setLowQualityUrl(checkedString(jp)); } private void parseUrlHd(JsonParser jp, DatenFilm datenFilm) throws IOException { - datenFilm.setUrlHighQuality(checkedString(jp)); + datenFilm.setHighQualityUrl(checkedString(jp)); } private void parseDatumLong(JsonParser jp, DatenFilm datenFilm) throws IOException { @@ -210,12 +220,12 @@ private void parseSendedatum(JsonParser jp, DatenFilm datenFilm) throws IOExcept } private void parseDauer(JsonParser jp, DatenFilm datenFilm) throws IOException { - datenFilm.setDauer(checkedString(jp)); + datenFilm.setFilmLength(checkedString(jp)); } private void parseGroesse(JsonParser jp, DatenFilm datenFilm) throws IOException { String value = checkedString(jp); - datenFilm.setSize(value); + datenFilm.getFileSize().setSize(value); } /** @@ -269,7 +279,7 @@ private void parseTitel(JsonParser jp, DatenFilm datenFilm) throws IOException { } private void parseUrl(JsonParser jp, DatenFilm datenFilm) throws IOException { - datenFilm.setUrlNormalQuality(checkedString(jp)); + datenFilm.setNormalQualityUrl(checkedString(jp)); } private void parseLivestream(DatenFilm datenFilm) { @@ -381,12 +391,12 @@ public void readFilmListe(String source, @NotNull ListeFilme listeFilme, int day notifyStart(source); // für die Progressanzeige if (source.startsWith("http")) { - final URL sourceUrl = new URL(source); - processFromWeb(sourceUrl, listeFilme); + final var sourceUrl = new URI(source); + processFromWeb(sourceUrl.toURL(), listeFilme); } else processFromFile(source, listeFilme); - } catch (MalformedURLException ex) { + } catch (MalformedURLException | URISyntaxException ex) { logger.warn(ex); } @@ -492,7 +502,7 @@ private void notifyProgress(String url, int iProgress) { private void notifyFertig(String url, ListeFilme liste) { logger.info("Liste Filme gelesen am: {}", DateTimeFormatter.ofPattern("dd.MM.yyyy, HH:mm") .format(LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()))); - logger.info(" erstellt am: {}", liste.metaData().getGenerationDateTimeAsString()); + logger.info(" erstellt am: {}", liste.getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", liste.size()); for (ListenerFilmeLaden l : listeners.getListeners(ListenerFilmeLaden.class)) { progressEvent.senderUrl = url; diff --git a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java index 63dc661fe4..c9a3c9fabd 100644 --- a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java +++ b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; +import mediathek.daten.Country; import mediathek.daten.DatenFilm; import mediathek.daten.ListeFilme; import mediathek.gui.messages.FilmListWriteStartEvent; @@ -11,6 +12,7 @@ import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import java.io.BufferedOutputStream; import java.io.File; @@ -20,14 +22,17 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; public class FilmListWriter { private static final Logger logger = LogManager.getLogger(FilmListWriter.class); private static final String TAG_JSON_LIST = "X"; + private final boolean readable; private String sender = ""; private String thema = ""; - private final boolean readable; + private boolean compressSenderTag = true; + private boolean compressThemaTag = true; public FilmListWriter(boolean readable) { this.readable = readable; @@ -54,7 +59,7 @@ private void checkOsxCacheDirectory() { } private void writeFormatHeader(JsonGenerator jg, ListeFilme listeFilme) throws IOException { - final var meta = listeFilme.metaData(); + final var meta = listeFilme.getMetaData(); jg.writeArrayFieldStart(ListeFilme.FILMLISTE); jg.writeString(""); //ListeFilme.FILMLISTE_DATUM_NR unused in newer versions @@ -136,25 +141,56 @@ private void writeEntry(DatenFilm datenFilm, JsonGenerator jg) throws IOExceptio writeTitel(jg, datenFilm); jg.writeString(datenFilm.getSendeDatum()); writeZeit(jg, datenFilm); - jg.writeString(datenFilm.getDauer()); - jg.writeString(datenFilm.getSize()); + jg.writeString(datenFilm.getFilmLengthAsString()); + jg.writeString(datenFilm.getFileSize().toString()); jg.writeString(datenFilm.getDescription()); jg.writeString(datenFilm.getUrlNormalQuality()); - jg.writeString(datenFilm.getWebsiteLink()); - jg.writeString(datenFilm.getUrlSubtitle()); + jg.writeString(datenFilm.getWebsiteUrl()); + jg.writeString(datenFilm.getSubtitleUrl()); skipEntry(jg); //DatenFilm.FILM_URL_RTMP - jg.writeString(datenFilm.getUrlLowQuality()); + writeLowQualityUrl(jg, datenFilm); skipEntry(jg); //DatenFilm.URL_RTMP_KLEIN - jg.writeString(datenFilm.getUrlHighQuality()); + writeHighQualityUrl(jg, datenFilm); skipEntry(jg); //DatenFilm.FILM_URL_RTMP_HD jg.writeString(datenFilm.getDatumLong()); skipEntry(jg); //DatenFilm.FILM_URL_HISTORY - jg.writeString(datenFilm.getGeo().orElse("")); + if (datenFilm.countrySet.isEmpty()) + jg.writeString(""); + else + jg.writeString(datenFilm.countrySet.stream().map(Country::toString).collect(Collectors.joining("-"))); jg.writeString(Boolean.toString(datenFilm.isNew())); jg.writeEndArray(); } + private void writeLowQualityUrl(@NotNull JsonGenerator jg, @NotNull DatenFilm datenFilm) throws IOException { + String url = datenFilm.getLowQualityUrl(); + if (decompressUrls) { + if (DatenFilm.isCompressedUrl(url)) { + url = datenFilm.decompressUrl(url); + } + } + + jg.writeString(url); + } + + private void writeHighQualityUrl(@NotNull JsonGenerator jg, @NotNull DatenFilm datenFilm) throws IOException { + String url = datenFilm.getHighQualityUrl(); + if (decompressUrls) { + if (DatenFilm.isCompressedUrl(url)) { + url = datenFilm.decompressUrl(url); + } + } + + jg.writeString(url); + } + + public void setDecompressUrls(boolean decompressUrls) { + this.decompressUrls = decompressUrls; + } + + private boolean decompressUrls; + private void skipEntry(JsonGenerator jg) throws IOException { jg.writeString(""); } @@ -166,21 +202,37 @@ private void writeTitel(JsonGenerator jg, DatenFilm datenFilm) throws IOExceptio private void writeSender(JsonGenerator jg, DatenFilm datenFilm) throws IOException { String tempSender = datenFilm.getSender(); - if (tempSender.equals(sender)) { - jg.writeString(""); - } else { - sender = tempSender; - jg.writeString(tempSender); + if (compressSenderTag) { + if (tempSender.equals(sender)) { + jg.writeString(""); + } else { + sender = tempSender; + jg.writeString(tempSender); + } } + else + jg.writeString(tempSender); + } + + public void setCompressThemaTag(boolean compressThemaTag) { + this.compressThemaTag = compressThemaTag; + } + + public void setCompressSenderTag(boolean compress) { + compressSenderTag = compress; } private void writeThema(JsonGenerator jg, DatenFilm datenFilm) throws IOException { - if (datenFilm.getThema().equals(thema)) { - jg.writeString(""); - } else { - thema = datenFilm.getThema(); - jg.writeString(datenFilm.getThema()); + if (compressThemaTag) { + if (datenFilm.getThema().equals(thema)) { + jg.writeString(""); + } else { + thema = datenFilm.getThema(); + jg.writeString(datenFilm.getThema()); + } } + else + jg.writeString(datenFilm.getThema()); } private void writeZeit(JsonGenerator jg, DatenFilm datenFilm) throws IOException { diff --git a/src/main/java/mediathek/gui/AppShutdownWindow.java b/src/main/java/mediathek/gui/AppShutdownWindow.java new file mode 100644 index 0000000000..68354e69b7 --- /dev/null +++ b/src/main/java/mediathek/gui/AppShutdownWindow.java @@ -0,0 +1,64 @@ +/* + * Created by JFormDesigner on Mon Jul 25 15:12:10 CEST 2022 + */ + +package mediathek.gui; + +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; +import org.jdesktop.swingx.JXBusyLabel; + +import javax.swing.*; +import java.awt.*; + +/** + * @author Christian Franzke + */ +public class AppShutdownWindow extends JWindow { + public AppShutdownWindow(Window owner) { + super(owner); + initComponents(); + } + + private void initComponents() { + // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents + // Generated using JFormDesigner non-commercial license + label1 = new JXBusyLabel(); + message = new JLabel(); + progress = new JProgressBar(); + + //======== this ======== + var contentPane = getContentPane(); + contentPane.setLayout(new MigLayout( + new LC().insets("20").hideMode(3), //NON-NLS + // columns + new AC() + .fill().gap() + .fill(), + // rows + new AC() + .gap() + )); + + //---- label1 ---- + label1.setVerticalAlignment(SwingConstants.TOP); + contentPane.add(label1, new CC().cell(0, 0, 1, 2).alignY("top").growY(0)); //NON-NLS + + //---- message ---- + message.setText("Progress text here..."); //NON-NLS + contentPane.add(message, new CC().cell(1, 0)); + contentPane.add(progress, new CC().cell(1, 1).alignY("center").grow(100, 0).width("350:350:350")); //NON-NLS + pack(); + setLocationRelativeTo(getOwner()); + // JFormDesigner - End of component initialization //GEN-END:initComponents + } + + // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables + // Generated using JFormDesigner non-commercial license + public JXBusyLabel label1; + public JLabel message; + public JProgressBar progress; + // JFormDesigner - End of variables declaration //GEN-END:variables +} diff --git a/src/main/java/mediathek/gui/AppShutdownWindow.jfd b/src/main/java/mediathek/gui/AppShutdownWindow.jfd new file mode 100644 index 0000000000..bd30f4d883 --- /dev/null +++ b/src/main/java/mediathek/gui/AppShutdownWindow.jfd @@ -0,0 +1,43 @@ +JFDML JFormDesigner: "7.0.7.0.1134" Java: "11.0.15" encoding: "UTF-8" + +new FormModel { + contentType: "form/swing" + root: new FormRoot { + add( new FormWindow( "javax.swing.JWindow", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 20,hidemode 3" + "$columnConstraints": "[fill][fill]" + "$rowConstraints": "[][]" + } ) { + name: "this" + add( new FormComponent( "org.jdesktop.swingx.JXBusyLabel" ) { + name: "label1" + "verticalAlignment": 1 + auxiliary() { + "JavaCodeGenerator.variableModifiers": 1 + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0 1 2,aligny top,growy 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "message" + "text": "Progress text here..." + auxiliary() { + "JavaCodeGenerator.variableModifiers": 1 + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + add( new FormComponent( "javax.swing.JProgressBar" ) { + name: "progress" + auxiliary() { + "JavaCodeGenerator.variableModifiers": 1 + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 1,aligny center,grow 100 0,width 350:350:350" + } ) + }, new FormLayoutConstraints( null ) { + "location": new java.awt.Point( 0, 0 ) + "size": new java.awt.Dimension( 425, 110 ) + } ) + } +} diff --git a/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java new file mode 100644 index 0000000000..78e6c1d4c3 --- /dev/null +++ b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java @@ -0,0 +1,54 @@ +package mediathek.gui; + +import mediathek.tool.FilterConfiguration; +import mediathek.tool.FilterDTO; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +public class FilterSelectionComboBoxModel extends DefaultComboBoxModel { + private final FilterConfiguration filterConfiguration = new FilterConfiguration(); + private final List availableFilters = new ArrayList<>(); + + @Override + public void setSelectedItem(Object anObject) { + super.setSelectedItem(anObject); + if (anObject != null) { + filterConfiguration.setCurrentFilter((FilterDTO) anObject); + } + } + + @Override + public Object getSelectedItem() { + return filterConfiguration.getCurrentFilter(); + } + + public FilterSelectionComboBoxModel() { + availableFilters.addAll(filterConfiguration.getAvailableFilters()); + FilterConfiguration.addAvailableFiltersObserver(() -> { + //System.out.println("FILTER LIST CHANGED"); + SwingUtilities.invokeLater(() -> { + availableFilters.clear(); + availableFilters.addAll(filterConfiguration.getAvailableFilters()); + this.fireContentsChanged(this, 0, availableFilters.size()); + }); + }); + FilterConfiguration.addCurrentFiltersObserver(filterDTO -> SwingUtilities.invokeLater(() -> { + if (getSelectedItem() != filterDTO) { + //System.out.println("CURRENT FILTER CHANGED"); + fireContentsChanged(this, 0, availableFilters.size()); + } + })); + } + + @Override + public int getSize() { + return availableFilters.size(); + } + + @Override + public FilterDTO getElementAt(int index) { + return availableFilters.get(index); + } +} diff --git a/src/main/java/mediathek/gui/MVTray.java b/src/main/java/mediathek/gui/MVTray.java index 6bba5e5a10..a334c31464 100644 --- a/src/main/java/mediathek/gui/MVTray.java +++ b/src/main/java/mediathek/gui/MVTray.java @@ -98,7 +98,7 @@ public MVTray systemTray() { popup.addSeparator(); MenuItem itemBeenden = new MenuItem("Programm beenden"); - itemBeenden.addActionListener(e -> MediathekGui.ui().beenden(false, false)); + itemBeenden.addActionListener(e -> MediathekGui.ui().quitApplication()); popup.add(itemBeenden); trayIcon.setPopupMenu(popup); @@ -136,7 +136,7 @@ public void mouseClicked(MouseEvent e) { private String getTextInfos() { String strText = ""; - strText += "Filmliste erstellt: " + daten.getListeFilme().metaData().getGenerationDateTimeAsString() + " Uhr "; + strText += "Filmliste erstellt: " + daten.getListeFilme().getMetaData().getGenerationDateTimeAsString() + " Uhr "; strText += "\n"; strText += "Anz. Filme: " + daten.getListeFilme().size(); strText += "\n"; diff --git a/src/main/java/mediathek/gui/TabPaneIndex.java b/src/main/java/mediathek/gui/TabPaneIndex.java deleted file mode 100644 index 95894d6357..0000000000 --- a/src/main/java/mediathek/gui/TabPaneIndex.java +++ /dev/null @@ -1,5 +0,0 @@ -package mediathek.gui; - -public enum TabPaneIndex { - NONE, FILME, DOWNLOAD -} diff --git a/src/main/java/mediathek/gui/abo/AboInformationController.kt b/src/main/java/mediathek/gui/abo/AboInformationController.kt deleted file mode 100644 index 2052ca1488..0000000000 --- a/src/main/java/mediathek/gui/abo/AboInformationController.kt +++ /dev/null @@ -1,67 +0,0 @@ -package mediathek.gui.abo - -import javafx.fxml.FXML -import javafx.fxml.Initializable -import javafx.scene.control.Label -import mediathek.config.Daten -import mediathek.daten.abo.DatenAbo -import mediathek.gui.messages.AboListChangedEvent -import mediathek.javafx.tool.JavaFxUtils -import mediathek.tool.MessageBus -import net.engio.mbassy.listener.Handler -import java.net.URL -import java.util.* - -class AboInformationController : Initializable { - @FXML - private lateinit var totalAbos: Label - - @FXML - private lateinit var activeAbos: Label - - @FXML - private lateinit var inactiveAbos: Label - - private fun updateDisplayText() { - val listeAbo = Daten.getInstance().listeAbo - val numAbos = listeAbo.size - - if (numAbos == 1) { - totalAbos.text = "Gesamt: 1 Abo" - } else { - totalAbos.text = "Gesamt: $numAbos Abos" - } - - activeAbos.text = "${numActiveAbos()} eingeschaltet" - inactiveAbos.text = "${numInactiveAbos()} ausgeschaltet" - } - - /** - * Get the number of abos which are active and used. - * - * @return num of used abos - */ - private fun numActiveAbos(): Long { - return Daten.getInstance().listeAbo.stream().filter { obj: DatenAbo -> obj.isActive }.count() - } - - /** - * Get the number of abos which are created but offline. - * - * @return number of abos which are offline - */ - private fun numInactiveAbos(): Long { - return Daten.getInstance().listeAbo.stream().filter { abo: DatenAbo -> !abo.isActive }.count() - } - - @Handler - @Suppress("UNUSED_PARAMETER") - private fun handleAboChangedEvent(e: AboListChangedEvent) { - JavaFxUtils.invokeInFxThreadAndWait { updateDisplayText() } - } - - override fun initialize(location: URL?, resources: ResourceBundle?) { - MessageBus.messageBus.subscribe(this) - updateDisplayText() - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/abo/FXAboToolBar.kt b/src/main/java/mediathek/gui/abo/FXAboToolBar.kt deleted file mode 100644 index fbd5f22459..0000000000 --- a/src/main/java/mediathek/gui/abo/FXAboToolBar.kt +++ /dev/null @@ -1,49 +0,0 @@ -package mediathek.gui.abo - -import ca.odell.glazedlists.javafx.EventObservableList -import javafx.fxml.FXML -import javafx.fxml.FXMLLoader -import javafx.fxml.Initializable -import javafx.scene.control.Button -import javafx.scene.control.ComboBox -import javafx.scene.control.ToolBar -import mediathek.tool.SenderListModel -import org.apache.logging.log4j.LogManager -import java.io.IOException -import java.net.URL -import java.util.* - -class FXAboToolBar : ToolBar(), Initializable { - @FXML lateinit var cbSender: ComboBox - - @FXML lateinit var btnOn: Button - - @FXML lateinit var btnOff: Button - - @FXML lateinit var btnDelete: Button - - @FXML lateinit var btnEdit: Button - - @FXML lateinit var btnNewAbo: Button - - override fun initialize(url: URL?, resourceBundle: ResourceBundle?) { - cbSender.items = EventObservableList(SenderListModel()) - cbSender.selectionModel.select(0) - } - - companion object { - private val logger = LogManager.getLogger() - } - - init { - try { - val url = FXAboToolBar::class.java.getResource("/mediathek/res/programm/fxml/abo/abo_toolbar.fxml") - val fxmlLoader = FXMLLoader(url) - fxmlLoader.setRoot(this) - fxmlLoader.setController(this) - fxmlLoader.load() - } catch (e: IOException) { - logger.error("Failed to load FXML!", e) - } - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/abo/ManageAboDialog.kt b/src/main/java/mediathek/gui/abo/ManageAboDialog.kt index c9fabdcc7f..3f97e67c13 100644 --- a/src/main/java/mediathek/gui/abo/ManageAboDialog.kt +++ b/src/main/java/mediathek/gui/abo/ManageAboDialog.kt @@ -4,6 +4,7 @@ import mediathek.tool.ApplicationConfiguration import mediathek.tool.EscapeKeyHandler import org.apache.commons.configuration2.sync.LockMode import java.awt.BorderLayout +import java.awt.Dimension import java.awt.Frame import javax.swing.JDialog @@ -36,7 +37,7 @@ class ManageAboDialog(owner: Frame?) : JDialog(owner) { setSize(width, height) setLocation(x,y) } - catch(e: NoSuchElementException) { + catch(_: NoSuchElementException) { } finally { config.unlock(LockMode.READ) @@ -62,10 +63,11 @@ class ManageAboDialog(owner: Frame?) : JDialog(owner) { defaultCloseOperation = DISPOSE_ON_CLOSE isResizable = true isModal = true - aboPanel = ManageAboPanel() + aboPanel = ManageAboPanel(this) val contentPane = contentPane contentPane.layout = BorderLayout() contentPane.add(aboPanel, BorderLayout.CENTER) + minimumSize = Dimension(640,480) pack() restoreFromConfig() diff --git a/src/main/java/mediathek/gui/abo/ManageAboPanel.java b/src/main/java/mediathek/gui/abo/ManageAboPanel.java index d3595f73cd..1aadd7ade8 100644 --- a/src/main/java/mediathek/gui/abo/ManageAboPanel.java +++ b/src/main/java/mediathek/gui/abo/ManageAboPanel.java @@ -1,21 +1,14 @@ package mediathek.gui.abo; -import javafx.embed.swing.JFXPanel; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.layout.HBox; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; +import ca.odell.glazedlists.swing.GlazedListsSwing; import mediathek.config.Daten; import mediathek.daten.abo.AboTags; import mediathek.daten.abo.DatenAbo; import mediathek.gui.actions.CreateNewAboAction; import mediathek.gui.dialog.DialogEditAbo; import mediathek.gui.messages.AboListChangedEvent; -import mediathek.javafx.tool.JavaFxUtils; import mediathek.mainwindow.MediathekGui; -import mediathek.tool.MessageBus; -import mediathek.tool.NoSelectionErrorDialog; +import mediathek.tool.*; import mediathek.tool.cellrenderer.CellRendererAbo; import mediathek.tool.listener.BeobTableHeader; import mediathek.tool.models.TModelAbo; @@ -24,12 +17,15 @@ import net.engio.mbassy.listener.Handler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jdesktop.swingx.JXStatusBar; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; -import java.net.URL; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; public class ManageAboPanel extends JPanel { private static final String ACTION_MAP_KEY_EDIT_ABO = "edit_abo"; @@ -38,18 +34,17 @@ public class ManageAboPanel extends JPanel { private final MVTable tabelle = new MVAbosTable(); private final Daten daten; private final CreateNewAboAction createAboAction = new CreateNewAboAction(Daten.getInstance().getListeAbo()); - private final JFXPanel toolBarPanel = new JFXPanel(); - private final JFXPanel infoPanel = new JFXPanel(); - private FXAboToolBar toolBar; + private final JXStatusBar infoPanel = new JXStatusBar(); + private final JLabel totalAbos = new JLabel("totalAbos"); + private final JLabel activeAbos = new JLabel("activeAbos"); + private final JLabel inactiveAbos = new JLabel("inactiveAbos"); + private final JToolBar swingToolBar = new JToolBar(); + private final JComboBox senderCombo = new JComboBox<>(); + private final InfiniteProgressPanel infiniteProgressPanel = new InfiniteProgressPanel(); + private final JButton btnEditAbo = new JButton(); private JScrollPane jScrollPane1; - /* - * controller must be kept in variable for strong ref, otherwise GC will erase controller and therefore - * update of abos in dialog will stop working... - */ - private AboInformationController infoController; - public ManageAboPanel() { - super(); + public ManageAboPanel(@NotNull JDialog dialog) { daten = Daten.getInstance(); initComponents(); @@ -57,12 +52,29 @@ public ManageAboPanel() { setupToolBar(); setupInfoPanel(); + updateInfoText(); MessageBus.getMessageBus().subscribe(this); initListeners(); initializeTable(); + + //disable edit button when multiple rows are selected + tabelle.getSelectionModel().addListSelectionListener(e -> btnEditAbo.setEnabled(tabelle.getSelectedRows().length <= 1)); + + //edit abo on double mouse-click + tabelle.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent mouseEvent) { + if (mouseEvent.getClickCount() == 2 && tabelle.getSelectedRow() != -1) { + if (tabelle.rowAtPoint(mouseEvent.getPoint()) != -1) { + editAbo(); + } + } + } + }); + + dialog.setGlassPane(infiniteProgressPanel); } public void addObjectData(TModelAbo model, String sender) { @@ -90,21 +102,9 @@ public void addObjectData(TModelAbo model, String sender) { } private void setupInfoPanel() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - try { - URL url = getClass().getResource("/mediathek/res/programm/fxml/abo/abo_information_panel.fxml"); - - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(url); - - HBox infoPane = loader.load(); - infoPanel.setScene(new Scene(infoPane)); - - infoController = loader.getController(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); + infoPanel.add(totalAbos); + infoPanel.add(activeAbos); + infoPanel.add(inactiveAbos); } private void initializeTable() { @@ -116,20 +116,41 @@ private void initializeTable() { } private void setupToolBar() { - CreateNewAboAction newAboAction = new CreateNewAboAction(Daten.getInstance().getListeAbo()); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - toolBar = new FXAboToolBar(); - toolBar.btnOn.setOnAction(e -> SwingUtilities.invokeLater(() -> changeAboActiveState(true))); - toolBar.btnOff.setOnAction(e -> SwingUtilities.invokeLater(() -> changeAboActiveState(false))); - toolBar.btnDelete.setOnAction(e -> SwingUtilities.invokeLater(this::aboLoeschen)); - toolBar.btnEdit.setOnAction(e -> SwingUtilities.invokeLater(this::editAbo)); - - toolBar.btnNewAbo.setOnAction(e -> SwingUtilities.invokeLater(() -> newAboAction.actionPerformed(null))); - - toolBar.cbSender.setOnAction(e -> SwingUtilities.invokeLater(this::tabelleLaden)); - - toolBarPanel.setScene(new Scene(toolBar)); - }); + JButton button = new JButton(); + button.setToolTipText("Abos einschalten"); + button.addActionListener(l -> changeAboActiveState(true)); + button.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg")); + swingToolBar.add(button); + + button = new JButton(); + button.setToolTipText("Abos ausschalten"); + button.addActionListener(l -> changeAboActiveState(false)); + button.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/xmark.svg", 16f)); + swingToolBar.add(button); + swingToolBar.addSeparator(); + + button = new JButton(createAboAction); + button.setText(""); + swingToolBar.add(button); + + button = new JButton(); + button.setToolTipText("Abos löschen"); + button.addActionListener(l -> aboLoeschen()); + button.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); + swingToolBar.add(button); + + btnEditAbo.setToolTipText("Abo ändern"); + btnEditAbo.addActionListener(l -> editAbo()); + btnEditAbo.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/pen-to-square.svg")); + swingToolBar.add(btnEditAbo); + swingToolBar.addSeparator(); + + swingToolBar.add(new JLabel("Abos für Sender:")); + senderCombo.setMaximumSize(new Dimension(150, Integer.MAX_VALUE)); + senderCombo.setModel(GlazedListsSwing.eventComboBoxModel(new SenderListModel())); + senderCombo.setSelectedIndex(0); + senderCombo.addActionListener(l -> tabelleLaden()); + swingToolBar.add(senderCombo); } public void tabelleSpeichern() { @@ -140,7 +161,41 @@ public void tabelleSpeichern() { @Handler private void handleAboListChanged(AboListChangedEvent e) { - SwingUtilities.invokeLater(this::tabelleLaden); + SwingUtilities.invokeLater(() -> { + tabelleLaden(); + updateInfoText(); + }); + } + + /** + * Get the number of abos which are active and used. + * + * @return num of used abos + */ + private long numActiveAbos() { + return Daten.getInstance().getListeAbo().stream().filter(DatenAbo::isActive).count(); + } + + /** + * Get the number of abos which are created but offline. + * + * @return number of abos which are offline + */ + private long numInactiveAbos() { + return Daten.getInstance().getListeAbo().stream().filter(abo -> !abo.isActive()).count(); + } + + private void updateInfoText() { + var listeAbo = Daten.getInstance().getListeAbo(); + var numAbos = listeAbo.size(); + + if (numAbos == 1) + totalAbos.setText("Gesamt: 1 Abo"); + else + totalAbos.setText(String.format("Gesamt: %d Abos", numAbos)); + + activeAbos.setText(String.format("%d eingeschaltet", numActiveAbos())); + inactiveAbos.setText(String.format("%d ausgeschaltet", numInactiveAbos())); } private void setupKeyMap() { @@ -165,19 +220,19 @@ public void actionPerformed(ActionEvent e) { private JPopupMenu createContextMenu() { JMenuItem itemEinschalten = new JMenuItem("Abo einschalten"); - itemEinschalten.setIcon(IconFontSwing.buildIcon(FontAwesome.CHECK, 16)); + itemEinschalten.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg")); itemEinschalten.addActionListener(e -> changeAboActiveState(true)); JMenuItem itemDeaktivieren = new JMenuItem("Abo ausschalten"); - itemDeaktivieren.setIcon(IconFontSwing.buildIcon(FontAwesome.TIMES, 16)); + itemDeaktivieren.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/xmark.svg")); itemDeaktivieren.addActionListener(e -> changeAboActiveState(false)); JMenuItem itemLoeschen = new JMenuItem("Abo löschen"); - itemLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.MINUS, 16)); + itemLoeschen.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); itemLoeschen.addActionListener(e -> aboLoeschen()); JMenuItem itemAendern = new JMenuItem("Abo ändern"); - itemAendern.setIcon(IconFontSwing.buildIcon(FontAwesome.PENCIL_SQUARE_O, 16)); + itemAendern.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/pen-to-square.svg")); itemAendern.addActionListener(e -> editAbo()); JMenuItem itemNeu = new JMenuItem(); @@ -214,15 +269,14 @@ private void initListeners() { private void tabelleLaden() { tabelle.getSpalten(); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - final String selectedItem = toolBar.cbSender.getValue(); - if (selectedItem != null) { - SwingUtilities.invokeLater(() -> { - addObjectData((TModelAbo) tabelle.getModel(), selectedItem); - tabelle.setSpalten(); - }); - } - }); + String selectedItem = null; + var item = senderCombo.getSelectedItem(); + if (item != null) + selectedItem = item.toString(); + if (selectedItem != null) { + addObjectData((TModelAbo) tabelle.getModel(), selectedItem); + tabelle.setSpalten(); + } } private void aboLoeschen() { @@ -231,7 +285,7 @@ private void aboLoeschen() { String text; if (rows.length == 1) { final int delRow = tabelle.convertRowIndexToModel(rows[0]); - var abo = (DatenAbo)tabelle.getModel().getValueAt(delRow, DatenAbo.ABO_REF); + var abo = (DatenAbo) tabelle.getModel().getValueAt(delRow, DatenAbo.ABO_REF); text = '"' + abo.getName() + "\" löschen?"; } else { text = "Möchten Sie wirklich " + rows.length + " Abos löschen?"; @@ -243,7 +297,7 @@ private void aboLoeschen() { final var listeAbo = daten.getListeAbo(); for (var row : rows) { final int modelRow = tabelle.convertRowIndexToModel(row); - var abo = (DatenAbo)tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); + var abo = (DatenAbo) tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); listeAbo.remove(abo); } } catch (Exception e) { @@ -254,9 +308,10 @@ private void aboLoeschen() { selectFirstRow(); - daten.getListeAbo().aenderungMelden(); + var worker = new ChangeWorker(); + worker.execute(); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } @@ -273,13 +328,13 @@ private void selectFirstRow() { public void editAbo() { // nichts selektiert if (tabelle.getSelectedRowCount() == 0) { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); return; } final int[] rows = tabelle.getSelectedRows(); int modelRow = tabelle.convertRowIndexToModel(tabelle.getSelectedRow()); - var editedAbo = (DatenAbo)tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); + var editedAbo = (DatenAbo) tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); DialogEditAbo dialog = new DialogEditAbo(MediathekGui.ui(), editedAbo, tabelle.getSelectedRowCount() > 1); dialog.setTitle("Abo ändern"); @@ -298,7 +353,7 @@ public void editAbo() { } modelRow = tabelle.convertRowIndexToModel(row); - var curSelAbo = (DatenAbo)tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); + var curSelAbo = (DatenAbo) tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); AboTags.fromIndex(b).ifPresent(tag -> { switch (tag) { @@ -316,7 +371,8 @@ public void editAbo() { } tabelleLaden(); - daten.getListeAbo().aenderungMelden(); + var worker = new ChangeWorker(); + worker.execute(); } private void changeAboActiveState(boolean ein) { @@ -324,7 +380,7 @@ private void changeAboActiveState(boolean ein) { if (rows.length > 0) { for (int row : rows) { int modelRow = tabelle.convertRowIndexToModel(row); - var akt = (DatenAbo)tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); + var akt = (DatenAbo) tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); akt.setActive(ein); } tabelleLaden(); @@ -334,9 +390,10 @@ private void changeAboActiveState(boolean ein) { tabelle.addRowSelectionInterval(row, row); } - daten.getListeAbo().aenderungMelden(); + var worker = new ChangeWorker(); + worker.execute(); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } @@ -350,8 +407,27 @@ private void initComponents() { jTable1.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); jScrollPane1.setViewportView(jTable1); - add(toolBarPanel, BorderLayout.NORTH); + add(swingToolBar, BorderLayout.NORTH); add(jScrollPane1, BorderLayout.CENTER); add(infoPanel, BorderLayout.SOUTH); } + + class ChangeWorker extends SwingWorker { + public ChangeWorker() { + infiniteProgressPanel.start(); + infiniteProgressPanel.setText("Verarbeite Abos..."); + } + + @Override + protected void done() { + infiniteProgressPanel.stop(); + infiniteProgressPanel.setText(""); + } + + @Override + protected Void doInBackground() { + daten.getListeAbo().aenderungMelden(); + return null; + } + } } diff --git a/src/main/java/mediathek/gui/actions/AdvanceDownloadsAction.java b/src/main/java/mediathek/gui/actions/AdvanceDownloadsAction.java new file mode 100644 index 0000000000..d8d0fc27f4 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/AdvanceDownloadsAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class AdvanceDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public AdvanceDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Downloads vorziehen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-up.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.downloadsVorziehen(); + } +} diff --git a/src/main/java/mediathek/gui/actions/ChangeGlobalFontSetting.java b/src/main/java/mediathek/gui/actions/ChangeGlobalFontSetting.java deleted file mode 100644 index efbd8bbd1a..0000000000 --- a/src/main/java/mediathek/gui/actions/ChangeGlobalFontSetting.java +++ /dev/null @@ -1,133 +0,0 @@ -package mediathek.gui.actions; - -import javafx.application.Platform; -import javafx.geometry.Insets; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.*; -import javafx.scene.layout.FlowPane; -import javafx.stage.Stage; -import mediathek.mainwindow.MediathekGui; -import mediathek.tool.ApplicationConfiguration; -import mediathek.tool.swing.LookAndFeelType; -import mediathek.tool.swing.SwingUIFontChanger; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.controlsfx.glyphfont.FontAwesome; -import org.controlsfx.glyphfont.GlyphFont; -import org.controlsfx.glyphfont.GlyphFontRegistry; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; - -public class ChangeGlobalFontSetting extends AbstractAction { - private static final Logger logger = LogManager.getLogger(); - private JMenuItem menuItem; - private boolean sizeChanged; - private Stage window; - - public ChangeGlobalFontSetting() { - putValue(Action.NAME, "Globale Schriftgröße ändern..."); - } - - public void setMenuItem(JMenuItem menuItem) { - this.menuItem = menuItem; - } - - private int getCurrentSize() { - int result = -1; - Font font; - - switch (LookAndFeelType.get(UIManager.getLookAndFeel().getClass().getName())) { - case Windows -> { - font = (Font) UIManager.getDefaults().get(SwingUIFontChanger.WINDOWS_DEFAULT_FONT); - result = font.getSize(); - } - case Nimbus -> { - font = (Font) UIManager.getDefaults().get(SwingUIFontChanger.NIMBUS_DEFAULT_FONT); - result = font.getSize(); - } - } - return result; - } - - @Override - public void actionPerformed(ActionEvent e) { - var initialSize = getCurrentSize(); - sizeChanged = false; - - Platform.runLater(() -> { - createWindow(); - window.setScene(new Scene(createLayout(initialSize))); - window.show(); - }); - } - - private FlowPane createLayout(int initialSize) { - Label label = new Label("Schriftgröße:"); - final Spinner spinner = new Spinner<>(); - spinner.setEditable(true); - - var valueFactory = new SpinnerValueFactory.IntegerSpinnerValueFactory(12, 48, initialSize); - spinner.setValueFactory(valueFactory); - spinner.valueProperty().addListener((observableValue, oldValue, newValue) -> SwingUtilities.invokeLater(() -> spinnerUpdate(newValue))); - - FlowPane root = new FlowPane(); - root.setHgap(10); - root.setVgap(10); - root.setPadding(new Insets(10)); - - GlyphFont fontAwesome = GlyphFontRegistry.font("FontAwesome"); - Button btnReset = new Button("", fontAwesome.create(FontAwesome.Glyph.TRASH_ALT)); - btnReset.setTooltip(new Tooltip("Schriftgröße zurücksetzen")); - - btnReset.setOnAction(evt -> resetAction()); - root.getChildren().addAll(label, spinner, btnReset); - - return root; - } - - private void spinnerUpdate(int newValue) { - logger.info("Updating Swing UI font size to {}", newValue); - SwingUIFontChanger fc = new SwingUIFontChanger(); - fc.changeFontSize(newValue); - ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.APPLICATION_UI_FONT_SIZE, (float) newValue); - - SwingUtilities.updateComponentTreeUI(MediathekGui.ui()); - sizeChanged = true; - } - - private void resetAction() { - logger.info("Resetting Swing UI to default font size"); - ApplicationConfiguration.getConfiguration().clearProperty(ApplicationConfiguration.APPLICATION_UI_FONT_SIZE); - sizeChanged = true; - window.close(); - } - - private void showAppTerminationAlert() { - Alert alert = new Alert(Alert.AlertType.CONFIRMATION, """ - Die Änderung der Schriftgröße erfordert einen Neustart. - Möchten Sie MediathekView nun beenden? - """, ButtonType.YES, ButtonType.NO); - alert.setHeaderText("Schriftgröße wurde geändert"); - alert.showAndWait(); - if (alert.getResult() == ButtonType.YES) { - SwingUtilities.invokeLater(() -> MediathekGui.ui().beenden(false, false)); - } - } - - private void createWindow() { - window = new Stage(); - window.setTitle("Globale Schriftgröße ändern"); - window.setResizable(false); - window.setOnShowing(evt -> SwingUtilities.invokeLater(() -> menuItem.setEnabled(false))); - window.setOnHidden(evt -> SwingUtilities.invokeLater(() -> { - menuItem.setEnabled(true); - if (sizeChanged) { - Platform.runLater(this::showAppTerminationAlert); - } - })); - } -} diff --git a/src/main/java/mediathek/gui/actions/CleanupDownloadListAction.java b/src/main/java/mediathek/gui/actions/CleanupDownloadListAction.java new file mode 100644 index 0000000000..c31b5a2dfc --- /dev/null +++ b/src/main/java/mediathek/gui/actions/CleanupDownloadListAction.java @@ -0,0 +1,23 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class CleanupDownloadListAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public CleanupDownloadListAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Liste säubern"); + putValue(Action.SHORT_DESCRIPTION, "Liste säubern"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/eraser.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.cleanupDownloads(); + } +} diff --git a/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt b/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt index 213874c440..f59810a768 100644 --- a/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt +++ b/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt @@ -1,8 +1,7 @@ package mediathek.gui.actions -import jiconfont.icons.font_awesome.FontAwesome -import jiconfont.swing.IconFontSwing import mediathek.daten.ListeAbo +import mediathek.tool.SVGIconUtilities import java.awt.event.ActionEvent import javax.swing.AbstractAction @@ -13,6 +12,7 @@ class CreateNewAboAction(private val listeAbo: ListeAbo) : AbstractAction() { init { putValue(NAME, "Abo anlegen...") - putValue(SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.PLUS, 16f)) + putValue(SHORT_DESCRIPTION, "Abo anlegen") + putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")) } } \ No newline at end of file diff --git a/src/main/java/mediathek/gui/actions/DeferDownloadsAction.java b/src/main/java/mediathek/gui/actions/DeferDownloadsAction.java new file mode 100644 index 0000000000..dd4eaf3215 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/DeferDownloadsAction.java @@ -0,0 +1,23 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class DeferDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public DeferDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Downloads zurückstellen"); + putValue(Action.SHORT_DESCRIPTION, "Downloads zurückstellen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/clock.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.downloadLoeschen(false); + } +} diff --git a/src/main/java/mediathek/gui/actions/DeleteDownloadAction.java b/src/main/java/mediathek/gui/actions/DeleteDownloadAction.java new file mode 100644 index 0000000000..bd03e7b249 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/DeleteDownloadAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class DeleteDownloadAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public DeleteDownloadAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/xmark.svg")); + putValue(Action.NAME, "gespeicherten Film (Datei) löschen"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.filmLoeschen_(); + } +} diff --git a/src/main/java/mediathek/gui/actions/DeleteDownloadsAction.java b/src/main/java/mediathek/gui/actions/DeleteDownloadsAction.java new file mode 100644 index 0000000000..f4d297fa2a --- /dev/null +++ b/src/main/java/mediathek/gui/actions/DeleteDownloadsAction.java @@ -0,0 +1,23 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class DeleteDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public DeleteDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Downloads aus Liste entfernen"); + putValue(Action.SHORT_DESCRIPTION, "Downloads entfernen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.downloadLoeschen(true); + } +} diff --git a/src/main/java/mediathek/gui/actions/DeleteLocalFilmlistAction.java b/src/main/java/mediathek/gui/actions/DeleteLocalFilmlistAction.java index 6d262481bf..a68eb31861 100644 --- a/src/main/java/mediathek/gui/actions/DeleteLocalFilmlistAction.java +++ b/src/main/java/mediathek/gui/actions/DeleteLocalFilmlistAction.java @@ -26,13 +26,13 @@ public DeleteLocalFilmlistAction(MediathekGui parent) { @Override public void actionPerformed(ActionEvent e) { try { - var filmlistPathStr = StandardLocations.getFilmlistFilePath(); + var filmlistPathStr = StandardLocations.getFilmlistFilePathString(); var fimlistPath = Paths.get(filmlistPathStr); FileUtils.moveToTrash(fimlistPath); JOptionPane.showMessageDialog(owner, "Filmliste wurde gelöscht.\nDas Programm wird nun beendet.", Konstanten.PROGRAMMNAME,JOptionPane.INFORMATION_MESSAGE); - MediathekGui.ui().beenden(false,false); + MediathekGui.ui().quitApplication(); } catch (IOException ex) { logger.error("Failed to delete filmlist", ex); JOptionPane.showMessageDialog(owner, diff --git a/src/main/java/mediathek/gui/actions/EditBlacklistAction.java b/src/main/java/mediathek/gui/actions/EditBlacklistAction.java new file mode 100644 index 0000000000..dcc921d25b --- /dev/null +++ b/src/main/java/mediathek/gui/actions/EditBlacklistAction.java @@ -0,0 +1,37 @@ +package mediathek.gui.actions; + +import mediathek.config.Daten; +import mediathek.gui.dialog.DialogLeer; +import mediathek.gui.dialogEinstellungen.PanelBlacklist; +import mediathek.tool.GuiFunktionen; +import mediathek.tool.SVGIconUtilities; +import org.apache.commons.lang3.SystemUtils; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +public class EditBlacklistAction extends AbstractAction { + private final JFrame parent; + + public EditBlacklistAction(JFrame parent) { + this.parent = parent; + + putValue(NAME, "Blacklist bearbeiten..."); + putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/rectangle-list.svg")); + putValue(SHORT_DESCRIPTION, "Blacklist bearbeiten"); + KeyStroke keyStroke; + if (SystemUtils.IS_OS_MAC_OSX) + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F9, GuiFunktionen.getPlatformControlKey()); + else + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_B, GuiFunktionen.getPlatformControlKey()); + putValue(Action.ACCELERATOR_KEY, keyStroke); + } + + @Override + public void actionPerformed(ActionEvent e) { + DialogLeer dialog = new DialogLeer(parent, true); + dialog.init("Blacklist", new PanelBlacklist(Daten.getInstance(), null, PanelBlacklist.class.getName() + "_3")); + dialog.setVisible(true); + } +} diff --git a/src/main/java/mediathek/gui/actions/EditDownloadAction.java b/src/main/java/mediathek/gui/actions/EditDownloadAction.java new file mode 100644 index 0000000000..300fc937cb --- /dev/null +++ b/src/main/java/mediathek/gui/actions/EditDownloadAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class EditDownloadAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public EditDownloadAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Download ändern"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/pen-to-square.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.editDownload(); + } +} diff --git a/src/main/java/mediathek/gui/actions/FilmListWriteWorkerTask.java b/src/main/java/mediathek/gui/actions/FilmListWriteWorkerTask.java deleted file mode 100644 index c69679963f..0000000000 --- a/src/main/java/mediathek/gui/actions/FilmListWriteWorkerTask.java +++ /dev/null @@ -1,28 +0,0 @@ -package mediathek.gui.actions; - -import javafx.concurrent.Task; -import mediathek.config.Daten; -import mediathek.config.StandardLocations; -import mediathek.filmlisten.writer.FilmListWriter; - -public class FilmListWriteWorkerTask extends Task { - - private final Daten daten; - - public FilmListWriteWorkerTask(Daten daten) { - super(); - this.daten = daten; - } - - @Override - protected Void call() { - FilmListWriter writer = new FilmListWriter(false); - updateMessage("Schreibe Filmliste"); - updateProgress(0d, 1d); - writer.writeFilmList(StandardLocations.getFilmlistFilePath(), - daten.getListeFilme(), - prog -> updateProgress(prog, 1d)); - - return null; - } -} diff --git a/src/main/java/mediathek/gui/actions/InvertSelectionAction.java b/src/main/java/mediathek/gui/actions/InvertSelectionAction.java new file mode 100644 index 0000000000..513f9223f3 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/InvertSelectionAction.java @@ -0,0 +1,20 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class InvertSelectionAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public InvertSelectionAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Auswahl umkehren"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.getTableComponent().invertSelection(); + } +} diff --git a/src/main/java/mediathek/gui/actions/LoadFilmListAction.java b/src/main/java/mediathek/gui/actions/LoadFilmListAction.java index d6711cdd61..c1a6192193 100644 --- a/src/main/java/mediathek/gui/actions/LoadFilmListAction.java +++ b/src/main/java/mediathek/gui/actions/LoadFilmListAction.java @@ -1,8 +1,7 @@ package mediathek.gui.actions; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import java.awt.event.ActionEvent; @@ -14,8 +13,9 @@ public class LoadFilmListAction extends AbstractAction { public LoadFilmListAction(MediathekGui mediathekGui) { this.mediathekGui = mediathekGui; putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0)); - putValue(Action.SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.CLOUD_DOWNLOAD, 16)); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/cloud-arrow-down.svg")); putValue(Action.NAME, "Neue Filmliste laden..."); + putValue(Action.SHORT_DESCRIPTION, "Neue Filmliste laden"); } @Override public void actionPerformed(ActionEvent e) { diff --git a/src/main/java/mediathek/gui/actions/ManageAboAction.kt b/src/main/java/mediathek/gui/actions/ManageAboAction.kt index 061cdb79c8..4807afd795 100644 --- a/src/main/java/mediathek/gui/actions/ManageAboAction.kt +++ b/src/main/java/mediathek/gui/actions/ManageAboAction.kt @@ -1,9 +1,8 @@ package mediathek.gui.actions -import jiconfont.icons.font_awesome.FontAwesome -import jiconfont.swing.IconFontSwing import mediathek.gui.abo.ManageAboDialog import mediathek.mainwindow.MediathekGui +import mediathek.tool.SVGIconUtilities import java.awt.event.ActionEvent import javax.swing.AbstractAction @@ -22,6 +21,7 @@ class ManageAboAction : AbstractAction() { init { putValue(NAME, "Abos verwalten...") - putValue(SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.DATABASE, 16f)) + putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/database.svg")) + putValue(SHORT_DESCRIPTION, "Abos verwalten") } } \ No newline at end of file diff --git a/src/main/java/mediathek/gui/actions/ManageBookmarkAction.java b/src/main/java/mediathek/gui/actions/ManageBookmarkAction.java new file mode 100644 index 0000000000..a3c54617f3 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/ManageBookmarkAction.java @@ -0,0 +1,24 @@ +package mediathek.gui.actions; + +import javafx.application.Platform; +import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class ManageBookmarkAction extends AbstractAction { + private final MediathekGui mediathekGui; + + public ManageBookmarkAction(MediathekGui mediathekGui) { + this.mediathekGui = mediathekGui; + putValue(Action.NAME, "Merkliste verwalten..."); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/file-lines.svg")); + putValue(Action.SHORT_DESCRIPTION, "Merkliste verwalten"); + } + + @Override + public void actionPerformed(ActionEvent e) { + Platform.runLater(() -> mediathekGui.tabFilme.showBookmarkWindow()); + } +} diff --git a/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java b/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java index efdc16f808..a45cacb4af 100644 --- a/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java +++ b/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java @@ -1,35 +1,35 @@ package mediathek.gui.actions; -import mediathek.javafx.MemoryMonitor; -import mediathek.javafx.tool.JavaFxUtils; +import mediathek.gui.dialog.MemoryMonitorDialog; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.event.ActionEvent; public class MemoryMonitorAction extends AbstractAction { - private MemoryMonitor memoryMonitor; + private MemoryMonitorDialog dialog; + private final JFrame parent; - public MemoryMonitorAction() { - putValue(Action.NAME,"Speicherverbrauch anzeigen"); + public MemoryMonitorAction(@NotNull JFrame parent) { + this.parent = parent; + putValue(Action.NAME, "Speicherverbrauch anzeigen"); } public void closeMemoryMonitor() { - if (memoryMonitor != null) - JavaFxUtils.invokeInFxThreadAndWait(() -> memoryMonitor.close()); + if (dialog != null) { + dialog.dispose(); + } } public void showMemoryMonitor() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - if (memoryMonitor == null) { - memoryMonitor = new MemoryMonitor(); - } - - memoryMonitor.show(); - }); + if (dialog == null) + dialog = new MemoryMonitorDialog(parent); + dialog.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { showMemoryMonitor(); } + } diff --git a/src/main/java/mediathek/gui/actions/OpenTargetFolderAction.java b/src/main/java/mediathek/gui/actions/OpenTargetFolderAction.java new file mode 100644 index 0000000000..ea46d8d4af --- /dev/null +++ b/src/main/java/mediathek/gui/actions/OpenTargetFolderAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class OpenTargetFolderAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public OpenTargetFolderAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Zielordner öffnen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.zielordnerOeffnen(); + } +} diff --git a/src/main/java/mediathek/gui/actions/PlayDownloadAction.java b/src/main/java/mediathek/gui/actions/PlayDownloadAction.java new file mode 100644 index 0000000000..e260aab89c --- /dev/null +++ b/src/main/java/mediathek/gui/actions/PlayDownloadAction.java @@ -0,0 +1,23 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class PlayDownloadAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public PlayDownloadAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Gespeicherten Film abspielen"); + putValue(Action.SHORT_DESCRIPTION, "Film abspielen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.filmAbspielen(); + } +} diff --git a/src/main/java/mediathek/gui/actions/PlayFilmAction.java b/src/main/java/mediathek/gui/actions/PlayFilmAction.java new file mode 100644 index 0000000000..025bf62fba --- /dev/null +++ b/src/main/java/mediathek/gui/actions/PlayFilmAction.java @@ -0,0 +1,45 @@ +package mediathek.gui.actions; + +import mediathek.config.Daten; +import mediathek.config.Konstanten; +import mediathek.daten.DatenPset; +import mediathek.gui.tabs.tab_film.GuiFilme; +import mediathek.mainwindow.MediathekGui; +import mediathek.tool.GuiFunktionen; +import mediathek.tool.SVGIconUtilities; +import org.apache.commons.lang3.SystemUtils; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +public class PlayFilmAction extends AbstractAction { + private final GuiFilme guiFilme; + + public PlayFilmAction(GuiFilme guiFilme) { + this.guiFilme = guiFilme; + putValue(Action.NAME, "Film abspielen"); + putValue(Action.SHORT_DESCRIPTION, "Film abspielen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg")); + KeyStroke keyStroke; + if (SystemUtils.IS_OS_MAC_OSX) + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F6, GuiFunktionen.getPlatformControlKey()); + else + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_P, GuiFunktionen.getPlatformControlKey()); + putValue(Action.ACCELERATOR_KEY, keyStroke); + } + + @Override + public synchronized void actionPerformed(ActionEvent e) { + DatenPset pset = Daten.listePset.getPsetAbspielen(); + if (pset != null) { + guiFilme.playerStarten(pset); + } else { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Es wurde kein Videoplayer eingerichtet.\n" + + "Bitte legen Sie diesen unter \"Einstellungen->Set bearbeiten\" fest.", + Konstanten.PROGRAMMNAME, + JOptionPane.INFORMATION_MESSAGE); + } + } +} diff --git a/src/main/java/mediathek/gui/actions/QuitAction.java b/src/main/java/mediathek/gui/actions/QuitAction.java index 170f7c5448..bf55a37f73 100644 --- a/src/main/java/mediathek/gui/actions/QuitAction.java +++ b/src/main/java/mediathek/gui/actions/QuitAction.java @@ -17,6 +17,6 @@ public QuitAction(MediathekGui mediathekGui) { @Override public void actionPerformed(ActionEvent e) { - mediathekGui.beenden(false, false); + mediathekGui.quitApplication(); } } diff --git a/src/main/java/mediathek/gui/actions/RefreshDownloadListAction.java b/src/main/java/mediathek/gui/actions/RefreshDownloadListAction.java new file mode 100644 index 0000000000..2e20b0298d --- /dev/null +++ b/src/main/java/mediathek/gui/actions/RefreshDownloadListAction.java @@ -0,0 +1,25 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +public class RefreshDownloadListAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public RefreshDownloadListAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Liste der Downloads aktualisieren"); + putValue(Action.SHORT_DESCRIPTION, "Downloadliste aktualisieren"); + putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK)); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/arrows-rotate.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.updateDownloads(); + } +} diff --git a/src/main/java/mediathek/gui/actions/ResetFilterDialogPosition.kt b/src/main/java/mediathek/gui/actions/ResetFilterDialogPosition.kt new file mode 100644 index 0000000000..6f1359c0eb --- /dev/null +++ b/src/main/java/mediathek/gui/actions/ResetFilterDialogPosition.kt @@ -0,0 +1,15 @@ +package mediathek.gui.actions + +import mediathek.mainwindow.MediathekGui +import java.awt.event.ActionEvent +import javax.swing.AbstractAction + +class ResetFilterDialogPosition(private val mediathekGui: MediathekGui) : AbstractAction() { + init { + putValue(NAME, "Filterdialog-Position zurücksetzen") + } + + override fun actionPerformed(e: ActionEvent) { + mediathekGui.tabFilme?.filmActionPanel?.filterDialog?.setLocation(100, 100) + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/actions/SetAppMemoryAction.kt b/src/main/java/mediathek/gui/actions/SetAppMemoryAction.kt deleted file mode 100644 index f4cb7ef19c..0000000000 --- a/src/main/java/mediathek/gui/actions/SetAppMemoryAction.kt +++ /dev/null @@ -1,78 +0,0 @@ -package mediathek.gui.actions - -import mediathek.config.Konstanten -import mediathek.mainwindow.MediathekGui -import mediathek.tool.GuiFunktionenProgramme -import mediathek.tool.javafx.FXErrorDialog -import okio.buffer -import okio.sink -import org.apache.commons.lang3.SystemUtils -import org.apache.logging.log4j.LogManager -import java.awt.Desktop -import java.awt.event.ActionEvent -import java.io.File -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import javax.swing.AbstractAction -import javax.swing.JOptionPane - -class SetAppMemoryAction : AbstractAction() { - override fun actionPerformed(e: ActionEvent) { - val fileName: String - val optionsPath: Path - if (SystemUtils.IS_OS_MAC_OSX) { - //on macOS we cannot write into app bundle, we have to use a file in our config dir.. - fileName = MAC_VMOPTIONS_FILE - optionsPath = Paths.get(fileName) - if (Files.notExists(optionsPath)) { - //create dummy file - try { - optionsPath.sink().buffer().use { sink -> - sink.writeUtf8("### SEIEN SIE VORSICHTIG! FEHLERHAFTE EINTRÄGE VERHINDERN DEN START DER ANWENDUNG!\n") - sink.writeUtf8("-Xmx2G") - } - } catch (ex: IOException) { - logger.error(ex) - FXErrorDialog.showErrorDialogWithoutParent(Konstanten.PROGRAMMNAME, "Schreibvorgang fehlgeschlagen", - """ - Die Datei '${optionsPath.normalize().toAbsolutePath()}' konnte nicht geschrieben werden. - Bitte wenden Sie sich bei Fragen an das Forum! - """.trimIndent(), ex) - } - } - } else { - fileName = WIN_VMOPTIONS_FILE - optionsPath = Paths.get(GuiFunktionenProgramme.getPathToApplicationJar(), fileName) - } - val fileStr = optionsPath.normalize().toAbsolutePath().toString() - if (Desktop.isDesktopSupported()) { - try { - Desktop.getDesktop().open(optionsPath.normalize().toAbsolutePath().toFile()) - } catch (ex: Exception) { - logger.error("Failed to open vm options file", ex) - FXErrorDialog.showErrorDialogWithoutParent(Konstanten.PROGRAMMNAME, "Datei konnte nicht geöffnet werden", - """ - Es trat ein Fehler beim Öffnen der Datei auf. - Diese Funktion wird nur durch die offiziellen MediathekView-Apps unterstützt. - Bitte wenden Sie sich mit dem unten sichtbaren Fehler an das Forum. - """.trimIndent(), ex) - } - } else { - logger.error("Desktop is not supported") - JOptionPane.showMessageDialog(MediathekGui.ui(), "Datei konnte nicht geöffnet werden, da Java auf Ihrem System das Öffnen nicht unterstützt.
" + - "Bitte bearbeiten Sie manuell die Datei '" + fileStr + "'") - } - } - - companion object { - private const val WIN_VMOPTIONS_FILE = "MediathekView.vmoptions" - private val MAC_VMOPTIONS_FILE = SystemUtils.USER_HOME + File.separator + ".mediathek3" + File.separator + "MediathekView_vmoptions.txt" - private val logger = LogManager.getLogger() - } - - init { - putValue(NAME, "Speicherzuweisung ändern...") - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/actions/SettingsAction.java b/src/main/java/mediathek/gui/actions/SettingsAction.java index 639d0036b2..7ba80b7210 100644 --- a/src/main/java/mediathek/gui/actions/SettingsAction.java +++ b/src/main/java/mediathek/gui/actions/SettingsAction.java @@ -1,8 +1,7 @@ package mediathek.gui.actions; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import java.awt.event.ActionEvent; @@ -15,7 +14,8 @@ public SettingsAction(MediathekGui mediathekGui) { this.mediathekGui = mediathekGui; putValue(Action.NAME, "Einstellungen..."); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0)); - putValue(Action.SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.COGS, 16)); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/gears.svg")); + putValue(Action.SHORT_DESCRIPTION, "Einstellungen öffnen"); } @Override public void actionPerformed(ActionEvent e) { diff --git a/src/main/java/mediathek/gui/actions/ShowAboutAction.kt b/src/main/java/mediathek/gui/actions/ShowAboutAction.kt index 0075ec5650..c7d6b90b84 100644 --- a/src/main/java/mediathek/gui/actions/ShowAboutAction.kt +++ b/src/main/java/mediathek/gui/actions/ShowAboutAction.kt @@ -1,6 +1,6 @@ package mediathek.gui.actions -import mediathek.gui.dialog.about.AboutDialog +import mediathek.gui.dialog.AboutDialog import mediathek.mainwindow.MediathekGui import mediathek.tool.GuiFunktionen import java.awt.event.ActionEvent diff --git a/src/main/java/mediathek/gui/actions/ShowBandwidthUsageAction.kt b/src/main/java/mediathek/gui/actions/ShowBandwidthUsageAction.kt new file mode 100644 index 0000000000..bb097f37e3 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/ShowBandwidthUsageAction.kt @@ -0,0 +1,20 @@ +package mediathek.gui.actions + +import mediathek.gui.bandwidth.BandwidthDialog +import mediathek.mainwindow.MediathekGui +import java.awt.event.ActionEvent +import java.util.* +import javax.swing.AbstractAction + +class ShowBandwidthUsageAction(private val mediathekGui: MediathekGui) : AbstractAction() { + var dialogOptional = Optional.empty() + + init { + putValue(NAME, "Bandbreitennutzung") + } + + override fun actionPerformed(e: ActionEvent?) { + val dialog = BandwidthDialog(mediathekGui, this) + dialog.isVisible = true + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/actions/ShowBlacklistDialogAction.java b/src/main/java/mediathek/gui/actions/ShowBlacklistDialogAction.java deleted file mode 100644 index 67fb9cb7c4..0000000000 --- a/src/main/java/mediathek/gui/actions/ShowBlacklistDialogAction.java +++ /dev/null @@ -1,31 +0,0 @@ -package mediathek.gui.actions; - -import mediathek.config.Daten; -import mediathek.gui.dialog.DialogLeer; -import mediathek.gui.dialogEinstellungen.PanelBlacklist; -import mediathek.mainwindow.MediathekGui; -import mediathek.res.GetIcon; - -import javax.swing.*; -import java.awt.event.ActionEvent; - -public class ShowBlacklistDialogAction extends AbstractAction { - private static final String PANEL_BLACKLIST_NAME_POSTFIX = "_2"; - private final JFrame parent; - private final Daten daten; - - public ShowBlacklistDialogAction(JFrame parent, Daten daten) { - this.daten = daten; - this.parent = parent; - - putValue(NAME, "Blacklist öffnen..."); - putValue(SMALL_ICON, GetIcon.getProgramIcon("menue-blacklist.png", 16, 16)); - } - - @Override - public void actionPerformed(ActionEvent e) { - DialogLeer dialog = new DialogLeer(parent, true); - dialog.init("Blacklist", new PanelBlacklist(daten, MediathekGui.ui(), PanelBlacklist.class.getName() + PANEL_BLACKLIST_NAME_POSTFIX)); - dialog.setVisible(true); - } -} diff --git a/src/main/java/mediathek/gui/actions/ShowFilmInformationAction.java b/src/main/java/mediathek/gui/actions/ShowFilmInformationAction.java index 5b93f71969..bd79cda089 100644 --- a/src/main/java/mediathek/gui/actions/ShowFilmInformationAction.java +++ b/src/main/java/mediathek/gui/actions/ShowFilmInformationAction.java @@ -2,6 +2,7 @@ import mediathek.mainwindow.MediathekGui; import mediathek.tool.GuiFunktionen; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import java.awt.event.ActionEvent; @@ -9,11 +10,13 @@ public class ShowFilmInformationAction extends AbstractAction { - public ShowFilmInformationAction(boolean showAccelerator) { - putValue(NAME,"Filminformationen anzeigen"); - if (showAccelerator) - putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_I, GuiFunktionen.getPlatformControlKey())); + public ShowFilmInformationAction() { + putValue(Action.NAME, "Filminformation anzeigen"); + putValue(Action.SHORT_DESCRIPTION, "Filminformation anzeigen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-info.svg", 14f)); + putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_I, GuiFunktionen.getPlatformControlKey())); } + @Override public void actionPerformed(ActionEvent e) { if (!MediathekGui.ui().getFilmInfoDialog().isVisible()) { diff --git a/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java b/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java index 040991dc09..0d1c9ea79a 100644 --- a/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java +++ b/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java @@ -1,32 +1,43 @@ package mediathek.gui.actions; -import javafx.application.Platform; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Konstanten; import mediathek.mainwindow.MediathekGui; -import mediathek.tool.javafx.FXErrorDialog; +import mediathek.tool.SVGIconUtilities; +import mediathek.tool.SwingErrorDialog; import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; +import java.io.IOException; +import java.net.URI; import java.net.URISyntaxException; public class ShowOnlineHelpAction extends AbstractAction { public ShowOnlineHelpAction() { super(); putValue(NAME, "Online-Hilfe anzeigen..."); - putValue(SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); } @Override public void actionPerformed(ActionEvent e) { - try { - UrlHyperlinkAction.openURL(MediathekGui.ui(),Konstanten.ADRESSE_ONLINE_HELP); - } catch (URISyntaxException ex) { - Platform.runLater(() -> FXErrorDialog.showErrorDialog("Online-Hilfe", - "Fehler beim Öffnen der Online-Hilfe", - "Es trat ein Fehler beim Öffnen der Online-Hilfe auf.\nSollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", - ex)); + if (Desktop.isDesktopSupported()) { + Desktop d = Desktop.getDesktop(); + if (d.isSupported(Desktop.Action.BROWSE)) { + try { + d.browse(new URI(Konstanten.ADRESSE_ONLINE_HELP)); + } catch (IOException | URISyntaxException ex) { + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), + "Es trat ein Fehler beim Öffnen der Online-Hilfe auf.\nSollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", + ex); + } + } + else { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Ihr Betriebssystem unterstützt das Öffnen des Browsers nicht.
" + + "Bitte öffnen Sie " + Konstanten.ADRESSE_ONLINE_HELP + " selbst in Ihrem Browser.", + Konstanten.PROGRAMMNAME, JOptionPane.ERROR_MESSAGE); + } } } } diff --git a/src/main/java/mediathek/gui/actions/StartAllDownloadsAction.java b/src/main/java/mediathek/gui/actions/StartAllDownloadsAction.java new file mode 100644 index 0000000000..d7ea04c425 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StartAllDownloadsAction.java @@ -0,0 +1,23 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StartAllDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public StartAllDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/angles-down.svg")); + putValue(Action.SHORT_DESCRIPTION, "Alle Downloads starten"); + putValue(Action.NAME, "Alle Downloads starten"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.starten(true); + } +} diff --git a/src/main/java/mediathek/gui/actions/StartAllDownloadsTimedAction.java b/src/main/java/mediathek/gui/actions/StartAllDownloadsTimedAction.java new file mode 100644 index 0000000000..36f7cdaa09 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StartAllDownloadsTimedAction.java @@ -0,0 +1,20 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StartAllDownloadsTimedAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public StartAllDownloadsTimedAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Alle Downloads zeitverzögert starten..."); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.startAllDownloadsAtSpecificTime(); + } +} diff --git a/src/main/java/mediathek/gui/actions/StartDownloadsAction.java b/src/main/java/mediathek/gui/actions/StartDownloadsAction.java new file mode 100644 index 0000000000..cbd2b168e0 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StartDownloadsAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StartDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public StartDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Ausgewählte Downloads starten"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/caret-down.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.starten(false); + } +} diff --git a/src/main/java/mediathek/gui/actions/StopAllDownloadsAction.java b/src/main/java/mediathek/gui/actions/StopAllDownloadsAction.java new file mode 100644 index 0000000000..04d4c66cce --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StopAllDownloadsAction.java @@ -0,0 +1,21 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StopAllDownloadsAction extends AbstractAction { + + private final GuiDownloads guiDownloads; + + public StopAllDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Alle Downloads stoppen"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.stoppen(true); + } +} diff --git a/src/main/java/mediathek/gui/actions/StopAllWaitingDownloadsAction.java b/src/main/java/mediathek/gui/actions/StopAllWaitingDownloadsAction.java new file mode 100644 index 0000000000..69150dafef --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StopAllWaitingDownloadsAction.java @@ -0,0 +1,20 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StopAllWaitingDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public StopAllWaitingDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Wartende Downloads stoppen"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.stopAllWaitingDownloads(); + } +} diff --git a/src/main/java/mediathek/gui/actions/StopDownloadsAction.java b/src/main/java/mediathek/gui/actions/StopDownloadsAction.java new file mode 100644 index 0000000000..1d06c05ce0 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StopDownloadsAction.java @@ -0,0 +1,20 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StopDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public StopDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Ausgewählte Downloads stoppen"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.stoppen(false); + } +} diff --git a/src/main/java/mediathek/gui/actions/ToggleBlacklistAction.java b/src/main/java/mediathek/gui/actions/ToggleBlacklistAction.java new file mode 100644 index 0000000000..7d4d530c3b --- /dev/null +++ b/src/main/java/mediathek/gui/actions/ToggleBlacklistAction.java @@ -0,0 +1,61 @@ +package mediathek.gui.actions; + +import com.formdev.flatlaf.extras.FlatSVGIcon; +import mediathek.config.Daten; +import mediathek.config.MVConfig; +import mediathek.gui.messages.BlacklistChangedEvent; +import mediathek.tool.MessageBus; +import mediathek.tool.SVGIconUtilities; +import net.engio.mbassy.listener.Handler; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; + +public class ToggleBlacklistAction extends AbstractAction { + private final FlatSVGIcon enabledIcon; + private final FlatSVGIcon disabledIcon; + private boolean blacklist_is_on; + + public ToggleBlacklistAction() { + enabledIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/list-check.svg"); + + disabledIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/list-check.svg"); + disabledIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.RED)); + + blacklist_is_on = Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_ON)); + setupState(); + + MessageBus.getMessageBus().subscribe(this); + } + + @Handler + private void handleBlacklistChangedEvent(BlacklistChangedEvent e) { + //config was changed outside + SwingUtilities.invokeLater(() -> { + blacklist_is_on = Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_ON)); + setupState(); + }); + } + + private void setupState() { + if (blacklist_is_on) { + putValue(NAME, "Blacklist ausschalten"); + putValue(SHORT_DESCRIPTION, "Blacklist ausschalten"); + putValue(SMALL_ICON, enabledIcon); + } else { + putValue(NAME, "Blacklist einschalten"); + putValue(SHORT_DESCRIPTION, "Blacklist einschalten"); + putValue(SMALL_ICON, disabledIcon); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + blacklist_is_on = !blacklist_is_on; + + MVConfig.add(MVConfig.Configs.SYSTEM_BLACKLIST_ON, Boolean.toString(blacklist_is_on)); + Daten.getInstance().getListeBlacklist().filterListe(); + MessageBus.getMessageBus().publishAsync(new BlacklistChangedEvent()); + } +} diff --git a/src/main/java/mediathek/gui/actions/ToggleDarkModeAction.kt b/src/main/java/mediathek/gui/actions/ToggleDarkModeAction.kt new file mode 100644 index 0000000000..311ea90d62 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/ToggleDarkModeAction.kt @@ -0,0 +1,37 @@ +package mediathek.gui.actions + +import com.formdev.flatlaf.FlatLaf +import com.formdev.flatlaf.extras.FlatAnimatedLafChange +import mediathek.gui.messages.DarkModeChangeEvent +import mediathek.mainwindow.MediathekGui +import mediathek.tool.* +import java.awt.event.ActionEvent +import javax.swing.AbstractAction +import javax.swing.LookAndFeel + +class ToggleDarkModeAction : AbstractAction() { + init { + putValue(SHORT_DESCRIPTION, "Dunkelmodus ein-/ausschalten") + putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-half-stroke.svg")) + } + + override fun actionPerformed(e: ActionEvent) { + FlatAnimatedLafChange.showSnapshot() + + val laf: LookAndFeel = if (!FlatLaf.isLafDark()) { + DarkModeFactory.lookAndFeel + } else { + LightModeFactory.lookAndFeel + } + FlatLaf.setup(laf) + MediathekGui.ui().setupAlternatingRowColors() + // update all components + FlatLaf.updateUI() + + FlatAnimatedLafChange.hideSnapshotWithAnimation() + ApplicationConfiguration.getConfiguration() + .setProperty(ApplicationConfiguration.APPLICATION_DARK_MODE, FlatLaf.isLafDark()) + + MessageBus.messageBus.publishAsync(DarkModeChangeEvent()) + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/actions/export/DiskSpaceUtil.kt b/src/main/java/mediathek/gui/actions/export/DiskSpaceUtil.kt new file mode 100644 index 0000000000..7bda8e559d --- /dev/null +++ b/src/main/java/mediathek/gui/actions/export/DiskSpaceUtil.kt @@ -0,0 +1,35 @@ +package mediathek.gui.actions.export + +import org.apache.logging.log4j.LogManager +import java.io.File +import java.nio.file.Files + +internal object DiskSpaceUtil { + private val logger = LogManager.getLogger() + + /** + * Check if there is more than 1 GB of free disk space available. + */ + fun enoughDiskSpace(selectedFile: File): Boolean { + try { + var tempFileCreated = false + + if (!selectedFile.exists()) { + selectedFile.createNewFile() + tempFileCreated = true + } + val store = Files.getFileStore(selectedFile.toPath()) + val availFreeSpace = store.usableSpace / (1024 * 1024) //Megabyte + + if (tempFileCreated) + selectedFile.delete() + + return availFreeSpace >= 1024 + } + catch (e: Exception) { + // in case of error pretend there is space available as it may fail on network drives + logger.error("enoughDiskSpace failed!", e) + return true + } + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/actions/export/ExportDecompressedFilmlistAction.kt b/src/main/java/mediathek/gui/actions/export/ExportDecompressedFilmlistAction.kt new file mode 100644 index 0000000000..4aa0e98387 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/export/ExportDecompressedFilmlistAction.kt @@ -0,0 +1,52 @@ +package mediathek.gui.actions.export + +import mediathek.config.Konstanten +import mediathek.mainwindow.MediathekGui +import mediathek.tool.FileDialogs.Companion.chooseSaveFileLocation +import java.awt.event.ActionEvent +import java.beans.PropertyChangeEvent +import javax.swing.AbstractAction +import javax.swing.JOptionPane +import javax.swing.ProgressMonitor + + +/** + * Exports the current film list to JSON file. + */ +class ExportDecompressedFilmlistAction : AbstractAction() { + init { + putValue(NAME, "Dekomprimierte Filmliste...") + } + + override fun actionPerformed(e: ActionEvent) { + isEnabled = false + val monitor = ProgressMonitor(MediathekGui.ui(), "Exportiere Filmliste", "", 0, 100) + monitor.millisToPopup = 100 + monitor.millisToDecideToPopup = 100 + val selectedFile = chooseSaveFileLocation(MediathekGui.ui(), "Lesbare Filmliste sichern", "") + if (selectedFile != null) { + if (!DiskSpaceUtil.enoughDiskSpace(selectedFile)) { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Nicht genügend freier Speicher auf dem gewählten Laufwerk.\nVorgang wurde abgebrochen.", + Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE) + return + } + + val worker = FilmlistExportWorker(this, selectedFile, compressSender = false, compressThema = false) + worker.addPropertyChangeListener { evt: PropertyChangeEvent -> + if ("progress" == evt.propertyName) { + val progress = evt.newValue as Int + monitor.setProgress(progress) + } + } + worker.execute() + } else { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Export wurde abgebrochen", + Konstanten.PROGRAMMNAME, + JOptionPane.WARNING_MESSAGE) + isEnabled = true + } + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/actions/export/ExportReadableFilmlistAction.kt b/src/main/java/mediathek/gui/actions/export/ExportReadableFilmlistAction.kt new file mode 100644 index 0000000000..dd658e0b44 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/export/ExportReadableFilmlistAction.kt @@ -0,0 +1,50 @@ +package mediathek.gui.actions.export + +import mediathek.config.Konstanten +import mediathek.mainwindow.MediathekGui +import mediathek.tool.FileDialogs.Companion.chooseSaveFileLocation +import java.awt.event.ActionEvent +import java.beans.PropertyChangeEvent +import javax.swing.AbstractAction +import javax.swing.JOptionPane +import javax.swing.ProgressMonitor + +/** + * Exports the current film list to JSON file. + */ +class ExportReadableFilmlistAction : AbstractAction() { + init { + putValue(NAME, "Lesbare Filmliste...") + } + + override fun actionPerformed(e: ActionEvent) { + isEnabled = false + val monitor = ProgressMonitor(MediathekGui.ui(), "Exportiere Filmliste", "", 0, 100) + monitor.millisToPopup = 100 + monitor.millisToDecideToPopup = 100 + val selectedFile = chooseSaveFileLocation(MediathekGui.ui(), "Lesbare Filmliste sichern", "") + if (selectedFile != null) { + if (!DiskSpaceUtil.enoughDiskSpace(selectedFile)) { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Nicht genügend freier Speicher auf dem gewählten Laufwerk.\nVorgang wurde abgebrochen.", + Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE) + return + } + val worker = FilmlistExportWorker(this, selectedFile, compressSender = true, compressThema = true) + worker.addPropertyChangeListener { evt: PropertyChangeEvent -> + if ("progress" == evt.propertyName) { + val progress = evt.newValue as Int + monitor.setProgress(progress) + } + } + worker.execute() + } else { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Export wurde abgebrochen", + Konstanten.PROGRAMMNAME, + JOptionPane.WARNING_MESSAGE) + isEnabled = true + } + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/actions/export/FilmListExportAction.java b/src/main/java/mediathek/gui/actions/export/FilmListExportAction.java deleted file mode 100644 index 8ab4127d7e..0000000000 --- a/src/main/java/mediathek/gui/actions/export/FilmListExportAction.java +++ /dev/null @@ -1,81 +0,0 @@ -package mediathek.gui.actions.export; - -import javafx.scene.control.Alert; -import javafx.stage.Modality; -import mediathek.config.Konstanten; -import mediathek.javafx.tool.FXProgressPane; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.mainwindow.MediathekGui; -import mediathek.tool.FileDialogs; -import org.controlsfx.control.StatusBar; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.io.File; -import java.util.concurrent.CompletableFuture; - -/** - * Exports the current film list to JSON file. - */ -public class FilmListExportAction extends AbstractAction { - private final static String HEADER = "Export der Filmliste"; - private final MediathekGui gui; - - public FilmListExportAction(MediathekGui gui) { - super(); - this.gui = gui; - - putValue(NAME, "Lesbare Filmliste..."); - } - - private void export(File selectedFile) { - StatusBar bar = gui.getStatusBarController().getStatusBar(); - FXProgressPane hb = new FXProgressPane(); - - FilmListExportWorkerTask task = new FilmListExportWorkerTask(selectedFile, true); - task.setOnSucceeded(e -> { - bar.getRightItems().remove(hb); - showSuccess(); - }); - task.setOnFailed(e -> { - bar.getRightItems().remove(hb); - showError(); - }); - - bar.getRightItems().add(hb); - hb.prog.progressProperty().bind(task.progressProperty()); - - CompletableFuture.runAsync(task); - } - - private void showError() { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText(HEADER); - alert.setContentText("Es gab einen Fehler beim Export der Filmliste."); - alert.initModality(Modality.APPLICATION_MODAL); - alert.showAndWait(); - } - - private void showSuccess() { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText(HEADER); - alert.setContentText("Der Export wurde erfolgreich beendet."); - alert.initModality(Modality.APPLICATION_MODAL); - alert.showAndWait(); - } - - @Override - public void actionPerformed(ActionEvent e) { - setEnabled(false); - - var selectedFile = FileDialogs.chooseSaveFileLocation(gui,"Lesbare Filmliste sichern",""); - if (selectedFile != null) { - JavaFxUtils.invokeInFxThreadAndWait(() -> export(selectedFile)); - } - - setEnabled(true); - } - -} diff --git a/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java b/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java deleted file mode 100644 index 9262003ca2..0000000000 --- a/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java +++ /dev/null @@ -1,30 +0,0 @@ -package mediathek.gui.actions.export; - -import javafx.concurrent.Task; -import mediathek.config.Daten; -import mediathek.filmlisten.writer.FilmListWriter; - -import java.io.File; - -/** - * JavaFX worker task which will export the filmlist and handle progress reporting. - */ -class FilmListExportWorkerTask extends Task { - private final File selectedFile; - private final boolean readable; - - public FilmListExportWorkerTask(File selectedFile, boolean readable) { - super(); - this.selectedFile = selectedFile; - this.readable = readable; - } - - @Override - protected Void call() { - FilmListWriter writer = new FilmListWriter(readable); - writer.writeFilmList(selectedFile.getAbsolutePath(), - Daten.getInstance().getListeFilme(), - prog -> updateProgress(prog, 1d)); - return null; - } -} diff --git a/src/main/java/mediathek/gui/actions/export/FilmlistExportWorker.kt b/src/main/java/mediathek/gui/actions/export/FilmlistExportWorker.kt new file mode 100644 index 0000000000..739f5e8b5e --- /dev/null +++ b/src/main/java/mediathek/gui/actions/export/FilmlistExportWorker.kt @@ -0,0 +1,66 @@ +package mediathek.gui.actions.export + +import mediathek.config.Daten +import mediathek.config.Konstanten +import mediathek.filmlisten.writer.FilmListWriter +import mediathek.mainwindow.MediathekGui +import java.io.File +import javax.swing.AbstractAction +import javax.swing.JOptionPane +import javax.swing.SwingWorker +import kotlin.math.roundToInt + +class FilmlistExportWorker(private val exportAction: AbstractAction, selectedFile: File, + compressSender: Boolean, compressThema: Boolean) : SwingWorker() { + private val selectedFile: File? + private val compressSender: Boolean + private val compressThema: Boolean + + init { + this.selectedFile = selectedFile + this.compressSender = compressSender + this.compressThema = compressThema + } + + private fun showError() { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Es gab einen Fehler beim Export der Filmliste.", + Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE) + } + + private fun showSuccess() { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Der Export wurde erfolgreich abgeschlossen.", + Konstanten.PROGRAMMNAME, + JOptionPane.INFORMATION_MESSAGE) + } + + override fun done() { + try { + val result = get() + if (result!!) + showSuccess() + else + showError() + } + catch (e: Exception) { + showError() + } + exportAction.isEnabled = true + } + + @Throws(Exception::class) + override fun doInBackground(): Boolean { + if (selectedFile != null) { + val writer = FilmListWriter(true) + writer.setCompressSenderTag(compressSender) + writer.setCompressThemaTag(compressThema) + writer.setDecompressUrls(true) + writer.writeFilmList(selectedFile.absolutePath, + Daten.getInstance().listeFilme) + { prog: Double -> progress = (100.0 * prog).roundToInt() } + } + return true + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/actions/import_actions/ImportOldAbosAction.java b/src/main/java/mediathek/gui/actions/import_actions/ImportOldAbosAction.java deleted file mode 100644 index 31255c0246..0000000000 --- a/src/main/java/mediathek/gui/actions/import_actions/ImportOldAbosAction.java +++ /dev/null @@ -1,45 +0,0 @@ -package mediathek.gui.actions.import_actions; - -import javafx.application.Platform; -import javafx.scene.control.Alert; -import javafx.stage.FileChooser; -import mediathek.config.Konstanten; -import mediathek.tool.javafx.FXErrorDialog; - -import javax.swing.*; -import java.awt.event.ActionEvent; - -public class ImportOldAbosAction extends AbstractAction { - public ImportOldAbosAction() { - putValue(Action.NAME, "Alte Abos..."); - putValue(Action.SHORT_DESCRIPTION, "Ermöglicht den Import der Abos aus einer alten Konfigurationsdatei."); - } - @Override - public void actionPerformed(ActionEvent e) { - Platform.runLater(() -> { - var fileChooser = new FileChooser(); - fileChooser.setTitle("Konfigurationsdatei öffnen"); - var selectedFile = fileChooser.showOpenDialog(null); - if (selectedFile != null) { - try { - var configReader = new OldConfigFileImporter(); - var result = configReader.importAboBlacklist(selectedFile.getAbsolutePath(), true, false, false); - var alert = new ImportSettingsAlert(Alert.AlertType.INFORMATION); - String text = "Es wurden " + result.left + " Einträge importiert."; - alert.setContentText(text); - alert.showAndWait(); - } catch (Exception ex) { - Platform.runLater(() -> FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, - "Fehler beim Importieren der Abos", - "Es trat ein Fehler beim Import der Abos auf.\n" + - "Sollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", - ex)); - } - } else { - var alert = new ImportSettingsAlert(Alert.AlertType.WARNING); - alert.setContentText("Der Import der Abos wurde abgebrochen."); - alert.showAndWait(); - } - }); - } -} diff --git a/src/main/java/mediathek/gui/actions/import_actions/ImportOldBlacklistAction.java b/src/main/java/mediathek/gui/actions/import_actions/ImportOldBlacklistAction.java deleted file mode 100644 index 8a0c809b54..0000000000 --- a/src/main/java/mediathek/gui/actions/import_actions/ImportOldBlacklistAction.java +++ /dev/null @@ -1,46 +0,0 @@ -package mediathek.gui.actions.import_actions; - -import javafx.application.Platform; -import javafx.scene.control.Alert; -import javafx.stage.FileChooser; -import mediathek.config.Konstanten; -import mediathek.tool.javafx.FXErrorDialog; - -import javax.swing.*; -import java.awt.event.ActionEvent; - -public class ImportOldBlacklistAction extends AbstractAction { - public ImportOldBlacklistAction() { - putValue(Action.NAME, "Alte Blacklist..."); - putValue(Action.SHORT_DESCRIPTION, "Ermöglicht den Import der Blacklist aus einer alten Konfigurationsdatei."); - } - - @Override - public void actionPerformed(ActionEvent e) { - Platform.runLater(() -> { - var fileChooser = new FileChooser(); - fileChooser.setTitle("Konfigurationsdatei öffnen"); - var selectedFile = fileChooser.showOpenDialog(null); - if (selectedFile != null) { - try { - var configReader = new OldConfigFileImporter(); - var result = configReader.importAboBlacklist(selectedFile.getAbsolutePath(), false, true, false); - var alert = new ImportSettingsAlert(Alert.AlertType.INFORMATION); - String text = "Es wurden " + result.middle + " Einträge importiert."; - alert.setContentText(text); - alert.showAndWait(); - } catch (Exception ex) { - Platform.runLater(() -> FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, - "Fehler beim Importieren der Blacklist", - "Es trat ein Fehler beim Import der Blacklist auf.\n" + - "Sollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", - ex)); - } - } else { - var alert = new ImportSettingsAlert(Alert.AlertType.WARNING); - alert.setContentText("Der Import der Blacklist wurde abgebrochen."); - alert.showAndWait(); - } - }); - } -} diff --git a/src/main/java/mediathek/gui/actions/import_actions/ImportOldReplacementListAction.java b/src/main/java/mediathek/gui/actions/import_actions/ImportOldReplacementListAction.java deleted file mode 100644 index 8efcdf2c2c..0000000000 --- a/src/main/java/mediathek/gui/actions/import_actions/ImportOldReplacementListAction.java +++ /dev/null @@ -1,45 +0,0 @@ -package mediathek.gui.actions.import_actions; - -import javafx.application.Platform; -import javafx.scene.control.Alert; -import javafx.stage.FileChooser; -import mediathek.config.Konstanten; -import mediathek.tool.javafx.FXErrorDialog; - -import javax.swing.*; -import java.awt.event.ActionEvent; - -public class ImportOldReplacementListAction extends AbstractAction { - public ImportOldReplacementListAction() { - putValue(Action.NAME, "Alte Ersetzungstabelle..."); - putValue(Action.SHORT_DESCRIPTION, "Ermöglicht den Import der Ersetzungstabelle aus einer alten Konfigurationsdatei."); - } - - @Override - public void actionPerformed(ActionEvent e) { - Platform.runLater(() -> { - var fileChooser = new FileChooser(); - fileChooser.setTitle("Konfigurationsdatei öffnen"); - var selectedFile = fileChooser.showOpenDialog(null); - if (selectedFile != null) { - try { - var configReader = new OldConfigFileImporter(); - var result = configReader.importAboBlacklist(selectedFile.getAbsolutePath(), false, false, true); - var alert = new ImportSettingsAlert(Alert.AlertType.INFORMATION); - String text = "Es wurden " + result.right + " Einträge importiert."; - alert.setContentText(text); - alert.showAndWait(); - } catch (Exception ex) { - Platform.runLater(() -> FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, - "Fehler beim Importieren der Ersetzungstabelle", - "Es trat ein Fehler beim Import der Ersetzungstabelle auf.\nSollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", - ex)); - } - } else { - var alert = new ImportSettingsAlert(Alert.AlertType.WARNING); - alert.setContentText("Der Import der Ersetzungstabelle wurde abgebrochen."); - alert.showAndWait(); - } - }); - } -} diff --git a/src/main/java/mediathek/gui/actions/import_actions/ImportSettingsAlert.java b/src/main/java/mediathek/gui/actions/import_actions/ImportSettingsAlert.java deleted file mode 100644 index 89e4bc7d8b..0000000000 --- a/src/main/java/mediathek/gui/actions/import_actions/ImportSettingsAlert.java +++ /dev/null @@ -1,12 +0,0 @@ -package mediathek.gui.actions.import_actions; - -import javafx.scene.control.Alert; -import mediathek.config.Konstanten; - -class ImportSettingsAlert extends Alert { - public ImportSettingsAlert(AlertType alertType) { - super(alertType); - setTitle(Konstanten.PROGRAMMNAME); - setHeaderText("Einstellungen importieren"); - } -} diff --git a/src/main/java/mediathek/gui/bandwidth/BandwidthDialog.java b/src/main/java/mediathek/gui/bandwidth/BandwidthDialog.java new file mode 100644 index 0000000000..c75780db6d --- /dev/null +++ b/src/main/java/mediathek/gui/bandwidth/BandwidthDialog.java @@ -0,0 +1,268 @@ +/* + * Created by JFormDesigner on Tue Jan 03 13:11:50 CET 2023 + */ + +package mediathek.gui.bandwidth; + +import mediathek.gui.actions.ShowBandwidthUsageAction; +import mediathek.gui.messages.DarkModeChangeEvent; +import mediathek.tool.ApplicationConfiguration; +import mediathek.tool.FileUtils; +import mediathek.tool.MessageBus; +import mediathek.tool.http.MVHttpClient; +import net.engio.mbassy.listener.Handler; +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; +import org.apache.commons.configuration2.Configuration; +import org.apache.commons.configuration2.sync.LockMode; +import org.jetbrains.annotations.NotNull; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.DateAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYSplineRenderer; +import org.jfree.chart.ui.RectangleInsets; +import org.jfree.data.time.Millisecond; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +/** + * @author christianfranzke + */ +public class BandwidthDialog extends JDialog { + protected static final String CONFIG_X = "bandwidth_monitor.x"; + protected static final String CONFIG_Y = "bandwidth_monitor.y"; + protected static final String CONFIG_HEIGHT = "bandwidth_monitor.height"; + protected static final String CONFIG_WIDTH = "bandwidth_monitor.width"; + private static final int DEFAULT_WIDTH = 300; + private static final int DEFAULT_HEIGHT = 150; + private final TimeSeries total = new TimeSeries("Bandwidth"); + private final DateAxis dateAxis = new DateAxis(); + private final NumberAxis bandwidthAxis = new NumberAxis(); + private final BandwidthUsageDataGenerator dataGenerator = new BandwidthUsageDataGenerator(1, TimeUnit.SECONDS); + private final Configuration config = ApplicationConfiguration.getConfiguration(); + private JLabel lblBandwidth; + private ChartPanel chartPanel1; + + public BandwidthDialog(@NotNull Window owner, @NotNull ShowBandwidthUsageAction menuAction) { + super(owner); + + initComponents(); + setupChart(); + + restoreSizeFromConfig(); + addComponentListener(new WriteConfigComponentListener(config, this)); + + addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent e) { + storeVisibilityState(true); + menuAction.setDialogOptional(Optional.of(BandwidthDialog.this)); + menuAction.setEnabled(false); + } + + @Override + public void windowClosed(WindowEvent e) { + storeVisibilityState(false); + menuAction.setDialogOptional(Optional.empty()); + menuAction.setEnabled(true); + } + }); + + MessageBus.getMessageBus().subscribe(this); + } + + @Handler + private void handleDarkModeChange(DarkModeChangeEvent e) { + SwingUtilities.invokeLater(this::setLabelColors); + } + + private void calculateHudPosition() { + final GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); + final DisplayMode dm = gd.getDisplayMode(); + setLocation(dm.getWidth() - DEFAULT_WIDTH, 0); + } + + private void restoreSizeFromConfig() { + try { + config.lock(LockMode.READ); + int x = config.getInt(CONFIG_X); + int y = config.getInt(CONFIG_Y); + int width = config.getInt(CONFIG_WIDTH); + int height = config.getInt(CONFIG_HEIGHT); + + setSize(width, height); + setLocation(x, y); + } catch (Exception ex) { + setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); + calculateHudPosition(); + } finally { + config.unlock(LockMode.READ); + } + } + + private void setLabelColors() { + var color = UIManager.getColor("Label.foreground"); + + dateAxis.setLabelPaint(color); + dateAxis.setTickLabelPaint(color); + dateAxis.setTickMarkPaint(color); + //prevent display of date x-axis labels + dateAxis.setDateFormatOverride(new SimpleDateFormat("")); + + bandwidthAxis.setLabelPaint(color); + bandwidthAxis.setTickLabelPaint(color); + bandwidthAxis.setTickMarkPaint(color); + } + + private void setupChart() { + total.setMaximumItemAge(TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS)); + + TimeSeriesCollection dataset = new TimeSeriesCollection(); + dataset.addSeries(total); + + bandwidthAxis.setAutoRange(true); + + setLabelColors(); + + var renderer = new XYSplineRenderer(); + renderer.setDefaultShapesVisible(false); + renderer.setSeriesPaint(0, Color.red); + + var plot = new XYPlot(dataset, dateAxis, bandwidthAxis, renderer); + plot.setBackgroundPaint(Color.BLACK); + plot.setDomainGridlinePaint(Color.white); + plot.setRangeGridlinePaint(Color.white); + plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0)); + plot.setDomainGridlinesVisible(false); + + dateAxis.setAutoRange(true); + dateAxis.setLowerMargin(0.0); + dateAxis.setUpperMargin(0.0); + dateAxis.setTickLabelsVisible(false); + dateAxis.setTickMarksVisible(false); + dateAxis.setAxisLineVisible(false); + + bandwidthAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); + bandwidthAxis.setNumberFormatOverride(new DecimalFormat("#######.##")); + + var chart = new JFreeChart(plot); + chart.removeLegend(); + + chartPanel1.setPopupMenu(null); + chartPanel1.setChart(chart); + + //reset counters first as there will be spikes otherwise... + MVHttpClient.getInstance().getByteCounter().resetCounters(); + dataGenerator.start(); + } + + @Override + public void dispose() { + dataGenerator.stop(); + + super.dispose(); + } + + public void storeVisibilityState(boolean newVar) { + config.setProperty(ApplicationConfiguration.APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE, newVar); + } + + private void initComponents() { + lblBandwidth = new JLabel(); + var label1 = new JLabel(); + var chartContainer = new JPanel(); + chartPanel1 = new ChartPanel(null); + + setTitle("Bandbreite"); +// setMinimumSize(new Dimension(400, 200)); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + setType(Window.Type.UTILITY); + setPreferredSize(new Dimension(400, 200)); + var contentPane = getContentPane(); + contentPane.setLayout(new MigLayout( + new LC().fill().insets("5").hideMode(3), + // columns + new AC() + .grow().fill().gap() + .align("right"), + // rows + new AC() + .align("center").gap() + .grow().fill())); + + lblBandwidth.setText("0"); + lblBandwidth.setHorizontalAlignment(SwingConstants.TRAILING); + lblBandwidth.setVerticalAlignment(SwingConstants.BOTTOM); + lblBandwidth.setFont(lblBandwidth.getFont().deriveFont(lblBandwidth.getFont().getStyle() | Font.BOLD, lblBandwidth.getFont().getSize() + 19f)); + contentPane.add(lblBandwidth, new CC().cell(0, 0).alignY("bottom").growY(0)); + + label1.setText("MBit/s"); //NON-NLS + label1.setFont(label1.getFont().deriveFont(label1.getFont().getSize() + 2f)); + contentPane.add(label1, new CC().cell(1, 0).alignY("bottom").growY(0)); + chartContainer.setMinimumSize(new Dimension(240, 120)); + chartContainer.setLayout(new BorderLayout()); + chartContainer.add(chartPanel1, BorderLayout.CENTER); + contentPane.add(chartContainer, new CC().cell(0, 1, 2, 1)); + pack(); + setLocationRelativeTo(getOwner()); + } + + public class BandwidthUsageDataGenerator extends Timer implements ActionListener { + + /** + * Constructor. + * + * @param interval the interval + */ + public BandwidthUsageDataGenerator(int interval, @NotNull TimeUnit timeUnit) { + super((int) TimeUnit.MILLISECONDS.convert(interval, timeUnit), null); + setName("BandwidthUsageGenerator"); + addActionListener(this); + } + + /** + * Calculate to current bandwidth usage. + * + * @return Used bandwidth in Megabits per second. + */ + private double calculateBandwidthUsage() { + var byteCounter = MVHttpClient.getInstance().getByteCounter(); + double bandwidth = byteCounter.bytesRead(); + byteCounter.resetCounters(); + + //convert to MBits per second + bandwidth = bandwidth * 8d / FileUtils.ONE_MB; + if (bandwidth < 0d) + bandwidth = 0d; + + return bandwidth; + } + + /** + * Adds a new total memory reading (converted to MByte) to the dataset. + * + * @param event the action event. + */ + public void actionPerformed(ActionEvent event) { + var usage = calculateBandwidthUsage(); + total.add(new Millisecond(), usage); + + lblBandwidth.setText(Long.toString(Math.round(usage))); + } + } +} diff --git a/src/main/java/mediathek/gui/bandwidth/BandwidthMonitorController.java b/src/main/java/mediathek/gui/bandwidth/BandwidthMonitorController.java deleted file mode 100644 index c4e072a55d..0000000000 --- a/src/main/java/mediathek/gui/bandwidth/BandwidthMonitorController.java +++ /dev/null @@ -1,226 +0,0 @@ -package mediathek.gui.bandwidth; - -import eu.hansolo.tilesfx.Tile; -import eu.hansolo.tilesfx.TileBuilder; -import javafx.animation.Animation; -import javafx.animation.KeyFrame; -import javafx.animation.Timeline; -import javafx.application.Platform; -import javafx.embed.swing.JFXPanel; -import javafx.scene.Scene; -import javafx.scene.paint.Color; -import javafx.scene.paint.Stop; -import javafx.util.Duration; -import mediathek.gui.messages.BandwidthMonitorStateChangedEvent; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.tool.ApplicationConfiguration; -import mediathek.tool.FileUtils; -import mediathek.tool.MessageBus; -import mediathek.tool.http.MVHttpClient; -import net.engio.mbassy.listener.Handler; -import org.apache.commons.configuration2.Configuration; -import org.apache.commons.configuration2.sync.LockMode; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.time.ZoneId; -import java.time.ZonedDateTime; - -/** - * This class will manage and display the download bandwidth chart display. - */ -public class BandwidthMonitorController { - - private static final int DEFAULT_WIDTH = 300; - private static final int DEFAULT_HEIGHT = 150; - private static final String CONFIG_X = "bandwidth_monitor.x"; - private static final String CONFIG_Y = "bandwidth_monitor.y"; - private static final String CONFIG_HEIGHT = "bandwidth_monitor.height"; - private static final String CONFIG_WIDTH = "bandwidth_monitor.width"; - private final Configuration config = ApplicationConfiguration.getConfiguration(); - private JDialog hudDialog; - private Timeline updateMemoryTimer; - private Tile bandwidthTile; - private JFXPanel fxPanel; - - public BandwidthMonitorController(JFrame parent) { - createDialog(parent); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - fxPanel.setScene(new Scene(createTile())); - createUpdateTimer(); - }); - - restoreSizeFromConfig(); - - MessageBus.getMessageBus().subscribe(this); - - setVisibility(); - hudDialog.addComponentListener(new WriteConfigComponentListener()); - } - - private void restoreSizeFromConfig() { - try { - config.lock(LockMode.READ); - int x = config.getInt(CONFIG_X); - int y = config.getInt(CONFIG_Y); - int width = config.getInt(CONFIG_WIDTH); - int height = config.getInt(CONFIG_HEIGHT); - - hudDialog.setSize(width, height); - hudDialog.setLocation(x, y); - } - catch (Exception ex) { - hudDialog.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); - calculateHudPosition(); - } - finally { - config.unlock(LockMode.READ); - } - } - - public void close() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - if (updateMemoryTimer != null) - updateMemoryTimer.stop(); - }); - hudDialog.dispose(); - } - - @Handler - private void handleBandwidthMonitorStateChangedEvent(BandwidthMonitorStateChangedEvent e) { - SwingUtilities.invokeLater(this::setVisibility); - } - - private void createDialog(JFrame parent) { - hudDialog = new JDialog(parent); - hudDialog.setTitle("Bandbreite"); - hudDialog.setResizable(true); - hudDialog.setType(Window.Type.UTILITY); - hudDialog.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); - hudDialog.addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - Platform.runLater(() -> updateMemoryTimer.play()); - } - - @Override - public void componentHidden(ComponentEvent e) { - Platform.runLater(() -> updateMemoryTimer.stop()); - updateListeners(); - } - }); - hudDialog.setLayout(new BorderLayout(0, 0)); - fxPanel = new JFXPanel(); - hudDialog.getContentPane().add(fxPanel, BorderLayout.CENTER); - } - - private void calculateHudPosition() { - final GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); - final DisplayMode dm = gd.getDisplayMode(); - hudDialog.setLocation(dm.getWidth() - DEFAULT_WIDTH, 0); - } - - /** - * Calculate to current bandwidth usage. - * - * @return Used bandwidth in Megabits per second. - */ - private double calculateBandwidthUsage() { - var byteCounter = MVHttpClient.getInstance().getByteCounter(); - double bandwidth = byteCounter.bytesRead(); - byteCounter.resetCounters(); - - //convert to MBits per second - bandwidth = bandwidth * 8d / FileUtils.ONE_MB; - if (bandwidth < 0d) - bandwidth = 0d; - - return bandwidth; - } - - private void createUpdateTimer() { - updateMemoryTimer = new Timeline(new KeyFrame(Duration.seconds(1), event -> bandwidthTile.setValue(calculateBandwidthUsage()))); - updateMemoryTimer.setCycleCount(Animation.INDEFINITE); - } - - private Tile createTile() { - bandwidthTile = TileBuilder.create() - .skinType(Tile.SkinType.SPARK_LINE) - .prefSize(400, 400) - .unit("MBit/s") - .minValue(0) - .maxValue(2d * 1024d) - .decimals(0) - .tickLabelDecimals(0) - .time(ZonedDateTime.now(ZoneId.of("Europe/Berlin"))) - .gradientStops(new Stop(0, Color.web("#1CAF4D")), - new Stop(0.0075, Color.web("#1CAF4D")), - new Stop(0.00751, Color.web("#91CA40")), - new Stop(0.01166, Color.web("#91CA40")), - new Stop(0.01167, Color.web("#F8C610")), - new Stop(0.01666, Color.web("#F8C610")), - new Stop(0.01667, Color.web("#F29222")), - new Stop(0.025, Color.web("#F29222")), - new Stop(0.02501, Color.web("#EC1D24")), - new Stop(1.0, Color.web("#EC1D24"))) - .strokeWithGradient(true) - .averagingPeriod(96) - .averageVisible(true) - .averagingEnabled(true) - .smoothing(true) - .build(); - - bandwidthTile.setValue(0d); - - return bandwidthTile; - } - - private void updateListeners() { - config.setProperty(ApplicationConfiguration.APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE,false); - MessageBus.getMessageBus().publishAsync(new BandwidthMonitorStateChangedEvent()); - } - - /** - * Show/hide bandwidth display. Take also care about the used timer. - */ - public void setVisibility() { - final var visible = config.getBoolean(ApplicationConfiguration.APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE,false); - // reset counters before otherwise huge spikes will appear - if (visible) { - var byteCounter = MVHttpClient.getInstance().getByteCounter(); - byteCounter.resetCounters(); - } - hudDialog.setVisible(visible); - } - - private class WriteConfigComponentListener extends ComponentAdapter { - @Override - public void componentResized(ComponentEvent e) { - try { - config.lock(LockMode.WRITE); - final var size = hudDialog.getSize(); - config.setProperty(CONFIG_WIDTH, size.width); - config.setProperty(CONFIG_HEIGHT, size.height); - } - finally { - config.unlock(LockMode.WRITE); - } - } - - @Override - public void componentMoved(ComponentEvent e) { - try { - config.lock(LockMode.WRITE); - final var location = hudDialog.getLocation(); - config.setProperty(CONFIG_X, location.x); - config.setProperty(CONFIG_Y, location.y); - } - finally { - config.unlock(LockMode.WRITE); - } - } - } -} - diff --git a/src/main/java/mediathek/gui/bandwidth/WriteConfigComponentListener.kt b/src/main/java/mediathek/gui/bandwidth/WriteConfigComponentListener.kt new file mode 100644 index 0000000000..f863965478 --- /dev/null +++ b/src/main/java/mediathek/gui/bandwidth/WriteConfigComponentListener.kt @@ -0,0 +1,34 @@ +package mediathek.gui.bandwidth + +import org.apache.commons.configuration2.Configuration +import org.apache.commons.configuration2.sync.LockMode +import java.awt.event.ComponentAdapter +import java.awt.event.ComponentEvent +import javax.swing.JDialog + +internal class WriteConfigComponentListener(private val config: Configuration, private val dialog: JDialog) : + ComponentAdapter() { + override fun componentResized(e: ComponentEvent) { + try { + config.lock(LockMode.WRITE) + val size = dialog.size + config.setProperty(BandwidthDialog.CONFIG_WIDTH, size.width) + config.setProperty(BandwidthDialog.CONFIG_HEIGHT, size.height) + } + finally { + config.unlock(LockMode.WRITE) + } + } + + override fun componentMoved(e: ComponentEvent) { + try { + config.lock(LockMode.WRITE) + val location = dialog.location + config.setProperty(BandwidthDialog.CONFIG_X, location.x) + config.setProperty(BandwidthDialog.CONFIG_Y, location.y) + } + finally { + config.unlock(LockMode.WRITE) + } + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/dialog/AboutDialog.java b/src/main/java/mediathek/gui/dialog/AboutDialog.java new file mode 100644 index 0000000000..a6d7e011e2 --- /dev/null +++ b/src/main/java/mediathek/gui/dialog/AboutDialog.java @@ -0,0 +1,260 @@ +/* + * Created by JFormDesigner on Sat Jul 23 12:40:16 CEST 2022 + */ + +package mediathek.gui.dialog; + +import mediathek.config.Konstanten; +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; +import org.apache.commons.lang3.SystemUtils; +import org.jdesktop.swingx.JXHyperlink; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.net.URI; + +/** + * @author Christian Franzke + */ +public class AboutDialog extends JDialog { + + public AboutDialog(Window owner) { + super(owner); + initComponents(); + + lblVersion.setText(String.format("Version %s (%s)", Konstanten.MVVERSION, SystemUtils.OS_ARCH)); + + hyperlinkHomepage.addActionListener(l -> browseToUrl(Konstanten.ADRESSE_WEBSITE)); + hyperlinkGuiDonation.addActionListener(l -> browseToUrl("https://paypal.me/ChristianFranzke")); + hyperlinkServerDonation.addActionListener(l -> browseToUrl(Konstanten.ADRESSE_DONATION)); + hyperlinkForum.addActionListener(l -> browseToUrl(Konstanten.ADRESSE_FORUM)); + hyperlinkOnlineHelp.addActionListener(l -> browseToUrl(Konstanten.ADRESSE_ANLEITUNG)); + + hyperlinkJetBrains.addActionListener(l -> browseToUrl("https://www.jetbrains.com")); + hyperlinkEjTechnologies.addActionListener(l -> browseToUrl("https://www.ej-technologies.com")); + + + SwingUtilities.invokeLater(() -> scrollPane1.getVerticalScrollBar().setValue(0)); + } + + private void showError() { + JOptionPane.showMessageDialog(this, "Es konnte kein Browser geöffnet werden.", + Konstanten.PROGRAMMNAME, JOptionPane.ERROR_MESSAGE); + } + + private void browseToUrl(@NotNull String url) { + if (Desktop.isDesktopSupported()) { + var desktop = Desktop.getDesktop(); + if (desktop.isSupported(Desktop.Action.BROWSE)) { + try { + desktop.browse(new URI(url)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + else { + showError(); + } + } + else { + showError(); + } + } + + private void initComponents() { + // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents + // Generated using JFormDesigner non-commercial license + var label1 = new JLabel(); + var panel1 = new JPanel(); + var label2 = new JLabel(); + lblVersion = new JLabel(); + var tabbedPane1 = new JTabbedPane(); + var panel2 = new JPanel(); + var scrollPane3 = new JScrollPane(); + var textPane1 = new JTextPane(); + var panel3 = new JPanel(); + scrollPane1 = new JScrollPane(); + var textPane2 = new JTextPane(); + var panel5 = new JPanel(); + hyperlinkHomepage = new JXHyperlink(); + hyperlinkGuiDonation = new JXHyperlink(); + hyperlinkServerDonation = new JXHyperlink(); + hyperlinkForum = new JXHyperlink(); + hyperlinkOnlineHelp = new JXHyperlink(); + var label4 = new JLabel(); + var panel4 = new JPanel(); + hyperlinkJetBrains = new JXHyperlink(); + hyperlinkEjTechnologies = new JXHyperlink(); + + //======== this ======== + setTitle("\u00dcber dieses Programm"); //NON-NLS + setModal(true); + setMinimumSize(new Dimension(725, 470)); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + setResizable(false); + var contentPane = getContentPane(); + contentPane.setLayout(new MigLayout( + new LC().fill().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .size("316").fill().gap() //NON-NLS + .fill(), + // rows + new AC() + .fill().gap() + .fill().gap() + .fill())); + + //---- label1 ---- + label1.setIcon(new ImageIcon(getClass().getResource("/mediathek/res/MediathekView.png"))); //NON-NLS + contentPane.add(label1, new CC().cell(0, 0)); + + //======== panel1 ======== + { + panel1.setLayout(new MigLayout( + new LC().insets("0").hideMode(3).gridGap("0", "0"), //NON-NLS + // columns + new AC() + .grow().align("left"), //NON-NLS + // rows + new AC() + .fill().gap() + .fill().gap("rel") //NON-NLS + .grow().fill())); + + //---- label2 ---- + label2.setText("MediathekView"); //NON-NLS + label2.setFont(label2.getFont().deriveFont(label2.getFont().getStyle() | Font.BOLD, label2.getFont().getSize() + 35f)); + panel1.add(label2, new CC().cell(0, 0)); + + //---- lblVersion ---- + lblVersion.setText("Version"); //NON-NLS + panel1.add(lblVersion, new CC().cell(0, 1)); + + //======== tabbedPane1 ======== + { + tabbedPane1.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + + //======== panel2 ======== + { + panel2.setPreferredSize(new Dimension(196, 220)); + panel2.setLayout(new BorderLayout()); + + //======== scrollPane3 ======== + { + scrollPane3.setOpaque(false); + scrollPane3.setMinimumSize(new Dimension(196, 162)); + + //---- textPane1 ---- + textPane1.setText("MediathekView-Client:\nChristian Franzke (derreisende77)\n\nMediathekView-Server:\nNicklas Wiegandt (nicklas2751)\nPeter W. (pidoubleyou)\nSascha Wiegandt (thesasch)\n\nServer-Administration:\nAlexander Finkh\u00e4user (alex1702)"); //NON-NLS + scrollPane3.setViewportView(textPane1); + } + panel2.add(scrollPane3, BorderLayout.CENTER); + } + tabbedPane1.addTab("aktive Entwickler", panel2); //NON-NLS + + //======== panel3 ======== + { + panel3.setLayout(new BorderLayout()); + + //======== scrollPane1 ======== + { + + //---- textPane2 ---- + textPane2.setText("Gr\u00fcnder des Programms:\nXaver W. (xaverW)\n\nWeitere Beteiligte:\nsiedlerchr\nstyrol\nzxsd\napoleon\nhostis\npmshell\nclel\nthausherr\nklauswich"); //NON-NLS + scrollPane1.setViewportView(textPane2); + } + panel3.add(scrollPane1, BorderLayout.CENTER); + } + tabbedPane1.addTab("ehemalige Mitwirkende", panel3); //NON-NLS + } + panel1.add(tabbedPane1, new CC().cell(0, 2).grow()); + } + contentPane.add(panel1, new CC().cell(1, 0, 1, 2).grow()); + + //======== panel5 ======== + { + panel5.setLayout(new MigLayout( + new LC().fillX().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .size("316").fill(), //NON-NLS + // rows + new AC() + .fill().gap() + .fill().gap() + .fill().gap() + .fill().gap() + .fill())); + + //---- hyperlinkHomepage ---- + hyperlinkHomepage.setText("Homepage"); //NON-NLS + panel5.add(hyperlinkHomepage, new CC().cell(0, 0)); + + //---- hyperlinkGuiDonation ---- + hyperlinkGuiDonation.setText("Spende an den Entwickler des Programms"); //NON-NLS + panel5.add(hyperlinkGuiDonation, new CC().cell(0, 1)); + + //---- hyperlinkServerDonation ---- + hyperlinkServerDonation.setText("Spende f\u00fcr den Server-Betrieb"); //NON-NLS + panel5.add(hyperlinkServerDonation, new CC().cell(0, 2)); + + //---- hyperlinkForum ---- + hyperlinkForum.setText("Hilfe-Forum"); //NON-NLS + panel5.add(hyperlinkForum, new CC().cell(0, 3)); + + //---- hyperlinkOnlineHelp ---- + hyperlinkOnlineHelp.setText("Online-Anleitung"); //NON-NLS + panel5.add(hyperlinkOnlineHelp, new CC().cell(0, 4)); + } + contentPane.add(panel5, new CC().cell(0, 1).growX()); + + //---- label4 ---- + label4.setText("Die Entwicklung wird unterst\u00fctzt von:"); //NON-NLS + label4.setVerticalAlignment(SwingConstants.TOP); + contentPane.add(label4, new CC().cell(0, 2)); + + //======== panel4 ======== + { + panel4.setLayout(new MigLayout( + new LC().insets("0").hideMode(3).gridGap("0", "0"), //NON-NLS + // columns + new AC() + .grow().align("left"), //NON-NLS + // rows + new AC() + .fill().gap() + .fill())); + + //---- hyperlinkJetBrains ---- + hyperlinkJetBrains.setText("JetBrains IntelliJ"); //NON-NLS + hyperlinkJetBrains.setVerticalAlignment(SwingConstants.TOP); + panel4.add(hyperlinkJetBrains, new CC().cell(0, 0).growX()); + + //---- hyperlinkEjTechnologies ---- + hyperlinkEjTechnologies.setText("ej-technologies install4j & JProfiler"); //NON-NLS + panel4.add(hyperlinkEjTechnologies, new CC().cell(0, 1).growX()); + } + contentPane.add(panel4, new CC().cell(1, 2)); + pack(); + setLocationRelativeTo(getOwner()); + // JFormDesigner - End of component initialization //GEN-END:initComponents + } + + // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables + // Generated using JFormDesigner non-commercial license + private JLabel lblVersion; + private JScrollPane scrollPane1; + private JXHyperlink hyperlinkHomepage; + private JXHyperlink hyperlinkGuiDonation; + private JXHyperlink hyperlinkServerDonation; + private JXHyperlink hyperlinkForum; + private JXHyperlink hyperlinkOnlineHelp; + private JXHyperlink hyperlinkJetBrains; + private JXHyperlink hyperlinkEjTechnologies; + // JFormDesigner - End of variables declaration //GEN-END:variables +} diff --git a/src/main/java/mediathek/gui/dialog/AboutDialog.jfd b/src/main/java/mediathek/gui/dialog/AboutDialog.jfd new file mode 100644 index 0000000000..45df4c85c1 --- /dev/null +++ b/src/main/java/mediathek/gui/dialog/AboutDialog.jfd @@ -0,0 +1,191 @@ +JFDML JFormDesigner: "7.0.7.0.1134" Java: "11.0.15" encoding: "UTF-8" + +new FormModel { + contentType: "form/swing" + root: new FormRoot { + add( new FormWindow( "javax.swing.JDialog", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "fill,insets 5,hidemode 3,gap 5 5" + "$columnConstraints": "[316,fill][fill]" + "$rowConstraints": "[fill][fill][fill]" + } ) { + name: "this" + "title": "Über dieses Programm" + "modal": true + "minimumSize": new java.awt.Dimension( 725, 470 ) + "defaultCloseOperation": 2 + "resizable": false + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label1" + "icon": new com.jformdesigner.model.SwingIcon( 0, "/mediathek/res/MediathekView.png" ) + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 0,hidemode 3,gap 0 0" + "$columnConstraints": "[grow,left]" + "$rowConstraints": "[fill][fill]rel[grow,fill]" + } ) { + name: "panel1" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label2" + "text": "MediathekView" + "font": new com.jformdesigner.model.SwingDerivedFont( null, 1, 35, false ) + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "lblVersion" + "text": "Version" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" + } ) + add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) { + name: "tabbedPane1" + "tabLayoutPolicy": 1 + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { + name: "panel2" + "preferredSize": new java.awt.Dimension( 196, 220 ) + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { + name: "scrollPane3" + "opaque": false + "minimumSize": new java.awt.Dimension( 196, 162 ) + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormComponent( "javax.swing.JTextPane" ) { + name: "textPane1" + "text": "MediathekView-Client:\nChristian Franzke (derreisende77)\n\nMediathekView-Server:\nNicklas Wiegandt (nicklas2751)\nPeter W. (pidoubleyou)\nSascha Wiegandt (thesasch)\n\nServer-Administration:\nAlexander Finkhäuser (alex1702)" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + } ) + }, new FormLayoutConstraints( class java.lang.String ) { + "value": "Center" + } ) + }, new FormLayoutConstraints( null ) { + "title": "aktive Entwickler" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { + name: "panel3" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { + name: "scrollPane1" + add( new FormComponent( "javax.swing.JTextPane" ) { + name: "textPane2" + "text": "Gründer des Programms:\nXaver W. (xaverW)\n\nWeitere Beteiligte:\nsiedlerchr\nstyrol\nzxsd\napoleon\nhostis\npmshell\nclel\nthausherr\nklauswich" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + } ) + }, new FormLayoutConstraints( class java.lang.String ) { + "value": "Center" + } ) + }, new FormLayoutConstraints( null ) { + "title": "ehemalige Mitwirkende" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2,grow" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0 1 2,grow" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$columnConstraints": "[316,fill]" + "$rowConstraints": "[fill][fill][fill][fill][fill]" + "$layoutConstraints": "fillx,insets 5,hidemode 3,gap 5 5" + } ) { + name: "panel5" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkHomepage" + "text": "Homepage" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkGuiDonation" + "text": "Spende an den Entwickler des Programms" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" + } ) + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkServerDonation" + "text": "Spende für den Server-Betrieb" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2" + } ) + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkForum" + "text": "Hilfe-Forum" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 3" + } ) + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkOnlineHelp" + "text": "Online-Anleitung" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 4" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1,growx" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label4" + "text": "Die Entwicklung wird unterstützt von:" + "verticalAlignment": 1 + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 0,hidemode 3,gap 0 0" + "$columnConstraints": "[grow,left]" + "$rowConstraints": "[fill][fill]" + } ) { + name: "panel4" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkJetBrains" + "text": "JetBrains IntelliJ" + "verticalAlignment": 1 + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0,growx" + } ) + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkEjTechnologies" + "text": "ej-technologies install4j & JProfiler" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1,growx" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 2" + } ) + }, new FormLayoutConstraints( null ) { + "location": new java.awt.Point( 0, 0 ) + "size": new java.awt.Dimension( 750, 465 ) + } ) + } +} diff --git a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java index 4d84cd8a2b..38417caaa1 100644 --- a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java @@ -3,8 +3,6 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.MVColor; @@ -100,10 +98,10 @@ public void actionPerformed(ActionEvent e) { } }; jRadioButtonAufloesungHd.addActionListener(listener); - jRadioButtonAufloesungHd.setEnabled(!datenFilm.getUrlHighQuality().isEmpty()); + jRadioButtonAufloesungHd.setEnabled(!datenFilm.getHighQualityUrl().isEmpty()); jRadioButtonAufloesungKlein.addActionListener(listener); - jRadioButtonAufloesungKlein.setEnabled(!datenFilm.getUrlLowQuality().isEmpty()); + jRadioButtonAufloesungKlein.setEnabled(!datenFilm.getLowQualityUrl().isEmpty()); jRadioButtonAufloesungHoch.addActionListener(listener); jRadioButtonAufloesungHoch.setSelected(true); @@ -132,7 +130,7 @@ private void launchResolutionFutures() { var decoratedPool = Daten.getInstance().getDecoratedPool(); hqFuture = decoratedPool.submit(() -> { var url = datenFilm.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY); - return datenFilm.getDateigroesse(url); + return datenFilm.getFileSizeForUrl(url); }); Futures.addCallback(hqFuture, new FutureCallback<>() { @@ -160,7 +158,7 @@ public void onFailure(@NotNull Throwable t) { hochFuture = decoratedPool.submit(() -> { var url = datenFilm.getUrlNormalQuality(); - return datenFilm.getDateigroesse(url); + return datenFilm.getFileSizeForUrl(url); }); Futures.addCallback(hochFuture, new FutureCallback<>() { @Override @@ -185,7 +183,7 @@ public void onFailure(@NotNull Throwable t) { kleinFuture = decoratedPool.submit(() -> { var url = datenFilm.getUrlFuerAufloesung(FilmResolution.Enum.LOW); - return datenFilm.getDateigroesse(url); + return datenFilm.getFileSizeForUrl(url); }); Futures.addCallback(kleinFuture, new FutureCallback<>() { @Override @@ -303,7 +301,7 @@ private void tus() { } private void setupZielButton() { - jButtonZiel.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonZiel.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonZiel.setText(""); jButtonZiel.addActionListener(l -> { var initialDirectory = ""; @@ -321,7 +319,7 @@ private void setupZielButton() { } private void setupDeleteHistoryButton() { - jButtonDelHistory.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); + jButtonDelHistory.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); jButtonDelHistory.addActionListener(e -> { MVConfig.add(MVConfig.Configs.SYSTEM_DIALOG_DOWNLOAD__PFADE_ZUM_SPEICHERN, ""); jComboBoxPfad.setModel(new DefaultComboBoxModel<>(new String[]{orgPfad})); @@ -408,9 +406,12 @@ private long getFreeDiskSpace(final String strPath) { * Calculate free disk space on volume and check if the movies can be safely downloaded. */ private void calculateAndCheckDiskSpace() { - jRadioButtonAufloesungHd.setForeground(Color.black); - jRadioButtonAufloesungHoch.setForeground(Color.black); - jRadioButtonAufloesungKlein.setForeground(Color.black); + var fgColor = UIManager.getColor("Label.foreground"); + if (fgColor != null) { + jRadioButtonAufloesungHd.setForeground(fgColor); + jRadioButtonAufloesungHoch.setForeground(fgColor); + jRadioButtonAufloesungKlein.setForeground(fgColor); + } try { var filmBorder = (TitledBorder)jPanelSize.getBorder(); @@ -505,12 +506,12 @@ public static void saveComboPfad(JComboBox jcb, String orgPath) { private boolean isHighQualityRequested() { return pSet.arr[DatenPset.PROGRAMMSET_AUFLOESUNG].equals(FilmResolution.Enum.HIGH_QUALITY.toString()) - && !datenFilm.getUrlHighQuality().isEmpty(); + && !datenFilm.getHighQualityUrl().isEmpty(); } private boolean isLowQualityRequested() { return pSet.arr[DatenPset.PROGRAMMSET_AUFLOESUNG].equals(FilmResolution.Enum.LOW.toString()) && - !datenFilm.getUrlLowQuality().isEmpty(); + !datenFilm.getLowQualityUrl().isEmpty(); } private boolean highQualityMandated; @@ -530,7 +531,7 @@ private void setupResolutionButtons() { jCheckBoxInfodatei.setSelected(Boolean.parseBoolean(pSet.arr[DatenPset.PROGRAMMSET_INFODATEI])); - if (datenFilm.getUrlSubtitle().isEmpty()) { + if (datenFilm.getSubtitleUrl().isEmpty()) { // dann gibts keinen Subtitle jCheckBoxSubtitle.setEnabled(false); } else { diff --git a/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java b/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java index 312610e11c..7313487c28 100644 --- a/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java @@ -1,15 +1,10 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.MVColor; import mediathek.config.MVConfig; import mediathek.daten.DatenPset; import mediathek.mainwindow.MediathekGui; -import mediathek.tool.ApplicationConfiguration; -import mediathek.tool.EscapeKeyHandler; -import mediathek.tool.FileDialogs; -import mediathek.tool.FilenameUtils; +import mediathek.tool.*; import org.apache.commons.configuration2.Configuration; import org.jdesktop.swingx.VerticalLayout; @@ -61,7 +56,7 @@ public DialogAddMoreDownload(JFrame parent, DatenPset pSet) { beenden(); }); - jButtonPath.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonPath.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonPath.addActionListener(l -> { var initialDirectory = ""; var cbItem = Objects.requireNonNull(jComboBoxPath.getSelectedItem()).toString(); @@ -77,7 +72,7 @@ public DialogAddMoreDownload(JFrame parent, DatenPset pSet) { }); - jButtonDelPath.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); + jButtonDelPath.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); jButtonDelPath.addActionListener(e -> { MVConfig.add(MVConfig.Configs.SYSTEM_DIALOG_DOWNLOAD__PFADE_ZUM_SPEICHERN, ""); jComboBoxPath.setModel(new DefaultComboBoxModel<>(new String[]{pSet.getZielPfad()})); diff --git a/src/main/java/mediathek/gui/dialog/DialogBeenden.kt b/src/main/java/mediathek/gui/dialog/DialogBeenden.kt index aea762f0bc..b22f3c5de7 100644 --- a/src/main/java/mediathek/gui/dialog/DialogBeenden.kt +++ b/src/main/java/mediathek/gui/dialog/DialogBeenden.kt @@ -1,13 +1,11 @@ package mediathek.gui.dialog -import jiconfont.icons.font_awesome.FontAwesome -import jiconfont.swing.IconFontSwing import mediathek.config.Daten import mediathek.file.GetFile -import mediathek.javafx.AppTerminationIndefiniteProgress import mediathek.mainwindow.MediathekGui +import mediathek.tool.AppTerminationIndefiniteProgress import mediathek.tool.EscapeKeyHandler -import java.awt.BorderLayout +import mediathek.tool.SVGIconUtilities import java.awt.event.WindowAdapter import java.awt.event.WindowEvent import java.util.* @@ -16,9 +14,10 @@ import javax.swing.* class DialogBeenden(parent: JFrame) : JDialog(parent, true) { /** - * Indicates whether the application can terminate. + * Indicate whether the application can terminate. */ - private var applicationCanTerminate = false + var applicationCanTerminate = false + private set /** * Indicate whether computer should be shut down. @@ -26,11 +25,6 @@ class DialogBeenden(parent: JFrame) : JDialog(parent, true) { var isShutdownRequested = false private set - /** - * JPanel for displaying the glassPane with the busy indicator label. - */ - private var glassPane: GlassPanel? = null - /** * The download monitoring [javax.swing.SwingWorker]. */ @@ -39,22 +33,7 @@ class DialogBeenden(parent: JFrame) : JDialog(parent, true) { private val btnContinue: JButton = JButton("Weiter") private val cbShutdownComputer: JCheckBox = JCheckBox("Rechner herunterfahren") private val btnCancel: JButton = JButton("Abbrechen") - private val jButtonHilfe: JButton = JButton(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16f)) - - /** - * Return whether the user still wants to terminate the application. - * - * @return true if the app should continue to terminate. - */ - fun applicationCanTerminate(): Boolean { - return applicationCanTerminate - } - - fun setComboWaitAndTerminate() { - comboActions.selectedItem = WAIT_FOR_DOWNLOADS_AND_TERMINATE - cbShutdownComputer.isSelected = true - isShutdownRequested = true - } + private val jButtonHilfe: JButton = JButton(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")) /** * Create the ComboBoxModel for user selection. @@ -65,7 +44,7 @@ class DialogBeenden(parent: JFrame) : JDialog(parent, true) { get() = DefaultComboBoxModel( arrayOf( CANCEL_AND_TERMINATE_PROGRAM, WAIT_FOR_DOWNLOADS_AND_TERMINATE, - WAIT_FOR_RUNNING_DOWNLOADS_AND_TERMINATE, DONT_TERMINATE + WAIT_FOR_RUNNING_DOWNLOADS_AND_TERMINATE ) ) @@ -79,21 +58,13 @@ class DialogBeenden(parent: JFrame) : JDialog(parent, true) { dispose() } - private class GlassPanel(isShutdownRequested: Boolean) : JPanel() { - init { - layout = BorderLayout(5, 5) - add(AppTerminationIndefiniteProgress(isShutdownRequested), BorderLayout.CENTER) - } - } - /** * Handler which will wait untill all downloads have finished. * * @param waitForRunningDownloadsOnly if true stop all waiting DL and wait only for those running. */ private fun waitUntilDownloadsHaveFinished(waitForRunningDownloadsOnly: Boolean = false) { - glassPane = GlassPanel(isShutdownRequested) - setGlassPane(glassPane) + glassPane = AppTerminationIndefiniteProgress(isShutdownRequested) glassPane?.isVisible = true if (waitForRunningDownloadsOnly) @@ -195,7 +166,6 @@ class DialogBeenden(parent: JFrame) : JDialog(parent, true) { private const val WAIT_FOR_DOWNLOADS_AND_TERMINATE = "Auf Abschluß aller Downloads warten, danach beenden" private const val WAIT_FOR_RUNNING_DOWNLOADS_AND_TERMINATE = "Nur auf bereits laufende Downloads warten, danach beenden" - private const val DONT_TERMINATE = "Programm nicht beenden" } init { @@ -243,10 +213,6 @@ class DialogBeenden(parent: JFrame) : JDialog(parent, true) { applicationCanTerminate = true dispose() } - DONT_TERMINATE -> { - applicationCanTerminate = false - dispose() - } } } diff --git a/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.java b/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.java index dea62c0650..a6018f7d44 100644 --- a/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.java +++ b/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.java @@ -1,21 +1,20 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; +import com.github.lgooddatepicker.components.DateTimePicker; import mediathek.config.Daten; import mediathek.daten.DatenDownload; import mediathek.file.GetFile; -import mediathek.javafx.AppTerminationIndefiniteProgress; +import mediathek.tool.AppTerminationIndefiniteProgress; import mediathek.tool.EscapeKeyHandler; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -42,7 +41,7 @@ public class DialogBeendenZeit extends JDialog { /** * The download monitoring {@link javax.swing.SwingWorker}. */ - private SwingWorker downloadMonitorWorker; + private BusyWaitWorker downloadMonitorWorker; /** * Return whether the user still wants to terminate the application. @@ -65,6 +64,16 @@ public boolean isShutdownRequested() { public DialogBeendenZeit(JFrame parent, final ArrayList listeDownloadsStarten_) { super(parent, true); initComponents(); + + dateTimePicker.datePicker.setDateToToday(); + var timePicker = dateTimePicker.timePicker; + timePicker.setTimeToNow(); + //apply 1 hour 1 minute patch + var selTime = timePicker.getTime(); + selTime = selTime.plusHours(1).plusMinutes(1); + timePicker.setTime(selTime); + + listeDownloadsStarten = listeDownloadsStarten_; if (parent != null) { setLocationRelativeTo(parent); @@ -79,24 +88,10 @@ public void windowClosing(WindowEvent e) { } }); - // set date format - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.MINUTE, 1); - Date startDate = cal.getTime(); - cal.add(Calendar.MINUTE, 60); // würde sagen, das ist ein Bug, geht sonst nicht - Date now = cal.getTime(); - cal.add(Calendar.DATE, 2); - Date endDate = cal.getTime(); - SpinnerDateModel model = new SpinnerDateModel(now, startDate, endDate, Calendar.MINUTE); - - jSpinnerTime.setModel(model); - JSpinner.DateEditor dEditor = new JSpinner.DateEditor(jSpinnerTime, "dd.MM.yyy HH:mm"); - jSpinnerTime.setEditor(dEditor); - comboActions.setModel(getComboBoxModel()); comboActions.addActionListener(e -> setCbShutdownCoputer()); - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonHilfe.addActionListener(e -> new DialogHilfe(parent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_BEENDEN)).setVisible(true)); setCbShutdownCoputer(); @@ -105,11 +100,6 @@ public void windowClosing(WindowEvent e) { btnContinue.addActionListener(e -> { final String strSelectedItem = Objects.requireNonNull(comboActions.getSelectedItem()).toString(); - SimpleDateFormat format = ((JSpinner.DateEditor) jSpinnerTime.getEditor()).getFormat(); - format.applyPattern("HH:mm"); - Date sp = (Date) jSpinnerTime.getValue(); - String strDate = format.format(sp); - switch (strSelectedItem) { case WAIT_FOR_DOWNLOADS_AND_TERMINATE -> { applicationCanTerminate = true; @@ -128,9 +118,8 @@ public void windowClosing(WindowEvent e) { btnCancel.addActionListener(e -> escapeHandler()); - pack(); - getRootPane().setDefaultButton(btnContinue); + pack(); } private void setCbShutdownCoputer() { @@ -186,13 +175,10 @@ private JPanel createGlassPane() { } private void setTextWait() { - SimpleDateFormat format = ((JSpinner.DateEditor) jSpinnerTime.getEditor()).getFormat(); - format.applyPattern("dd.MM.yyy HH:mm"); - Date sp = (Date) jSpinnerTime.getValue(); - String strDate = format.format(sp); - - String strMessage = "Downloads starten: " + strDate; - progressPanel.setMessage(strMessage); + var dt = dateTimePicker.getDateTimePermissive(); + var t = dt.format(DateTimeFormatter.ofPattern("HH:mm")); + var d = dt.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")); + progressPanel.setMessage(String.format("Downloads werden am %s um %s gestartet.", d,t)); } /** @@ -205,31 +191,34 @@ private void waitUntilDownloadsHaveFinished() { setTextWait(); glassPane.setVisible(true); - downloadMonitorWorker = new SwingWorker<>() { - @Override - protected Void doInBackground() throws Exception { - while ((((Date) jSpinnerTime.getValue())).after(new Date())) { - TimeUnit.SECONDS.sleep(1); - } - - progressPanel.setMessage("Warte auf Abschluss der Downloads..."); - DatenDownload.startenDownloads(listeDownloadsStarten); + downloadMonitorWorker = new BusyWaitWorker(); + downloadMonitorWorker.execute(); + } - while ((Daten.getInstance().getListeDownloads().unfinishedDownloads() > 0) && !isCancelled()) { - TimeUnit.SECONDS.sleep(1); - } - return null; + private class BusyWaitWorker extends SwingWorker { + @Override + protected Void doInBackground() throws Exception { + while (LocalDateTime.now().isBefore(dateTimePicker.getDateTimePermissive())) { + TimeUnit.SECONDS.sleep(1); } - @Override - protected void done() { - glassPane.setVisible(false); - dispose(); - downloadMonitorWorker = null; + progressPanel.setMessage("Warte auf Abschluss der Downloads..."); + DatenDownload.startenDownloads(listeDownloadsStarten); + + while ((Daten.getInstance().getListeDownloads().unfinishedDownloads() > 0) && !isCancelled()) { + TimeUnit.SECONDS.sleep(1); } - }; - downloadMonitorWorker.execute(); + + return null; + } + + @Override + protected void done() { + glassPane.setVisible(false); + dispose(); + downloadMonitorWorker = null; + } } // //GEN-BEGIN:initComponents @@ -242,8 +231,7 @@ private void initComponents() { btnCancel = new JButton(); jButtonHilfe = new JButton(); var jLabel2 = new JLabel(); - jSpinnerTime = new JSpinner(); - var jLabel3 = new JLabel(); + dateTimePicker = new DateTimePicker(); //======== this ======== setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); @@ -269,9 +257,6 @@ private void initComponents() { //---- jLabel2 ---- jLabel2.setText("Alle Downloads starten um: "); //NON-NLS - //---- jLabel3 ---- - jLabel3.setText("Uhr"); //NON-NLS - GroupLayout contentPaneLayout = new GroupLayout(contentPane); contentPane.setLayout(contentPaneLayout); contentPaneLayout.setHorizontalGroup( @@ -279,7 +264,7 @@ private void initComponents() { .addGroup(contentPaneLayout.createSequentialGroup() .addContainerGap() .addGroup(contentPaneLayout.createParallelGroup() - .addComponent(jLabel1, GroupLayout.DEFAULT_SIZE, 386, Short.MAX_VALUE) + .addComponent(jLabel1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(comboActions, GroupLayout.DEFAULT_SIZE, 0, Short.MAX_VALUE) .addGroup(GroupLayout.Alignment.TRAILING, contentPaneLayout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) @@ -288,16 +273,11 @@ private void initComponents() { .addComponent(btnCancel) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnContinue)) + .addComponent(cbShutdownComputer) .addGroup(contentPaneLayout.createSequentialGroup() - .addGroup(contentPaneLayout.createParallelGroup() - .addComponent(cbShutdownComputer) - .addGroup(contentPaneLayout.createSequentialGroup() - .addComponent(jLabel2) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jSpinnerTime, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel3))) - .addGap(0, 0, Short.MAX_VALUE))) + .addComponent(jLabel2) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(dateTimePicker, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))) .addContainerGap()) ); contentPaneLayout.setVerticalGroup( @@ -305,10 +285,9 @@ private void initComponents() { .addGroup(GroupLayout.Alignment.TRAILING, contentPaneLayout.createSequentialGroup() .addContainerGap() .addGroup(contentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(jLabel2) - .addComponent(jSpinnerTime, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel3)) - .addGap(18, 18, Short.MAX_VALUE) + .addComponent(dateTimePicker, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel2)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 94, Short.MAX_VALUE) .addComponent(jLabel1) .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(comboActions, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) @@ -334,6 +313,6 @@ private void initComponents() { private JCheckBox cbShutdownComputer; private JButton btnCancel; private JButton jButtonHilfe; - private JSpinner jSpinnerTime; + private DateTimePicker dateTimePicker; // End of variables declaration//GEN-END:variables } diff --git a/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.jfd b/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.jfd index 7343ce0541..4cad55d001 100644 --- a/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.jfd +++ b/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.jfd @@ -1,11 +1,11 @@ -JFDML JFormDesigner: "7.0.3.0.337" Java: "11.0.9.1" encoding: "UTF-8" +JFDML JFormDesigner: "8.0.3.0.246" Java: "17.0.6" encoding: "UTF-8" new FormModel { contentType: "form/swing" root: new FormRoot { add( new FormWindow( "javax.swing.JDialog", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { - "$horizontalGroup": "par l {seq {space :::p, par l {comp jLabel1::l::386:x, comp comboActions::l::0:x, seq t {space :0:0:x, comp jButtonHilfe:::p::p, space :::p, comp btnCancel:::p::p, space :::p, comp btnContinue:::p::p}, seq {par l {comp cbShutdownComputer::l:p::p, seq l {comp jLabel2:::p::p, space :::p, comp jSpinnerTime:::p::p, space :::p, comp jLabel3:::p::p}}, space :0:0:x}}, space :::p}}" - "$verticalGroup": "par l {seq t {space :::p, par b {comp jLabel2::b:p::p, comp jSpinnerTime::b:p::p, comp jLabel3::b:p::p}, space s:::x, comp jLabel1:::p::p, space u:::p, comp comboActions:::p::p, space :::p, par t {seq {comp cbShutdownComputer:::p::p, space s:::p, par b {comp btnContinue::b:p::p, comp btnCancel::b:p::p}}, comp jButtonHilfe:::p::p}, space :::p}}" + "$horizontalGroup": "par l {seq {space :::p, par l {comp jLabel1::l:::x, comp comboActions::l::0:x, seq t {space :0:0:x, comp jButtonHilfe:::p::p, space :::p, comp btnCancel:::p::p, space :::p, comp btnContinue:::p::p}, comp cbShutdownComputer:::p::p, seq l {comp jLabel2:::p::p, space :::p, comp dateTimePicker1:::p::p}}, space :::p}}" + "$verticalGroup": "par l {seq t {space :::p, par b {comp dateTimePicker1::b:p::p, comp jLabel2::b:p::p}, space ::94:x, comp jLabel1:::p::p, space u:::p, comp comboActions:::p::p, space :::p, par t {seq {comp cbShutdownComputer:::p::p, space s:::p, par b {comp btnContinue::b:p::p, comp btnCancel::b:p::p}}, comp jButtonHilfe:::p::p}, space :::p}}" } ) { name: "this" "defaultCloseOperation": 2 @@ -47,18 +47,14 @@ new FormModel { "JavaCodeGenerator.variableLocal": true } } ) - add( new FormComponent( "javax.swing.JSpinner" ) { - name: "jSpinnerTime" - } ) - add( new FormComponent( "javax.swing.JLabel" ) { - name: "jLabel3" - "text": "Uhr" + add( new FormComponent( "com.github.lgooddatepicker.components.DateTimePicker" ) { + name: "dateTimePicker1" auxiliary() { - "JavaCodeGenerator.variableLocal": true + "JavaCodeGenerator.variableName": "dateTimePicker" } } ) }, new FormLayoutConstraints( null ) { - "size": new java.awt.Dimension( 400, 300 ) + "size": new java.awt.Dimension( 500, 300 ) "location": new java.awt.Point( 0, 0 ) } ) } diff --git a/src/main/java/mediathek/gui/dialog/DialogContinueDownload.java b/src/main/java/mediathek/gui/dialog/DialogContinueDownload.java index a9ecf331c6..f8ca9b8674 100644 --- a/src/main/java/mediathek/gui/dialog/DialogContinueDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogContinueDownload.java @@ -7,12 +7,8 @@ import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; +import java.awt.event.*; -@SuppressWarnings("serial") public class DialogContinueDownload extends JDialog { public enum DownloadResult { @@ -90,6 +86,15 @@ public void windowClosing(WindowEvent evt) { pack(); getRootPane().setDefaultButton(jButtonWeiter); + + addComponentListener(new ComponentAdapter() { + @Override + public void componentShown(ComponentEvent e) { + //display dialog slightly lower than normal to prevent inadvertent presses from other dialogs (#686) + var loc = getLocationOnScreen(); + setLocation(loc.x, loc.y + 70); + } + }); } /** diff --git a/src/main/java/mediathek/gui/dialog/DialogEditAbo.java b/src/main/java/mediathek/gui/dialog/DialogEditAbo.java index c471bb639a..7540dce72c 100644 --- a/src/main/java/mediathek/gui/dialog/DialogEditAbo.java +++ b/src/main/java/mediathek/gui/dialog/DialogEditAbo.java @@ -1,7 +1,5 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.MVColor; import mediathek.daten.abo.AboTags; @@ -91,7 +89,7 @@ public DialogEditAbo(final JFrame parent, DatenAbo aktA, boolean isMultiEditMode EscapeKeyHandler.installHandler(this, this::dispose); - jButtonHelp.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHelp.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonHelp.addActionListener(e -> new DialogHilfe(parent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_DIALOG_ADD_ABO)).setVisible(true)); if (comboboxPSet.getModel().getSize() == 0) { diff --git a/src/main/java/mediathek/gui/dialog/DialogEditDownload.form b/src/main/java/mediathek/gui/dialog/DialogEditDownload.form index 753533ca32..9c2eb067eb 100644 --- a/src/main/java/mediathek/gui/dialog/DialogEditDownload.form +++ b/src/main/java/mediathek/gui/dialog/DialogEditDownload.form @@ -11,7 +11,7 @@ - + @@ -35,16 +35,14 @@ - + - - - + @@ -55,14 +53,11 @@ - + - - - - + @@ -70,6 +65,10 @@ + + + + @@ -90,66 +89,20 @@ - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - @@ -176,5 +129,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java index 1e75b247e5..f52b14d87a 100644 --- a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java @@ -1,15 +1,16 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; -import mediathek.config.Icons; +import mediathek.config.MVColor; import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; import mediathek.daten.DatenProg; import mediathek.daten.FilmResolution; import mediathek.file.GetFile; +import mediathek.tool.ApplicationConfiguration; import mediathek.tool.EscapeKeyHandler; import mediathek.tool.MVMessageDialog; +import mediathek.tool.SVGIconUtilities; +import org.apache.commons.configuration2.sync.LockMode; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -20,7 +21,10 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; import java.io.File; +import java.util.NoSuchElementException; public class DialogEditDownload extends JDialog { private final DatenDownload datenDownload; @@ -41,23 +45,25 @@ public class DialogEditDownload extends JDialog { private final JFrame parent; private final String orgProgArray; private FilmResolution.Enum resolution = FilmResolution.Enum.NORMAL; - private final JLabel jLabelFilmHD = new JLabel(); - private final JLabel jLabelFilmUT = new JLabel(); - private static ImageIcon ja_sw_16; + private final JCheckBox cbHighQuality = new JCheckBox(); + private final JCheckBox cbSubtitleAvailable = new JCheckBox(); private final TableColumnModel columnModel; + private final GridBagLayout gridbag = new GridBagLayout(); - public DialogEditDownload(JFrame parent, boolean modal, DatenDownload ddownload, boolean ggestartet, TableColumnModel colModel) { + + public DialogEditDownload(JFrame parent, boolean modal, DatenDownload datenDownload, boolean started, TableColumnModel colModel) { super(parent, modal); initComponents(); this.parent = parent; - datenDownload = ddownload; - gestartet = ggestartet; - jScrollPane1.getVerticalScrollBar().setUnitIncrement(16); - ja_sw_16 = Icons.ICON_DIALOG_EIN_SW; + this.datenDownload = datenDownload; + this.gestartet = started; columnModel = colModel; - orgProgArray = datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY]; - mVPanelDownloadZiel = new MVPanelDownloadZiel(parent, datenDownload, false); + cbSubtitleAvailable.setEnabled(false); + cbHighQuality.setEnabled(false); + + orgProgArray = this.datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY]; + mVPanelDownloadZiel = new MVPanelDownloadZiel(parent, this.datenDownload, false); mVPanelDownloadZiel.setBorder(BorderFactory.createLineBorder(new Color(204, 204, 204))); jRadioButtonResHd.addActionListener(e -> changeRes()); jRadioButtonResHi.addActionListener(e -> changeRes()); @@ -74,6 +80,56 @@ public DialogEditDownload(JFrame parent, boolean modal, DatenDownload ddownload, setupResolutionButtons(); setExtra(); + + restoreLocation(); + + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + saveLocation(); + } + + @Override + public void componentMoved(ComponentEvent e) { + saveLocation(); + } + }); + } + + private void restoreLocation() { + var config = ApplicationConfiguration.getConfiguration(); + config.lock(LockMode.READ); + try { + var newLocation = new Point(); + newLocation.x = config.getInt(ApplicationConfiguration.EditDownloadDialog.X); + newLocation.y = config.getInt(ApplicationConfiguration.EditDownloadDialog.Y); + setLocation(newLocation); + + int w = config.getInt(ApplicationConfiguration.EditDownloadDialog.WIDTH, -1); + int h = config.getInt(ApplicationConfiguration.EditDownloadDialog.HEIGHT, -1); + if (w != -1 && h != -1) { + setSize(w,h); + } + } catch (NoSuchElementException ignored) { + } finally { + config.unlock(LockMode.READ); + } + } + + private void saveLocation() { + if (!isVisible()) + return; + var config = ApplicationConfiguration.getConfiguration(); + try { + config.lock(LockMode.WRITE); + var location = getLocationOnScreen(); + config.setProperty(ApplicationConfiguration.EditDownloadDialog.X, location.x); + config.setProperty(ApplicationConfiguration.EditDownloadDialog.Y, location.y); + config.setProperty(ApplicationConfiguration.EditDownloadDialog.WIDTH, getWidth()); + config.setProperty(ApplicationConfiguration.EditDownloadDialog.HEIGHT, getHeight()); + } finally { + config.unlock(LockMode.WRITE); + } } private void setupResolutionButtons() { @@ -88,24 +144,24 @@ private void setupResolutionButtons() { if (datenDownload.film != null) { jRadioButtonResHi.setEnabled(!gestartet); jRadioButtonResHi.setSelected(datenDownload.arr[DatenDownload.DOWNLOAD_URL].equals(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.NORMAL))); - dateiGroesse_Hoch = datenDownload.film.getDateigroesse(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.NORMAL)); + dateiGroesse_Hoch = datenDownload.film.getFileSizeForUrl(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.NORMAL)); if (!dateiGroesse_Hoch.isEmpty()) { jRadioButtonResHi.setText(jRadioButtonResHi.getText() + " [ " + dateiGroesse_Hoch + " MB ]"); } - if (!datenDownload.film.getUrlHighQuality().isEmpty()) { + if (!datenDownload.film.getHighQualityUrl().isEmpty()) { jRadioButtonResHd.setEnabled(!gestartet); jRadioButtonResHd.setSelected(datenDownload.arr[DatenDownload.DOWNLOAD_URL].equals(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY))); - dateiGroesse_HD = datenDownload.film.getDateigroesse(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY)); + dateiGroesse_HD = datenDownload.film.getFileSizeForUrl(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY)); if (!dateiGroesse_HD.isEmpty()) { jRadioButtonResHd.setText(jRadioButtonResHd.getText() + " [ " + dateiGroesse_HD + " MB ]"); } } - if (!datenDownload.film.getUrlLowQuality().isEmpty()) { + if (!datenDownload.film.getLowQualityUrl().isEmpty()) { jRadioButtonResLo.setEnabled(!gestartet); jRadioButtonResLo.setSelected(datenDownload.arr[DatenDownload.DOWNLOAD_URL].equals(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.LOW))); - dateiGroesse_Klein = datenDownload.film.getDateigroesse(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.LOW)); + dateiGroesse_Klein = datenDownload.film.getFileSizeForUrl(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.LOW)); if (!dateiGroesse_Klein.isEmpty()) { jRadioButtonResLo.setText(jRadioButtonResLo.getText() + " [ " + dateiGroesse_Klein + " MB ]"); } @@ -132,8 +188,6 @@ private FilmResolution.Enum getRadioButtonResolution() { private void changeRes() { // RadioButton sind nur enabled wenn "datenDownload.film" vorhanden var res = getRadioButtonResolution(); - //var test = res.toString(); - //var test2 = res.name(); datenDownload.arr[DatenDownload.DOWNLOAD_URL] = datenDownload.film.getUrlFuerAufloesung(res); textfeldListe[DatenDownload.DOWNLOAD_URL].setText(datenDownload.arr[DatenDownload.DOWNLOAD_URL]); @@ -161,37 +215,59 @@ private void changeRes() { private void setExtra() { jPanelExtra.removeAll(); - GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; c.insets = new Insets(5, 10, 10, 5); jPanelExtra.setLayout(gridbag); int zeile = 0; - for (int i = 0; i < columnModel.getColumnCount(); ++i) { + var colCount = columnModel.getColumnCount(); + for (int i = 0; i < colCount; ++i) { + //skip information that is not useful to the user... + if (doNotShow(i)) { + continue; + } + //do not show Abo information if there is none... + if (i == DatenDownload.DOWNLOAD_ABO && datenDownload.arr[DatenDownload.DOWNLOAD_ABO].isEmpty()) + continue; + //do not show geo information if not needed... + if (i == DatenDownload.DOWNLOAD_GEO && datenDownload.arr[DatenDownload.DOWNLOAD_GEO].isEmpty()) + continue; + //do not show subtitle url if it is empty... + if (i == DatenDownload.DOWNLOAD_URL_SUBTITLE && datenDownload.arr[DatenDownload.DOWNLOAD_URL_SUBTITLE].isEmpty()) + continue; + JLabel label = new JLabel(" " + columnModel.getColumn(i).getHeaderValue() + ": "); labelListe[i] = label; JTextField textfeld = new JTextField(); textfeld.setEditable(false); textfeld.setText(datenDownload.arr[i]); textfeldListe[i] = textfeld; - addExtraFeld(i, gridbag, c); + addExtraFeld(i, c); ++zeile; c.gridy = zeile; } jPanelExtra.validate(); } - private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { - //Label - c.gridx = 0; - c.weightx = 0; - if (i == DatenDownload.DOWNLOAD_ZURUECKGESTELLT + private boolean doNotShow(int i) { + return i == DatenDownload.DOWNLOAD_ZURUECKGESTELLT || i == DatenDownload.DOWNLOAD_URL_RTMP || i == DatenDownload.DOWNLOAD_BUTTON_DEL || i == DatenDownload.DOWNLOAD_BUTTON_START || i == DatenDownload.DOWNLOAD_REF - || i == DatenDownload.DOWNLOAD_BANDBREITE) { - // ist eigentlich Unsinn, es anzuzeigen + || i == DatenDownload.DOWNLOAD_BANDBREITE + || i == DatenDownload.DOWNLOAD_NR + || i == DatenDownload.DOWNLOAD_FILM_NR + || i == DatenDownload.DOWNLOAD_HISTORY_URL + || i == DatenDownload.DOWNLOAD_PROGRESS + || i == DatenDownload.DOWNLOAD_RESTZEIT; + } + + private void addExtraFeld(int i, GridBagConstraints c) { + //Label + c.gridx = 0; + c.weightx = 0; + if (doNotShow(i)) { return; } @@ -210,7 +286,7 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { } else { switch (i) { case DatenDownload.DOWNLOAD_PROGRAMM_RESTART: - labelListe[i].setForeground(Color.BLUE); + labelListe[i].setForeground(MVColor.getBlueColor()); jCheckBoxRestart.setSelected(datenDownload.isRestart()); jCheckBoxRestart.addActionListener(new BeobCheckbox()); jCheckBoxRestart.setEnabled(!gestartet); @@ -241,7 +317,7 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { if (!datenDownload.isInterrupted()) { return; } - labelListe[i].setForeground(Color.BLUE); + labelListe[i].setForeground(MVColor.getBlueColor()); jCheckBoxUnterbrochen.setSelected(datenDownload.isInterrupted()); jCheckBoxUnterbrochen.addActionListener(new BeobCheckbox()); jCheckBoxUnterbrochen.setEnabled(!gestartet); @@ -253,7 +329,7 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { jPanelExtra.add(jCheckBoxUnterbrochen); break; case DatenDownload.DOWNLOAD_INFODATEI: - labelListe[i].setForeground(Color.BLUE); + labelListe[i].setForeground(MVColor.getBlueColor()); jCheckBoxInfodatei.setSelected(Boolean.parseBoolean(datenDownload.arr[DatenDownload.DOWNLOAD_INFODATEI])); jCheckBoxInfodatei.addActionListener(new BeobCheckbox()); jCheckBoxInfodatei.setEnabled(!gestartet); @@ -265,7 +341,7 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { jPanelExtra.add(jCheckBoxInfodatei); break; case DatenDownload.DOWNLOAD_SUBTITLE: - labelListe[i].setForeground(Color.BLUE); + labelListe[i].setForeground(MVColor.getBlueColor()); jCheckBoxSubtitle.setSelected(Boolean.parseBoolean(datenDownload.arr[DatenDownload.DOWNLOAD_SUBTITLE])); jCheckBoxSubtitle.addActionListener(new BeobCheckbox()); jCheckBoxSubtitle.setEnabled(!gestartet); @@ -277,7 +353,7 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { jPanelExtra.add(jCheckBoxSubtitle); break; case DatenDownload.DOWNLOAD_SPOTLIGHT: - labelListe[i].setForeground(Color.BLUE); + labelListe[i].setForeground(MVColor.getBlueColor()); jCheckBoxSpotlight.setSelected(Boolean.parseBoolean(datenDownload.arr[DatenDownload.DOWNLOAD_SPOTLIGHT])); jCheckBoxSpotlight.addActionListener(new BeobCheckbox()); jCheckBoxSpotlight.setEnabled(!gestartet); @@ -289,33 +365,31 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { jPanelExtra.add(jCheckBoxSpotlight); break; case DatenDownload.DOWNLOAD_HD: - jLabelFilmHD.setOpaque(false); - jLabelFilmHD.setIcon(ja_sw_16); + cbHighQuality.setSelected(true); gridbag.setConstraints(labelListe[i], c); jPanelExtra.add(labelListe[i]); c.gridx = 1; c.weightx = 10; - gridbag.setConstraints(jLabelFilmHD, c); - jPanelExtra.add(jLabelFilmHD); + gridbag.setConstraints(cbHighQuality, c); + jPanelExtra.add(cbHighQuality); if (datenDownload.film != null) { - jLabelFilmHD.setVisible(datenDownload.film.isHighQuality()); + cbHighQuality.setVisible(datenDownload.film.isHighQuality()); } else { - jLabelFilmHD.setVisible(false); + cbHighQuality.setVisible(false); } break; case DatenDownload.DOWNLOAD_UT: - jLabelFilmUT.setOpaque(false); - jLabelFilmUT.setIcon(ja_sw_16); + cbSubtitleAvailable.setSelected(true); gridbag.setConstraints(labelListe[i], c); jPanelExtra.add(labelListe[i]); c.gridx = 1; c.weightx = 10; - gridbag.setConstraints(jLabelFilmUT, c); - jPanelExtra.add(jLabelFilmUT); + gridbag.setConstraints(cbSubtitleAvailable, c); + jPanelExtra.add(cbSubtitleAvailable); if (datenDownload.film != null) { - jLabelFilmUT.setVisible(datenDownload.film.hasSubtitle()); + cbSubtitleAvailable.setVisible(datenDownload.film.hasSubtitle()); } else { - jLabelFilmUT.setVisible(false); + cbSubtitleAvailable.setVisible(false); } break; case DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF: @@ -325,7 +399,7 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { // nur bei Downloads über ein Programm if (datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY].isEmpty()) { // Aufruf über Array ist leer -> Win, Mac - labelListe[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY].setForeground(Color.BLUE); + labelListe[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY].setForeground(MVColor.getBlueColor()); textfeldListe[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF].setEditable(!gestartet);// und wenn noch nicht gestartet textfeldListe[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF].getDocument().addDocumentListener(new BeobachterDocumentTextfeld(DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF)); gridbag.setConstraints(labelListe[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY], c); @@ -336,7 +410,7 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { jPanelExtra.add(textfeldListe[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF]); } else { // dann ist ein Array vorhanden -> Linux - labelListe[i].setForeground(Color.BLUE); + labelListe[i].setForeground(MVColor.getBlueColor()); textfeldListe[i].setEditable(!gestartet);// und wenn noch nicht gestartet textfeldListe[i].getDocument().addDocumentListener(new BeobachterDocumentTextfeld(i)); gridbag.setConstraints(labelListe[i], c); @@ -351,10 +425,10 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { JButton jButtonReset = new JButton(""); jButtonReset.setToolTipText("Reset"); - jButtonReset.setIcon(IconFontSwing.buildIcon(FontAwesome.REFRESH, 16)); + jButtonReset.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrows-rotate.svg")); jButtonReset.addActionListener(e -> textfeldListe[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY].setText(orgProgArray)); JButton jButtonHelp = new JButton(""); - jButtonHelp.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHelp.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonHelp.setToolTipText("Hilfe anzeigen"); jButtonHelp.addActionListener(e -> new DialogHilfe(parent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_EDIT_DOWNLOAD_PROG)).setVisible(true)); @@ -387,47 +461,47 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { break; default: switch (i) { - case DatenDownload.DOWNLOAD_NR: - textfeldListe[i].setText(String.valueOf(datenDownload.nr)); - break; - case DatenDownload.DOWNLOAD_FILM_NR: + case DatenDownload.DOWNLOAD_NR -> textfeldListe[i].setText(String.valueOf(datenDownload.nr)); + case DatenDownload.DOWNLOAD_FILM_NR -> { if (datenDownload.film != null) { textfeldListe[i].setText(String.valueOf(datenDownload.film.getFilmNr())); } - break; - case DatenDownload.DOWNLOAD_URL: + } + case DatenDownload.DOWNLOAD_URL -> { if (datenDownload.art == DatenDownload.ART_DOWNLOAD) { // nur bei direkten Downloads - labelListe[i].setForeground(Color.BLUE); + labelListe[i].setForeground(MVColor.getBlueColor()); textfeldListe[i].setEditable(!gestartet);// und wenn noch nicht gestartet textfeldListe[i].getDocument().addDocumentListener(new BeobachterDocumentTextfeld(i)); } - break; - case DatenDownload.DOWNLOAD_GROESSE: - textfeldListe[i].setText(datenDownload.mVFilmSize.toString()); - break; - case DatenDownload.DOWNLOAD_PROGRESS: - textfeldListe[i].setText(Start.getTextProgress(datenDownload.isDownloadManager(), datenDownload.start)); - break; - case DatenDownload.DOWNLOAD_RESTZEIT: - textfeldListe[i].setText(datenDownload.getTextRestzeit()); - break; - case DatenDownload.DOWNLOAD_ART: + } + case DatenDownload.DOWNLOAD_GROESSE -> + textfeldListe[i].setText(datenDownload.mVFilmSize.toString()); + case DatenDownload.DOWNLOAD_PROGRESS -> + textfeldListe[i].setText(Start.getTextProgress(datenDownload.isDownloadManager(), datenDownload.start)); + case DatenDownload.DOWNLOAD_RESTZEIT -> + textfeldListe[i].setText(datenDownload.getTextRestzeit()); + case DatenDownload.DOWNLOAD_ART -> { switch (datenDownload.art) { - case DatenDownload.ART_DOWNLOAD -> textfeldListe[i].setText(DatenDownload.ART_DOWNLOAD_TXT); - case DatenDownload.ART_PROGRAMM -> textfeldListe[i].setText(DatenDownload.ART_PROGRAMM_TXT); + case DatenDownload.ART_DOWNLOAD -> + textfeldListe[i].setText(DatenDownload.ART_DOWNLOAD_TXT); + case DatenDownload.ART_PROGRAMM -> + textfeldListe[i].setText(DatenDownload.ART_PROGRAMM_TXT); } - break; - case DatenDownload.DOWNLOAD_QUELLE: + } + case DatenDownload.DOWNLOAD_QUELLE -> { switch (datenDownload.quelle) { - case DatenDownload.QUELLE_ALLE -> textfeldListe[i].setText(DatenDownload.QUELLE_ALLE_TXT); + case DatenDownload.QUELLE_ALLE -> + textfeldListe[i].setText(DatenDownload.QUELLE_ALLE_TXT); case DatenDownload.QUELLE_ABO -> textfeldListe[i].setText(DatenDownload.QUELLE_ABO_TXT); - case DatenDownload.QUELLE_BUTTON -> textfeldListe[i].setText(DatenDownload.QUELLE_BUTTON_TXT); - case DatenDownload.QUELLE_DOWNLOAD -> textfeldListe[i].setText(DatenDownload.QUELLE_DOWNLOAD_TXT); + case DatenDownload.QUELLE_BUTTON -> + textfeldListe[i].setText(DatenDownload.QUELLE_BUTTON_TXT); + case DatenDownload.QUELLE_DOWNLOAD -> + textfeldListe[i].setText(DatenDownload.QUELLE_DOWNLOAD_TXT); } - break; - default: - break; + } + default -> { + } } gridbag.setConstraints(labelListe[i], c); jPanelExtra.add(labelListe[i]); @@ -482,6 +556,51 @@ private boolean check() { return ok; } + private class BeobachterDocumentTextfeld implements DocumentListener { + + int nr; + + public BeobachterDocumentTextfeld(int n) { + nr = n; + } + + @Override + public void insertUpdate(DocumentEvent arg0) { + eingabe(); + } + + @Override + public void removeUpdate(DocumentEvent arg0) { + eingabe(); + } + + @Override + public void changedUpdate(DocumentEvent arg0) { + eingabe(); + } + + private void eingabe() { + datenDownload.arr[nr] = textfeldListe[nr].getText().trim(); + if (nr == DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY) { + datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF] = DatenProg.makeProgAufrufArray(datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY]); + textfeldListe[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF].setText(datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF]); + } + } + } + + private class BeobCheckbox implements ActionListener { + + @Override + public void actionPerformed(ActionEvent e) { + datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_RESTART] = Boolean.toString(jCheckBoxRestart.isSelected()); + datenDownload.arr[DatenDownload.DOWNLOAD_UNTERBROCHEN] = Boolean.toString(jCheckBoxUnterbrochen.isSelected()); + datenDownload.arr[DatenDownload.DOWNLOAD_INFODATEI] = Boolean.toString(jCheckBoxInfodatei.isSelected()); + datenDownload.arr[DatenDownload.DOWNLOAD_SUBTITLE] = Boolean.toString(jCheckBoxSubtitle.isSelected()); + datenDownload.arr[DatenDownload.DOWNLOAD_SPOTLIGHT] = Boolean.toString(jCheckBoxSpotlight.isSelected()); + } + } + + /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is @@ -491,18 +610,18 @@ private boolean check() { private void initComponents() { javax.swing.ButtonGroup buttonGroup1 = new javax.swing.ButtonGroup(); - jScrollPane1 = new javax.swing.JScrollPane(); + javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane(); jPanelExtra = new javax.swing.JPanel(); - jButtonOk = new javax.swing.JButton(); - jButtonAbbrechen = new javax.swing.JButton(); jPanelRes = new javax.swing.JPanel(); - javax.swing.JLabel jLabelRes = new javax.swing.JLabel(); jRadioButtonResHd = new javax.swing.JRadioButton(); jRadioButtonResHi = new javax.swing.JRadioButton(); jRadioButtonResLo = new javax.swing.JRadioButton(); + javax.swing.JPanel jPanel1 = new javax.swing.JPanel(); + jButtonOk = new javax.swing.JButton(); + jButtonAbbrechen = new javax.swing.JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - setTitle("Download editieren"); + setTitle("Download ändern"); setMinimumSize(new java.awt.Dimension(500, 0)); javax.swing.GroupLayout jPanelExtraLayout = new javax.swing.GroupLayout(jPanelExtra); @@ -518,49 +637,26 @@ private void initComponents() { jScrollPane1.setViewportView(jPanelExtra); - jButtonOk.setText("Ok"); - - jButtonAbbrechen.setText("Abbrechen"); - - jPanelRes.setBorder(javax.swing.BorderFactory.createTitledBorder("")); - - jLabelRes.setText("Download-Qualität:"); + jPanelRes.setBorder(javax.swing.BorderFactory.createTitledBorder("Download-Qualität:")); + jPanelRes.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 10, 5)); buttonGroup1.add(jRadioButtonResHd); jRadioButtonResHd.setText("Höchste/Hoch"); + jPanelRes.add(jRadioButtonResHd); buttonGroup1.add(jRadioButtonResHi); jRadioButtonResHi.setText("Mittel"); + jPanelRes.add(jRadioButtonResHi); buttonGroup1.add(jRadioButtonResLo); jRadioButtonResLo.setText("Niedrig"); + jPanelRes.add(jRadioButtonResLo); - javax.swing.GroupLayout jPanelResLayout = new javax.swing.GroupLayout(jPanelRes); - jPanelRes.setLayout(jPanelResLayout); - jPanelResLayout.setHorizontalGroup( - jPanelResLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanelResLayout.createSequentialGroup() - .addContainerGap() - .addComponent(jLabelRes) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jRadioButtonResHd) - .addGap(18, 18, 18) - .addComponent(jRadioButtonResHi) - .addGap(18, 18, 18) - .addComponent(jRadioButtonResLo) - .addContainerGap(304, Short.MAX_VALUE)) - ); - jPanelResLayout.setVerticalGroup( - jPanelResLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanelResLayout.createSequentialGroup() - .addContainerGap() - .addGroup(jPanelResLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabelRes) - .addComponent(jRadioButtonResHd) - .addComponent(jRadioButtonResHi) - .addComponent(jRadioButtonResLo)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); + jButtonOk.setText("Ok"); + jPanel1.add(jButtonOk); + + jButtonAbbrechen.setText("Abbrechen"); + jPanel1.add(jButtonAbbrechen); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); @@ -573,25 +669,18 @@ private void initComponents() { .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) - .addComponent(jButtonOk, javax.swing.GroupLayout.PREFERRED_SIZE, 93, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jButtonAbbrechen))) + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addContainerGap()) ); - - layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {jButtonAbbrechen, jButtonOk}); - layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 577, Short.MAX_VALUE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 547, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(jPanelRes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButtonOk) - .addComponent(jButtonAbbrechen)) + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) ); @@ -605,50 +694,5 @@ private void initComponents() { private javax.swing.JRadioButton jRadioButtonResHd; private javax.swing.JRadioButton jRadioButtonResHi; private javax.swing.JRadioButton jRadioButtonResLo; - private javax.swing.JScrollPane jScrollPane1; // End of variables declaration//GEN-END:variables - - private class BeobachterDocumentTextfeld implements DocumentListener { - - int nr; - - public BeobachterDocumentTextfeld(int n) { - nr = n; - } - - @Override - public void insertUpdate(DocumentEvent arg0) { - eingabe(); - } - - @Override - public void removeUpdate(DocumentEvent arg0) { - eingabe(); - } - - @Override - public void changedUpdate(DocumentEvent arg0) { - eingabe(); - } - - private void eingabe() { - datenDownload.arr[nr] = textfeldListe[nr].getText().trim(); - if (nr == DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY) { - datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF] = DatenProg.makeProgAufrufArray(datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY]); - textfeldListe[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF].setText(datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF]); - } - } - } - - private class BeobCheckbox implements ActionListener { - - @Override - public void actionPerformed(ActionEvent e) { - datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_RESTART] = Boolean.toString(jCheckBoxRestart.isSelected()); - datenDownload.arr[DatenDownload.DOWNLOAD_UNTERBROCHEN] = Boolean.toString(jCheckBoxUnterbrochen.isSelected()); - datenDownload.arr[DatenDownload.DOWNLOAD_INFODATEI] = Boolean.toString(jCheckBoxInfodatei.isSelected()); - datenDownload.arr[DatenDownload.DOWNLOAD_SUBTITLE] = Boolean.toString(jCheckBoxSubtitle.isSelected()); - datenDownload.arr[DatenDownload.DOWNLOAD_SPOTLIGHT] = Boolean.toString(jCheckBoxSpotlight.isSelected()); - } - } } diff --git a/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java b/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java index ca70fe9d75..d0d563bc0b 100644 --- a/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java +++ b/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java @@ -1,9 +1,5 @@ package mediathek.gui.dialog; -import javafx.application.Platform; -import javafx.scene.control.Alert; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.MVConfig; @@ -11,11 +7,8 @@ import mediathek.daten.DatenFilm; import mediathek.daten.DatenPset; import mediathek.daten.ListePset; -import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.javafx.tool.JavaFxUtils; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; -import mediathek.tool.javafx.FXErrorDialog; import net.miginfocom.layout.AC; import net.miginfocom.layout.CC; import net.miginfocom.layout.LC; @@ -51,16 +44,15 @@ public DialogFilmBeschreibung(JFrame parent, DatenFilm datenFilm) { dispose(); }); - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); - jButtonHilfe.addActionListener(e -> Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setHeaderText("Hilfe zu " + TITLE); - alert.setContentText(""" + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); + jButtonHilfe.addActionListener(e -> { + var message = """ Diese Funktion richtet sich z.B. an Benutzer,welche eine angepasste Beschreibung der Sendung in Form der Infodatei ("Filmname.txt") anlegen und durch Drittprogramme einlesen lassen wollen. - Achtung: Diese Änderungen gehen nach dem Neuladen einer Filmliste verloren."""); - JFXHiddenApplication.showAlert(alert, MediathekGui.ui()); - })); + Achtung: Diese Änderungen gehen nach dem Neuladen einer Filmliste verloren. + """; + JOptionPane.showMessageDialog(this, message, Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE); + }); jButtonSpeichern.addActionListener(e -> { datenFilm.setDescription(jTextArea1.getText()); @@ -93,15 +85,11 @@ Diese Funktion richtet sich z.B. an Benutzer,welche eine angepasste Beschreibung var url = HttpUrl.parse(datenFilm.getUrlNormalQuality()); file.writeInfoFile(datenFilm, path, url); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setHeaderText("Infodatei schreiben"); - alert.setContentText("Infodatei wurde erfolgreich geschrieben."); - JFXHiddenApplication.showAlert(alert, MediathekGui.ui()); - }); + JOptionPane.showMessageDialog(this, "Infodatei wurde erfolgreich geschrieben.", + Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE); } catch (IOException ex) { - JavaFxUtils.invokeInFxThreadAndWait(() -> FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, "Infodatei schreiben", "Ein unbekannter Fehler ist aufgetreten!", ex)); + SwingErrorDialog.showExceptionMessage(this,"Ein unbekannter Fehler ist aufgetreten!", ex); logger.error("Ziel: {}", path.toAbsolutePath().toString(), ex); } } diff --git a/src/main/java/mediathek/gui/dialog/DialogNewSet.java b/src/main/java/mediathek/gui/dialog/DialogNewSet.java index 7681a07532..73715ca058 100644 --- a/src/main/java/mediathek/gui/dialog/DialogNewSet.java +++ b/src/main/java/mediathek/gui/dialog/DialogNewSet.java @@ -1,16 +1,14 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.file.GetFile; import mediathek.tool.EscapeKeyHandler; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import javax.swing.border.TitledBorder; import java.awt.*; -@SuppressWarnings("serial") public class DialogNewSet extends JDialog { public boolean ok; public boolean morgen = true; @@ -64,7 +62,7 @@ Es werden alle Programmsets (auch eigene)\s beenden(); } }); - jButtonSetHelp.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonSetHelp.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonSetHelp.addActionListener(e -> new DialogHilfe(parent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_RESET_SET)).setVisible(true)); EscapeKeyHandler.installHandler(this, () -> { diff --git a/src/main/java/mediathek/gui/dialog/DialogProgrammOrdnerOeffnen.java b/src/main/java/mediathek/gui/dialog/DialogProgrammOrdnerOeffnen.java index f3a8bc6cfd..c7d2e351d6 100644 --- a/src/main/java/mediathek/gui/dialog/DialogProgrammOrdnerOeffnen.java +++ b/src/main/java/mediathek/gui/dialog/DialogProgrammOrdnerOeffnen.java @@ -1,9 +1,8 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.tool.EscapeKeyHandler; import mediathek.tool.MVMessageDialog; +import mediathek.tool.SVGIconUtilities; import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -24,7 +23,7 @@ public DialogProgrammOrdnerOeffnen(java.awt.Frame parent, boolean modal, String super(parent, modal); parentComponent = parent; initComponents(); - jButtonZiel.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonZiel.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); setTitle(titel); jTextArea1.setText(text); jButtonOk.addActionListener(l -> { diff --git a/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java b/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java index 46a682a436..d172adf5f5 100644 --- a/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java +++ b/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java @@ -1,15 +1,10 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.MVColor; import mediathek.config.MVConfig; import mediathek.config.StandardLocations; import mediathek.daten.DatenDownload; -import mediathek.tool.FileSpecifier; -import mediathek.tool.FilenameUtils; -import mediathek.tool.GuiFunktionen; -import mediathek.tool.MVMessageDialog; +import mediathek.tool.*; import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -40,8 +35,8 @@ public MVPanelDownloadZiel(JFrame p, DatenDownload download, boolean letzterPfad parent = p; datenDownload = download; letztenPfadAnzeigen = letzterPfad; - jButtonPath.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); - jButtonDelPath.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); + jButtonPath.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); + jButtonDelPath.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); jLabelExists.setText(""); jButtonPath.addActionListener(new ZielBeobachter()); jButtonDelPath.addActionListener(e -> { @@ -149,7 +144,7 @@ private void checkPfadName() { try { File file = new File(pfadName); if (file.exists()) { - jLabelExists.setForeground(MVColor.DOWNLOAD_DATEINAME_EXISTIERT.color); + jLabelExists.setForeground(Color.RED); jLabelExists.setText("Datei existiert schon!"); } else if (!jTextFieldName.getText().equals(datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_DATEINAME]) || !(((JTextComponent) jComboBoxPath.getEditor().getEditorComponent()).getText()).equals(datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_PFAD])) { diff --git a/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java b/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java index 96da4fa708..f315893b3b 100644 --- a/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java +++ b/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java @@ -1,18 +1,16 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Konstanten; import mediathek.config.MVConfig; import mediathek.daten.DatenDownload; import mediathek.tool.EscapeKeyHandler; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -@SuppressWarnings("serial") public class MeldungDownloadfehler extends JDialog { private final Timer countdownTimer; @@ -33,7 +31,7 @@ public MeldungDownloadfehler(Frame parent, String text, DatenDownload datenDownl jTextFieldTitel.setText(datenDownload.arr[DatenDownload.DOWNLOAD_TITEL]); jButtonOk.addActionListener(e -> dispose()); jLabelIcon.setText(""); - jLabelIcon.setIcon(IconFontSwing.buildIcon(FontAwesome.EXCLAMATION_TRIANGLE, 32)); + jLabelIcon.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/triangle-exclamation.svg", 32f)); //start the countdown... countdownTimer = new Timer(0, new CountdownAction()); diff --git a/src/main/java/mediathek/gui/dialog/MemoryMonitorDialog.java b/src/main/java/mediathek/gui/dialog/MemoryMonitorDialog.java new file mode 100644 index 0000000000..3ffa0db3bb --- /dev/null +++ b/src/main/java/mediathek/gui/dialog/MemoryMonitorDialog.java @@ -0,0 +1,86 @@ +package mediathek.gui.dialog; + +import mediathek.mainwindow.MemoryUsagePanel; +import mediathek.tool.ApplicationConfiguration; +import org.apache.commons.configuration2.sync.LockMode; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.util.NoSuchElementException; +import java.util.concurrent.TimeUnit; + +public class MemoryMonitorDialog extends JDialog { + public MemoryMonitorDialog(@NotNull JFrame parent) { + super(parent, "Speicherverbrauch", false); + setType(Type.UTILITY); + + MemoryUsagePanel panel = new MemoryUsagePanel(2, TimeUnit.MINUTES); + panel.setPreferredSize(new Dimension(480, 240)); + getContentPane().add(panel, BorderLayout.CENTER); + pack(); + panel.new MemoryUsageDataGenerator(1, TimeUnit.SECONDS).start(); + + restoreLocation(); + + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + saveLocation(); + } + + @Override + public void componentMoved(ComponentEvent e) { + saveLocation(); + } + + @Override + public void componentShown(ComponentEvent e) { + ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.MemoryMonitorDialog.VISIBLE, true); + } + + @Override + public void componentHidden(ComponentEvent e) { + ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.MemoryMonitorDialog.VISIBLE, false); + } + }); + } + + private void restoreLocation() { + var config = ApplicationConfiguration.getConfiguration(); + config.lock(LockMode.READ); + try { + var newLocation = new Point(); + newLocation.x = config.getInt(ApplicationConfiguration.MemoryMonitorDialog.X); + newLocation.y = config.getInt(ApplicationConfiguration.MemoryMonitorDialog.Y); + setLocation(newLocation); + + int w = config.getInt(ApplicationConfiguration.MemoryMonitorDialog.WIDTH, -1); + int h = config.getInt(ApplicationConfiguration.MemoryMonitorDialog.HEIGHT, -1); + if (w != -1 && h != -1) { + setPreferredSize(new Dimension(w, h)); + } + } catch (NoSuchElementException ignored) { + } finally { + config.unlock(LockMode.READ); + } + } + + private void saveLocation() { + if (!isVisible()) + return; + var config = ApplicationConfiguration.getConfiguration(); + try { + config.lock(LockMode.WRITE); + var location = getLocationOnScreen(); + config.setProperty(ApplicationConfiguration.MemoryMonitorDialog.X, location.x); + config.setProperty(ApplicationConfiguration.MemoryMonitorDialog.Y, location.y); + config.setProperty(ApplicationConfiguration.MemoryMonitorDialog.WIDTH, getWidth()); + config.setProperty(ApplicationConfiguration.MemoryMonitorDialog.HEIGHT, getHeight()); + } finally { + config.unlock(LockMode.WRITE); + } + } +} diff --git a/src/main/java/mediathek/gui/dialog/about/AboutController.kt b/src/main/java/mediathek/gui/dialog/about/AboutController.kt deleted file mode 100644 index 0edcf33d42..0000000000 --- a/src/main/java/mediathek/gui/dialog/about/AboutController.kt +++ /dev/null @@ -1,131 +0,0 @@ -package mediathek.gui.dialog.about - -import javafx.fxml.FXML -import javafx.fxml.FXMLLoader -import javafx.scene.control.Hyperlink -import javafx.scene.control.Label -import javafx.scene.layout.AnchorPane -import mediathek.config.Konstanten -import mediathek.gui.actions.UrlHyperlinkAction -import mediathek.mainwindow.MediathekGui -import org.apache.logging.log4j.LogManager -import java.io.IOException -import java.net.URI -import java.net.URISyntaxException -import javax.swing.SwingUtilities - -class AboutController : AnchorPane() { - companion object { - private val logger = LogManager.getLogger() - } - - @FXML - private lateinit var homepage: Hyperlink - - @FXML - private lateinit var serverDonationHyperlink: Hyperlink - - @FXML - private lateinit var forum: Hyperlink - - @FXML - private lateinit var anleitung: Hyperlink - - @FXML - private lateinit var developerDonationHyperlink: Hyperlink - - @FXML - private lateinit var version: Label - - @FXML - private fun homepageLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURI(MediathekGui.ui(), URI(Konstanten.ADRESSE_WEBSITE)) - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun developerDonationLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURL(MediathekGui.ui(), "https://paypal.me/ChristianFranzke") - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun serverDonationLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURI(MediathekGui.ui(), URI(Konstanten.ADRESSE_DONATION)) - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun forumLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURI(MediathekGui.ui(), URI(Konstanten.ADRESSE_FORUM)) - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun anleitungLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURI(MediathekGui.ui(), URI(Konstanten.ADRESSE_ANLEITUNG)) - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun jetbrainsLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURL(MediathekGui.ui(), "https://www.jetbrains.com") - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun ejLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURL(MediathekGui.ui(), "https://www.ej-technologies.com") - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - init { - - try { - val url = javaClass.getResource("/mediathek/res/programm/fxml/about.fxml") - val fxmlLoader = FXMLLoader(url) - fxmlLoader.setRoot(this) - fxmlLoader.setController(this) - fxmlLoader.load() - } catch (e: IOException) { - logger.error("Failed to load FXML!") - } - - version.text = "Version ${Konstanten.MVVERSION}" - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/dialog/about/AboutDialog.kt b/src/main/java/mediathek/gui/dialog/about/AboutDialog.kt deleted file mode 100644 index 0b517ba16a..0000000000 --- a/src/main/java/mediathek/gui/dialog/about/AboutDialog.kt +++ /dev/null @@ -1,25 +0,0 @@ -package mediathek.gui.dialog.about - -import javafx.embed.swing.JFXPanel -import javafx.scene.Scene -import mediathek.javafx.tool.JavaFxUtils -import mediathek.tool.EscapeKeyHandler -import java.awt.BorderLayout -import java.awt.Frame -import javax.swing.JDialog -import javax.swing.SwingUtilities - -class AboutDialog(owner: Frame?) : JDialog(owner, "Über dieses Programm", true) { - init { - defaultCloseOperation = DISPOSE_ON_CLOSE - isResizable = false - EscapeKeyHandler.installHandler(this) { dispose() } - contentPane.layout = BorderLayout() - val fxPanel = JFXPanel() - contentPane.add(fxPanel, BorderLayout.CENTER) - JavaFxUtils.invokeInFxThreadAndWait { - fxPanel.scene = Scene(AboutController()) - SwingUtilities.invokeLater { pack() } - } - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/dialog/reset/ResetSettingsPanel.java b/src/main/java/mediathek/gui/dialog/reset/ResetSettingsPanel.java index 10fdbe458e..cbff00d765 100644 --- a/src/main/java/mediathek/gui/dialog/reset/ResetSettingsPanel.java +++ b/src/main/java/mediathek/gui/dialog/reset/ResetSettingsPanel.java @@ -1,7 +1,5 @@ package mediathek.gui.dialog.reset; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.daten.ListePsetVorlagen; import mediathek.file.GetFile; @@ -10,6 +8,7 @@ import mediathek.mainwindow.MediathekGui; import mediathek.tool.GuiFunktionenProgramme; import mediathek.tool.MessageBus; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.swing.MultilineLabel; import org.jdesktop.swingx.VerticalLayout; @@ -25,7 +24,7 @@ public class ResetSettingsPanel extends JPanel { public ResetSettingsPanel(JFrame parent) { initComponents(); - jButtonHilfeReset.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHilfeReset.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonHilfeReset.addActionListener(e -> new DialogHilfe(parent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_RESET)).setVisible(true)); jButtonResetSets.addActionListener(e -> { Daten.listePset.clear(); @@ -38,7 +37,7 @@ public ResetSettingsPanel(JFrame parent) { // damit wird vor dem Beenden das Konfig-Verzeichnis umbenannt und so startet das // Programm wie beim ersten Start Daten.setResetConfigurationData(true); - MediathekGui.ui().beenden(false, false); + MediathekGui.ui().quitApplication(); } }); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/DaysSpinner.java b/src/main/java/mediathek/gui/dialogEinstellungen/DaysSpinner.java new file mode 100644 index 0000000000..a97c33e798 --- /dev/null +++ b/src/main/java/mediathek/gui/dialogEinstellungen/DaysSpinner.java @@ -0,0 +1,43 @@ +package mediathek.gui.dialogEinstellungen; + +import mediathek.tool.ApplicationConfiguration; + +import javax.swing.*; + +public class DaysSpinner extends JSpinner { + private final static String ALLE = " Alle "; + + public DaysSpinner() { + SpinnerListModel daySpinnerModel = new SpinnerListModel(new Object[]{ALLE, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "12", "14", "16", "18", "20", "25", "30", "60", "90", "180", "365"}); + setModel(daySpinnerModel); + ((JSpinner.DefaultEditor) getEditor()).getTextField().setEditable(false); + addChangeListener(l -> { + String s = getModel().getValue().toString(); + if (s.equals(ALLE)) { + s = "0"; + } + + int num_days; + try { + num_days = Integer.parseInt(s); + } catch (NumberFormatException e) { + num_days = 0; + } + ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.FilmList.LOAD_NUM_DAYS, num_days); + }); + loadConfigValues(); + } + + private void loadConfigValues() { + String s; + final int num_days = ApplicationConfiguration.getConfiguration().getInt(ApplicationConfiguration.FilmList.LOAD_NUM_DAYS, 0); + if (num_days == 0) + s = ALLE; + else + s = Integer.toString(num_days); + + setValue(s); + } + +} diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java index 803aafedba..b4693ee32b 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java @@ -1,9 +1,8 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.Konstanten; +import mediathek.config.MVColor; import mediathek.config.MVConfig; import mediathek.daten.blacklist.BlacklistRule; import mediathek.file.GetFile; @@ -49,8 +48,8 @@ public PanelBlacklist(Daten d, JFrame parentComponent, String nname) { initComponents(); name = nname; - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); - jButtonTabelleLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); + jButtonTabelleLoeschen.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); jButtonAendern.setEnabled(jTableBlacklist.getSelectionModel().getSelectedItemsCount() == 1); @@ -68,8 +67,7 @@ public PanelBlacklist(Daten d, JFrame parentComponent, String nname) { }); jCheckBoxGeo.addActionListener(e -> { - ApplicationConfiguration.getConfiguration(). - setProperty(ApplicationConfiguration.BLACKLIST_DO_NOT_SHOW_GEOBLOCKED_FILMS, jCheckBoxGeo.isSelected()); + ApplicationConfiguration.getInstance().setBlacklistDoNotShowGeoblockedFilms(jCheckBoxGeo.isSelected()); notifyBlacklistChanged(); }); @@ -154,8 +152,7 @@ private void init_() { jCheckBoxZukunftNichtAnzeigen.setSelected(Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_ZUKUNFT_NICHT_ANZEIGEN))); - var config = ApplicationConfiguration.getConfiguration(); - jCheckBoxGeo.setSelected(config.getBoolean(ApplicationConfiguration.BLACKLIST_DO_NOT_SHOW_GEOBLOCKED_FILMS,false)); + jCheckBoxGeo.setSelected(ApplicationConfiguration.getInstance().getBlacklistDoNotShowGeoblockedFilms()); try { jSliderMinuten.setValue(Integer.parseInt(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_FILMLAENGE))); @@ -234,8 +231,25 @@ public void changedUpdate(DocumentEvent e) { } private void tus() { - Filter.validatePatternInput(jTextFieldThemaTitel); - Filter.validatePatternInput(jTextFieldTitel); + validatePatternInput(jTextFieldThemaTitel); + validatePatternInput(jTextFieldTitel); + } + + /** + * Check if entry in JTextField is a regexp pattern and its validity. + * If a recognized pattern is invalid, change the background color of the JTextField. + * + * @param tf The control that will be validated + */ + private void validatePatternInput(JTextField tf) { + String text = tf.getText(); + if (Filter.isPattern(text)) { + tf.setForeground(MVColor.getRegExPatternColor()); + GuiFunktionen.showErrorIndication(tf, Filter.makePatternNoCache(text) == null); + } else { + GuiFunktionen.showErrorIndication(tf, false); + tf.setForeground(UIManager.getColor("TextField.foreground")); + } } }; jTextFieldTitel.getDocument().addDocumentListener(documentListener); @@ -315,8 +329,6 @@ private void comboThemaLaden() { for (String item : lst) model.addElement(item); jComboBoxThema.setModel(model); - - lst.clear(); } private void fillControlsWithRuleData() { diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java index bda855baab..49ea2025b3 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java @@ -1,16 +1,10 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; -import mediathek.config.Icons; import mediathek.config.MVConfig; import mediathek.gui.PanelVorlage; import mediathek.gui.messages.ReplaceListChangedEvent; -import mediathek.tool.MessageBus; -import mediathek.tool.NoSelectionErrorDialog; -import mediathek.tool.ReplaceList; -import mediathek.tool.TextCopyPasteHandler; +import mediathek.tool.*; import mediathek.tool.models.NonEditableTableModel; import net.engio.mbassy.listener.Handler; @@ -24,10 +18,8 @@ import javax.swing.table.DefaultTableModel; import java.awt.*; -@SuppressWarnings("serial") public class PanelDateinamen extends PanelVorlage { public boolean ok; - public String ziel = ""; @Handler private void handleReplaceListChange(ReplaceListChangedEvent e) { @@ -44,11 +36,11 @@ public PanelDateinamen(Daten d, JFrame pparentComponent) { jLabelAlert.setVisible(false); jLabelAlert.setText(""); - jLabelAlert.setIcon(IconFontSwing.buildIcon(FontAwesome.EXCLAMATION_TRIANGLE, 32)); - jButtonPlus.setIcon(Icons.ICON_BUTTON_ADD); - jButtonMinus.setIcon(Icons.ICON_BUTTON_REMOVE); - jButtonUp.setIcon(Icons.ICON_BUTTON_MOVE_UP); - jButtonDown.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); + jLabelAlert.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/triangle-exclamation.svg", 32f)); + jButtonPlus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); + jButtonMinus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); + jButtonUp.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-up.svg")); + jButtonDown.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-down.svg")); jButtonReset.addActionListener(e -> { ReplaceList.init(); tabelleLaden(); @@ -150,7 +142,7 @@ private void upDown(boolean auf) { tabelle.setRowSelectionInterval(neu, neu); tabelle.scrollRectToVisible(tabelle.getCellRect(neu, 0, true)); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenErweitert.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenErweitert.java index 69c5637e28..1e3b8e9c27 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenErweitert.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenErweitert.java @@ -1,13 +1,12 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Konstanten; import mediathek.config.MVConfig; import mediathek.gui.messages.ProgramLocationChangedEvent; import mediathek.mainwindow.MediathekGui; import mediathek.tool.MVMessageDialog; import mediathek.tool.MessageBus; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.TextCopyPasteHandler; import net.engio.mbassy.listener.Handler; import net.miginfocom.layout.AC; @@ -94,7 +93,7 @@ private void init() { } private void setIcon() { - var icon = IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16); + var icon = SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg"); jButtonProgrammDateimanager.setIcon(icon); jButtonProgrammVideoplayer.setIcon(icon); jButtonProgrammUrl.setIcon(icon); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.java index 504df270d7..d965735264 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.java @@ -1,20 +1,27 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; +import com.formdev.flatlaf.util.ScaledImageIcon; import mediathek.config.Daten; -import mediathek.daten.GeoblockingField; +import mediathek.daten.Country; import mediathek.file.GetFile; import mediathek.gui.dialog.DialogHilfe; import mediathek.gui.messages.BlacklistChangedEvent; import mediathek.gui.messages.GeoStateChangedEvent; import mediathek.tool.ApplicationConfiguration; +import mediathek.tool.GuiFunktionen; import mediathek.tool.MessageBus; -import org.apache.commons.configuration2.Configuration; +import mediathek.tool.SVGIconUtilities; +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; import org.jdesktop.swingx.VerticalLayout; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.border.TitledBorder; +import java.awt.*; +import java.util.Objects; public class PanelEinstellungenGeo extends JPanel { private final JFrame parentComponent; @@ -23,46 +30,62 @@ public PanelEinstellungenGeo(JFrame pparentComponent) { parentComponent = pparentComponent; initComponents(); + setCountryFlags(); init(); } - private void init() { - final Configuration config = ApplicationConfiguration.getConfiguration(); + private void setCountryFlags() { + lblIcon_DE.setIcon(getScaledIconResource("/icons/countries/162-germany.png")); + lblIcon_AT.setIcon(getScaledIconResource("/icons/countries/003-austria.png")); + lblIcon_CH.setIcon(getScaledIconResource("/icons/countries/205-switzerland.png")); + lblIcon_FR.setIcon(getScaledIconResource("/icons/countries/195-france.png")); + lblIcon_EU.setIcon(getScaledIconResource("/icons/countries/259-european-union.png")); + } + + private static final Dimension FLAG_DIMENSIONS = new Dimension(32,24); + + private ScaledImageIcon getScaledIconResource(@NotNull String url) { + var icon = new ImageIcon(Objects.requireNonNull(getClass().getResource(url))); + var imageDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); + var destDim = GuiFunktionen.calculateFittedDimension(imageDim, FLAG_DIMENSIONS); + return new ScaledImageIcon(icon, destDim.width, destDim.height); + } - switch (config.getString(ApplicationConfiguration.GEO_LOCATION)) { - case GeoblockingField.GEO_CH -> jRadioButtonCH.setSelected(true); - case GeoblockingField.GEO_AT -> jRadioButtonAt.setSelected(true); - case GeoblockingField.GEO_EU -> jRadioButtonEu.setSelected(true); - case GeoblockingField.GEO_WELT -> jRadioButtonSonst.setSelected(true); + private void init() { + switch (ApplicationConfiguration.getInstance().getGeographicLocation()) { + case CH -> jRadioButtonCH.setSelected(true); + case AT -> jRadioButtonAt.setSelected(true); + case EU -> jRadioButtonEu.setSelected(true); + case FR -> radioButtonFR.setSelected(true); + case OTHER -> jRadioButtonSonst.setSelected(true); default -> jRadioButtonDe.setSelected(true); } jRadioButtonDe.addActionListener(e -> { - config.setProperty(ApplicationConfiguration.GEO_LOCATION, GeoblockingField.GEO_DE); + ApplicationConfiguration.getInstance().setGeographicLocation(Country.DE); + filterBlacklistAndNotifyChanges(); + }); + radioButtonFR.addActionListener(e -> { + ApplicationConfiguration.getInstance().setGeographicLocation(Country.FR); filterBlacklistAndNotifyChanges(); }); jRadioButtonCH.addActionListener(e -> { - config.setProperty(ApplicationConfiguration.GEO_LOCATION, GeoblockingField.GEO_CH); + ApplicationConfiguration.getInstance().setGeographicLocation(Country.CH); filterBlacklistAndNotifyChanges(); }); jRadioButtonAt.addActionListener(e -> { - config.setProperty(ApplicationConfiguration.GEO_LOCATION, GeoblockingField.GEO_AT); + ApplicationConfiguration.getInstance().setGeographicLocation(Country.AT); filterBlacklistAndNotifyChanges(); }); jRadioButtonEu.addActionListener(e -> { - config.setProperty(ApplicationConfiguration.GEO_LOCATION, GeoblockingField.GEO_EU); + ApplicationConfiguration.getInstance().setGeographicLocation(Country.EU); filterBlacklistAndNotifyChanges(); }); jRadioButtonSonst.addActionListener(e -> { - config.setProperty(ApplicationConfiguration.GEO_LOCATION, GeoblockingField.GEO_WELT); + ApplicationConfiguration.getInstance().setGeographicLocation(Country.OTHER); filterBlacklistAndNotifyChanges(); }); - jCheckBoxMarkieren.setSelected(config.getBoolean(ApplicationConfiguration.GEO_REPORT)); - jCheckBoxMarkieren.addActionListener(e -> { - config.setProperty(ApplicationConfiguration.GEO_REPORT, jCheckBoxMarkieren.isSelected()); - filterBlacklistAndNotifyChanges(); - }); - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonHilfe.addActionListener(e -> new DialogHilfe(parentComponent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_GEO)).setVisible(true)); } @@ -85,117 +108,176 @@ private void filterBlacklistAndNotifyChanges() { // //GEN-BEGIN:initComponents // Generated using JFormDesigner non-commercial license private void initComponents() { - var jPanel6 = new JPanel(); - jCheckBoxMarkieren = new JCheckBox(); var panel1 = new JPanel(); + var panel2 = new JPanel(); jRadioButtonDe = new JRadioButton(); - jRadioButtonCH = new JRadioButton(); + lblIcon_DE = new JLabel(); + var label2 = new JLabel(); + var panel3 = new JPanel(); jRadioButtonAt = new JRadioButton(); + lblIcon_AT = new JLabel(); + var label4 = new JLabel(); + var panel4 = new JPanel(); + jRadioButtonCH = new JRadioButton(); + lblIcon_CH = new JLabel(); + var label6 = new JLabel(); + var panel5 = new JPanel(); + radioButtonFR = new JRadioButton(); + lblIcon_FR = new JLabel(); + var label8 = new JLabel(); + var panel6 = new JPanel(); jRadioButtonEu = new JRadioButton(); + lblIcon_EU = new JLabel(); + var label10 = new JLabel(); jRadioButtonSonst = new JRadioButton(); jButtonHilfe = new JButton(); //======== this ======== + setLayout(new MigLayout( + new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .grow().fill(), + // rows + new AC() + .gap() + .fill())); - //======== jPanel6 ======== + //======== panel1 ======== { - jPanel6.setBorder(new TitledBorder("Geogeblockte Filme")); //NON-NLS + panel1.setBorder(new TitledBorder("Mein Standort")); //NON-NLS + panel1.setLayout(new VerticalLayout()); - //---- jCheckBoxMarkieren ---- - jCheckBoxMarkieren.setText("geblockte Sendungen gelb markieren"); //NON-NLS - - //======== panel1 ======== + //======== panel2 ======== { - panel1.setBorder(new TitledBorder("Mein Standort")); //NON-NLS - panel1.setLayout(new VerticalLayout()); + panel2.setLayout(new MigLayout( + new LC().insets("0").hideMode(3), //NON-NLS + // columns + new AC() + .grow().align("left"), //NON-NLS + // rows + new AC() + .fill())); //---- jRadioButtonDe ---- jRadioButtonDe.setSelected(true); - jRadioButtonDe.setText("DE - Deutschland"); //NON-NLS - panel1.add(jRadioButtonDe); + panel2.add(jRadioButtonDe, new CC().cell(0, 0)); + panel2.add(lblIcon_DE, new CC().cell(0, 0)); + + //---- label2 ---- + label2.setText("Deutschland (DE)"); //NON-NLS + panel2.add(label2, new CC().cell(0, 0)); + } + panel1.add(panel2); - //---- jRadioButtonCH ---- - jRadioButtonCH.setText("CH - Schweiz"); //NON-NLS - panel1.add(jRadioButtonCH); + //======== panel3 ======== + { + panel3.setLayout(new MigLayout( + new LC().insets("0").hideMode(3), //NON-NLS + // columns + new AC() + .grow().align("left"), //NON-NLS + // rows + new AC() + .fill())); + panel3.add(jRadioButtonAt, new CC().cell(0, 0)); + panel3.add(lblIcon_AT, new CC().cell(0, 0)); + + //---- label4 ---- + label4.setText("\u00d6sterreich (AT)"); //NON-NLS + panel3.add(label4, new CC().cell(0, 0)); + } + panel1.add(panel3); + + //======== panel4 ======== + { + panel4.setLayout(new MigLayout( + new LC().insets("0").hideMode(3), //NON-NLS + // columns + new AC() + .grow().align("left"), //NON-NLS + // rows + new AC() + .fill())); + panel4.add(jRadioButtonCH, new CC().cell(0, 0)); + panel4.add(lblIcon_CH, new CC().cell(0, 0)); - //---- jRadioButtonAt ---- - jRadioButtonAt.setText("AT - \u00d6sterreich"); //NON-NLS - panel1.add(jRadioButtonAt); + //---- label6 ---- + label6.setText("Schweiz (CH)"); //NON-NLS + panel4.add(label6, new CC().cell(0, 0)); + } + panel1.add(panel4); - //---- jRadioButtonEu ---- - jRadioButtonEu.setText("EU (EBU - European Broadcasting Union)"); //NON-NLS - panel1.add(jRadioButtonEu); + //======== panel5 ======== + { + panel5.setLayout(new MigLayout( + new LC().insets("0").hideMode(3), //NON-NLS + // columns + new AC() + .grow().align("left"), //NON-NLS + // rows + new AC() + .fill())); + panel5.add(radioButtonFR, new CC().cell(0, 0)); + panel5.add(lblIcon_FR, new CC().cell(0, 0)); - //---- jRadioButtonSonst ---- - jRadioButtonSonst.setText("Sonstiger"); //NON-NLS - panel1.add(jRadioButtonSonst); + //---- label8 ---- + label8.setText("Frankreich (FR)"); //NON-NLS + panel5.add(label8, new CC().cell(0, 0)); } + panel1.add(panel5); - //---- jButtonHilfe ---- - jButtonHilfe.setIcon(new ImageIcon(getClass().getResource("/mediathek/res/muster/button-help.png"))); //NON-NLS - jButtonHilfe.setToolTipText("Hilfe anzeigen"); //NON-NLS - - GroupLayout jPanel6Layout = new GroupLayout(jPanel6); - jPanel6.setLayout(jPanel6Layout); - jPanel6Layout.setHorizontalGroup( - jPanel6Layout.createParallelGroup() - .addGroup(jPanel6Layout.createSequentialGroup() - .addContainerGap() - .addGroup(jPanel6Layout.createParallelGroup() - .addGroup(jPanel6Layout.createSequentialGroup() - .addComponent(jCheckBoxMarkieren) - .addGap(0, 150, Short.MAX_VALUE)) - .addComponent(panel1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(GroupLayout.Alignment.TRAILING, jPanel6Layout.createSequentialGroup() - .addGap(0, 331, Short.MAX_VALUE) - .addComponent(jButtonHilfe))) - .addContainerGap()) - ); - jPanel6Layout.setVerticalGroup( - jPanel6Layout.createParallelGroup() - .addGroup(jPanel6Layout.createSequentialGroup() - .addContainerGap() - .addComponent(jCheckBoxMarkieren) - .addGap(18, 18, 18) - .addComponent(panel1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButtonHilfe) - .addContainerGap()) - ); + //======== panel6 ======== + { + panel6.setLayout(new MigLayout( + new LC().insets("0").hideMode(3), //NON-NLS + // columns + new AC() + .grow().align("left"), //NON-NLS + // rows + new AC() + .fill())); + panel6.add(jRadioButtonEu, new CC().cell(0, 0)); + panel6.add(lblIcon_EU, new CC().cell(0, 0)); + + //---- label10 ---- + label10.setText("EU (EBU - European Broadcasting Union)"); //NON-NLS + panel6.add(label10, new CC().cell(0, 0)); + } + panel1.add(panel6); + + //---- jRadioButtonSonst ---- + jRadioButtonSonst.setText("Sonstiger"); //NON-NLS + panel1.add(jRadioButtonSonst); } + add(panel1, new CC().cell(0, 0)); - GroupLayout layout = new GroupLayout(this); - setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup() - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(jPanel6, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup() - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(jPanel6, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); + //---- jButtonHilfe ---- + jButtonHilfe.setIcon(null); + jButtonHilfe.setToolTipText("Hilfe anzeigen"); //NON-NLS + add(jButtonHilfe, new CC().cell(0, 1).alignX("right").growX(0)); //NON-NLS //---- buttonGroup1 ---- var buttonGroup1 = new ButtonGroup(); buttonGroup1.add(jRadioButtonDe); - buttonGroup1.add(jRadioButtonCH); buttonGroup1.add(jRadioButtonAt); + buttonGroup1.add(jRadioButtonCH); + buttonGroup1.add(radioButtonFR); buttonGroup1.add(jRadioButtonEu); buttonGroup1.add(jRadioButtonSonst); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables // Generated using JFormDesigner non-commercial license - private JCheckBox jCheckBoxMarkieren; private JRadioButton jRadioButtonDe; - private JRadioButton jRadioButtonCH; + private JLabel lblIcon_DE; private JRadioButton jRadioButtonAt; + private JLabel lblIcon_AT; + private JRadioButton jRadioButtonCH; + private JLabel lblIcon_CH; + private JRadioButton radioButtonFR; + private JLabel lblIcon_FR; private JRadioButton jRadioButtonEu; + private JLabel lblIcon_EU; private JRadioButton jRadioButtonSonst; private JButton jButtonHilfe; // End of variables declaration//GEN-END:variables diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.jfd b/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.jfd index 694e176719..e7a29669a7 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.jfd +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.jfd @@ -1,29 +1,26 @@ -JFDML JFormDesigner: "7.0.1.0.272" Java: "11.0.6" encoding: "UTF-8" +JFDML JFormDesigner: "8.0.1.0.199" Java: "17.0.5" encoding: "UTF-8" new FormModel { contentType: "form/swing" root: new FormRoot { - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { - "$horizontalGroup": "par l {seq l {space :::p, comp jPanel6:::::x, space :::p}}" - "$verticalGroup": "par l {seq l {space :::p, comp jPanel6:::p::p, space :::x}}" + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 5,hidemode 3,gap 5 5" + "$columnConstraints": "[grow,fill]" + "$rowConstraints": "[][fill]" } ) { name: "this" - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { - "$horizontalGroup": "par l {seq {space :::p, par l {seq {comp jCheckBoxMarkieren:::p::p, space :0:150:x}, comp panel1:::::x, seq t {space :0:331:x, comp jButtonHilfe:::p::p}}, space :::p}}" - "$verticalGroup": "par l {seq l {space :::p, comp jCheckBoxMarkieren:::p::p, space :p:18:p, comp panel1:::::x, space :::p, comp jButtonHilfe:::p::p, space :p::p}}" - } ) { - name: "jPanel6" - "border": new javax.swing.border.TitledBorder( "Geogeblockte Filme" ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.swingx.VerticalLayout ) ) { + name: "panel1" + "border": new javax.swing.border.TitledBorder( "Mein Standort" ) auxiliary() { "JavaCodeGenerator.variableLocal": true } - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "jCheckBoxMarkieren" - "text": "geblockte Sendungen gelb markieren" - } ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.swingx.VerticalLayout ) ) { - name: "panel1" - "border": new javax.swing.border.TitledBorder( "Mein Standort" ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 0,hidemode 3" + "$columnConstraints": "[grow,left]" + "$rowConstraints": "[fill]" + } ) { + name: "panel2" auxiliary() { "JavaCodeGenerator.variableLocal": true } @@ -31,37 +28,161 @@ new FormModel { name: "jRadioButtonDe" "$buttonGroup": new FormReference( "buttonGroup1" ) "selected": true - "text": "DE - Deutschland" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "lblIcon_DE" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label2" + "text": "Deutschland (DE)" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" } ) + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 0,hidemode 3" + "$columnConstraints": "[grow,left]" + "$rowConstraints": "[fill]" + } ) { + name: "panel3" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } add( new FormComponent( "javax.swing.JRadioButton" ) { - name: "jRadioButtonCH" + name: "jRadioButtonAt" "$buttonGroup": new FormReference( "buttonGroup1" ) - "text": "CH - Schweiz" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "lblIcon_AT" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label4" + "text": "Österreich (AT)" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 0,hidemode 3" + "$columnConstraints": "[grow,left]" + "$rowConstraints": "[fill]" + } ) { + name: "panel4" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } add( new FormComponent( "javax.swing.JRadioButton" ) { - name: "jRadioButtonAt" + name: "jRadioButtonCH" "$buttonGroup": new FormReference( "buttonGroup1" ) - "text": "AT - Österreich" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "lblIcon_CH" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label6" + "text": "Schweiz (CH)" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 0,hidemode 3" + "$columnConstraints": "[grow,left]" + "$rowConstraints": "[fill]" + } ) { + name: "panel5" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } add( new FormComponent( "javax.swing.JRadioButton" ) { - name: "jRadioButtonEu" + name: "radioButtonFR" "$buttonGroup": new FormReference( "buttonGroup1" ) - "text": "EU (EBU - European Broadcasting Union)" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "lblIcon_FR" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label8" + "text": "Frankreich (FR)" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" } ) + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 0,hidemode 3" + "$columnConstraints": "[grow,left]" + "$rowConstraints": "[fill]" + } ) { + name: "panel6" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } add( new FormComponent( "javax.swing.JRadioButton" ) { - name: "jRadioButtonSonst" + name: "jRadioButtonEu" "$buttonGroup": new FormReference( "buttonGroup1" ) - "text": "Sonstiger" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "lblIcon_EU" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label10" + "text": "EU (EBU - European Broadcasting Union)" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" } ) } ) - add( new FormComponent( "javax.swing.JButton" ) { - name: "jButtonHilfe" - "icon": new com.jformdesigner.model.SwingIcon( 0, "/mediathek/res/muster/button-help.png" ) - "toolTipText": "Hilfe anzeigen" + add( new FormComponent( "javax.swing.JRadioButton" ) { + name: "jRadioButtonSonst" + "$buttonGroup": new FormReference( "buttonGroup1" ) + "text": "Sonstiger" } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JButton" ) { + name: "jButtonHilfe" + "icon": sfield com.jformdesigner.model.FormObject NULL_VALUE + "toolTipText": "Hilfe anzeigen" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1,alignx right,growx 0" } ) }, new FormLayoutConstraints( null ) { - "size": new java.awt.Dimension( 445, 250 ) + "size": new java.awt.Dimension( 445, 200 ) "location": new java.awt.Point( 0, 0 ) } ) add( new FormNonVisual( "javax.swing.ButtonGroup" ) { diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.java index 9eda660652..a07fb7496a 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.java @@ -1,7 +1,6 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; +import mediathek.config.Daten; import mediathek.config.MVConfig; import mediathek.controller.SenderFilmlistLoadApprover; import mediathek.gui.messages.FilmListImportTypeChangedEvent; @@ -13,6 +12,7 @@ import net.miginfocom.layout.CC; import net.miginfocom.layout.LC; import net.miginfocom.swing.MigLayout; +import org.jdesktop.swingx.VerticalLayout; import javax.swing.*; import javax.swing.border.TitledBorder; @@ -28,16 +28,28 @@ public class PanelFilmlisteLaden extends JPanel { private final List senderCbList = new ArrayList<>(); + private void initReloadButton() { + btnReloadFilmlist.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrows-rotate.svg")); + btnReloadFilmlist.addActionListener(l -> { + final var daten = Daten.getInstance(); + daten.getListeFilme().clear(); // sonst wird evtl. nur eine Diff geladen + daten.getFilmeLaden().loadFilmlist("", false); + }); + } + public PanelFilmlisteLaden(boolean inSettingsDialog) { super(); MessageBus.getMessageBus().subscribe(this); initComponents(); + jButtonDateiAuswaehlen.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); init(); + initReloadButton(); setupCheckBoxes(); + btnReloadFilmlist.setVisible(inSettingsDialog); //in settings we cannot load a list, therefore these controls make no sense if (inSettingsDialog) { prepareSettingsLayout(); @@ -143,7 +155,7 @@ public boolean hasSenderSelectionChanged() { private void init() { initRadio(); - jButtonDateiAuswaehlen.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonDateiAuswaehlen.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonDateiAuswaehlen.addActionListener(l -> { var loadFile = FileDialogs.chooseLoadFileLocation(MediathekGui.ui(),"Filmliste laden", ""); if (loadFile != null) { @@ -228,6 +240,12 @@ private void initComponents() { jRadioButtonAuto = new JRadioButton(); jRadioButtonManuell = new JRadioButton(); var separator1 = new JSeparator(); + var panel3 = new JPanel(); + var panel2 = new JPanel(); + var label1 = new JLabel(); + var jSpinnerDays = new DaysSpinner(); + var label2 = new JLabel(); + btnReloadFilmlist = new JButton(); var jPanel1 = new JPanel(); cbSign = new JCheckBox(); cbTrailer = new JCheckBox(); @@ -262,10 +280,9 @@ private void initComponents() { checkBox21 = new JCheckBox(); //======== this ======== - setMinimumSize(null); - setPreferredSize(new Dimension(740, 450)); + setPreferredSize(new Dimension(740, 506)); setLayout(new MigLayout( - new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + new LC().fillX().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS // columns new AC() .align("label").gap() //NON-NLS @@ -275,7 +292,6 @@ private void initComponents() { .gap() .gap() .gap() - .gap() )); //======== jPanelAuto ======== @@ -301,6 +317,7 @@ private void initComponents() { //======== jPanelManuel ======== { jPanelManuel.setBorder(new TitledBorder("Filmliste nur manuell laden")); //NON-NLS + jPanelManuel.setMaximumSize(new Dimension(750, 2147483647)); jPanelManuel.setLayout(new MigLayout( new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS // columns @@ -326,7 +343,6 @@ private void initComponents() { jPanelManuel.add(jTextFieldUrl, new CC().cell(1, 1, 2, 1)); //---- jButtonDateiAuswaehlen ---- - jButtonDateiAuswaehlen.setIcon(new ImageIcon(getClass().getResource("/mediathek/res/muster/button-file-open.png"))); //NON-NLS jButtonDateiAuswaehlen.setToolTipText("URL oder lokale Filmliste ausw\u00e4hlen"); //NON-NLS jPanelManuel.add(jButtonDateiAuswaehlen, new CC().cell(3, 1).alignX("left").growX(0).width("32:32:32").height("32:32:32")); //NON-NLS @@ -339,167 +355,203 @@ private void initComponents() { add(jRadioButtonManuell, new CC().cell(0, 1).alignY("top").growY(0)); //NON-NLS add(separator1, new CC().cell(0, 2, 2, 1).growX()); - //======== jPanel1 ======== - { - jPanel1.setBorder(new TitledBorder("Zus\u00e4tzliche Filmdaten laden")); //NON-NLS - jPanel1.setToolTipText("Alle nicht angew\u00e4hlten Eintr\u00e4ge werden beim Laden der Filmliste aus dem Endergebnis herausgefiltert.
Die Eintr\u00e4ge werden dauerhaft aus der lokalen Filmliste entfernt.
Sie werden erst wieder beim Laden einer neuen Liste vom Server hinzugef\u00fcgt wenn die Einstellungen entsprechend angepasst wurden."); //NON-NLS - jPanel1.setLayout(new MigLayout( - new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS - // columns - new AC() - .fill().gap() - .fill().gap() - .fill().gap() - .fill(), - // rows - new AC() - .fill())); - - //---- cbSign ---- - cbSign.setText("Geb\u00e4rdensprache"); //NON-NLS - jPanel1.add(cbSign, new CC().cell(2, 0)); - - //---- cbTrailer ---- - cbTrailer.setText("Trailer/Teaser/Vorschau"); //NON-NLS - jPanel1.add(cbTrailer, new CC().cell(0, 0)); - - //---- cbAudio ---- - cbAudio.setText("H\u00f6rfassungen"); //NON-NLS - jPanel1.add(cbAudio, new CC().cell(1, 0)); - - //---- cbLivestreams ---- - cbLivestreams.setText("Livestreams"); //NON-NLS - jPanel1.add(cbLivestreams, new CC().cell(3, 0)); - } - add(jPanel1, new CC().cell(0, 3, 2, 1).growX()); - - //======== panel1 ======== + //======== panel3 ======== { - panel1.setBorder(new TitledBorder("Diese Sender laden (\u00c4nderungen erfordern Programmneustart und eine vollst\u00e4ndig neue Filmliste)")); //NON-NLS - panel1.setToolTipText("Die Einstellungen beziehen sich auf den n\u00e4chsten vollst\u00e4ndigen Ladevorgang einer Fillmliste.
Es kann somit vorkommen dass die Aktualisierung erst nach Neustart des Programms
und dem Laden einer kompletten Liste vom Server (keine DIFF-Liste!) sichtbar wird.

Hier ge\u00e4nderte Einstellungen werden in der Senderliste des Filterdialogs erst nach Neustart sichtbar!"); //NON-NLS - panel1.setLayout(new MigLayout( - new LC().insets("5").hideMode(3).alignX("left").gridGapX("10"), //NON-NLS - // columns - new AC() - .fill().gap() - .fill().gap() - .fill().gap() - .fill().gap() - .fill().gap() - .fill().gap() - .fill(), - // rows - new AC() - .gap() - .gap() - .gap() - )); - - //---- checkBox1 ---- - checkBox1.setText("3Sat"); //NON-NLS - panel1.add(checkBox1, new CC().cell(0, 0)); - - //---- checkBox24 ---- - checkBox24.setText("ARTE.ES"); //NON-NLS - panel1.add(checkBox24, new CC().cell(1, 0)); - - //---- checkBox2 ---- - checkBox2.setText("BR"); //NON-NLS - panel1.add(checkBox2, new CC().cell(2, 0)); - - //---- checkBox13 ---- - checkBox13.setText("KiKA"); //NON-NLS - panel1.add(checkBox13, new CC().cell(3, 0)); - - //---- checkBox16 ---- - checkBox16.setText("PHOENIX"); //NON-NLS - panel1.add(checkBox16, new CC().cell(4, 0)); - - //---- checkBox19 ---- - checkBox19.setText("SRF"); //NON-NLS - panel1.add(checkBox19, new CC().cell(5, 0)); - - //---- checkBox22 ---- - checkBox22.setText("ZDF"); //NON-NLS - panel1.add(checkBox22, new CC().cell(6, 0)); - - //---- checkBox8 ---- - checkBox8.setText("ARD"); //NON-NLS - panel1.add(checkBox8, new CC().cell(0, 1)); - - //---- checkBox10 ---- - checkBox10.setText("ARTE.FR"); //NON-NLS - panel1.add(checkBox10, new CC().cell(1, 1)); - - //---- checkBox11 ---- - checkBox11.setText("DW"); //NON-NLS - panel1.add(checkBox11, new CC().cell(2, 1)); - - //---- checkBox3 ---- - checkBox3.setText("MDR"); //NON-NLS - panel1.add(checkBox3, new CC().cell(3, 1)); - - //---- checkBox4 ---- - checkBox4.setText("Radio Bremen TV"); //NON-NLS - panel1.add(checkBox4, new CC().cell(4, 1)); - - //---- checkBox5 ---- - checkBox5.setText("SRF.Podcast"); //NON-NLS - panel1.add(checkBox5, new CC().cell(5, 1)); - - //---- checkBox6 ---- - checkBox6.setText("ZDF-tivi"); //NON-NLS - panel1.add(checkBox6, new CC().cell(6, 1)); - - //---- checkBox9 ---- - checkBox9.setText("ARTE.DE"); //NON-NLS - panel1.add(checkBox9, new CC().cell(0, 2)); - - //---- checkBox25 ---- - checkBox25.setText("ARTE.IT"); //NON-NLS - panel1.add(checkBox25, new CC().cell(1, 2)); - - //---- checkBox7 ---- - checkBox7.setText("Funk.net"); //NON-NLS - panel1.add(checkBox7, new CC().cell(2, 2)); - - //---- checkBox14 ---- - checkBox14.setText("NDR"); //NON-NLS - panel1.add(checkBox14, new CC().cell(3, 2)); - - //---- checkBox17 ---- - checkBox17.setText("RBB"); //NON-NLS - panel1.add(checkBox17, new CC().cell(4, 2)); - - //---- checkBox20 ---- - checkBox20.setText("SWR"); //NON-NLS - panel1.add(checkBox20, new CC().cell(5, 2)); - - //---- checkBox23 ---- - checkBox23.setText("ARTE.EN"); //NON-NLS - panel1.add(checkBox23, new CC().cell(0, 3)); - - //---- checkBox26 ---- - checkBox26.setText("ARTE.PL"); //NON-NLS - panel1.add(checkBox26, new CC().cell(1, 3)); - - //---- checkBox12 ---- - checkBox12.setText("HR"); //NON-NLS - panel1.add(checkBox12, new CC().cell(2, 3)); - - //---- checkBox15 ---- - checkBox15.setText("ORF"); //NON-NLS - panel1.add(checkBox15, new CC().cell(3, 3)); - - //---- checkBox18 ---- - checkBox18.setText("SR"); //NON-NLS - panel1.add(checkBox18, new CC().cell(4, 3)); + panel3.setBorder(new TitledBorder("Einschr\u00e4nkungen f\u00fcr das Laden der Filmliste")); //NON-NLS + panel3.setLayout(new VerticalLayout()); + + //======== panel2 ======== + { + panel2.setLayout(new MigLayout( + new LC().insets("5").hideMode(3), //NON-NLS + // columns + new AC() + .fill().gap() + .fill().gap() + .fill().gap() + .align("left"), //NON-NLS + // rows + new AC() + .fill())); + + //---- label1 ---- + label1.setText("Nur Filme der letzten"); //NON-NLS + panel2.add(label1, new CC().cell(0, 0).alignX("center").growX(0)); //NON-NLS + panel2.add(jSpinnerDays, new CC().cell(1, 0)); + + //---- label2 ---- + label2.setText("Tage laden."); //NON-NLS + panel2.add(label2, new CC().cell(2, 0).alignX("center").growX(0)); //NON-NLS + + //---- btnReloadFilmlist ---- + btnReloadFilmlist.setToolTipText("Filmliste jetzt aktualisieren"); //NON-NLS + panel2.add(btnReloadFilmlist, new CC().cell(3, 0)); + } + panel3.add(panel2); + + //======== jPanel1 ======== + { + jPanel1.setBorder(new TitledBorder("Zus\u00e4tzliche Filmdaten laden")); //NON-NLS + jPanel1.setToolTipText("Alle nicht angew\u00e4hlten Eintr\u00e4ge werden beim Laden der Filmliste aus dem Endergebnis herausgefiltert.
Die Eintr\u00e4ge werden dauerhaft aus der lokalen Filmliste entfernt.
Sie werden erst wieder beim Laden einer neuen Liste vom Server hinzugef\u00fcgt wenn die Einstellungen entsprechend angepasst wurden."); //NON-NLS + jPanel1.setLayout(new MigLayout( + new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .fill().gap() + .fill().gap() + .fill().gap() + .fill(), + // rows + new AC() + .fill())); + + //---- cbSign ---- + cbSign.setText("Geb\u00e4rdensprache"); //NON-NLS + jPanel1.add(cbSign, new CC().cell(2, 0)); + + //---- cbTrailer ---- + cbTrailer.setText("Trailer/Teaser/Vorschau"); //NON-NLS + jPanel1.add(cbTrailer, new CC().cell(0, 0)); + + //---- cbAudio ---- + cbAudio.setText("H\u00f6rfassungen"); //NON-NLS + jPanel1.add(cbAudio, new CC().cell(1, 0)); + + //---- cbLivestreams ---- + cbLivestreams.setText("Livestreams"); //NON-NLS + jPanel1.add(cbLivestreams, new CC().cell(3, 0)); + } + panel3.add(jPanel1); + + //======== panel1 ======== + { + panel1.setBorder(new TitledBorder("Diese Sender laden (\u00c4nderungen erfordern Programmneustart und eine vollst\u00e4ndig neue Filmliste)")); //NON-NLS + panel1.setToolTipText("Die Einstellungen beziehen sich auf den n\u00e4chsten vollst\u00e4ndigen Ladevorgang einer Fillmliste.
Es kann somit vorkommen dass die Aktualisierung erst nach Neustart des Programms
und dem Laden einer kompletten Liste vom Server (keine DIFF-Liste!) sichtbar wird.

Hier ge\u00e4nderte Einstellungen werden in der Senderliste des Filterdialogs erst nach Neustart sichtbar!"); //NON-NLS + panel1.setLayout(new MigLayout( + new LC().insets("5").hideMode(3).alignX("left").gridGapX("10"), //NON-NLS + // columns + new AC() + .fill().gap() + .fill().gap() + .fill().gap() + .fill().gap() + .fill().gap() + .fill().gap() + .fill(), + // rows + new AC() + .gap() + .gap() + .gap() + )); + + //---- checkBox1 ---- + checkBox1.setText("3Sat"); //NON-NLS + panel1.add(checkBox1, new CC().cell(0, 0)); + + //---- checkBox24 ---- + checkBox24.setText("ARTE.ES"); //NON-NLS + panel1.add(checkBox24, new CC().cell(1, 0)); + + //---- checkBox2 ---- + checkBox2.setText("BR"); //NON-NLS + panel1.add(checkBox2, new CC().cell(2, 0)); + + //---- checkBox13 ---- + checkBox13.setText("KiKA"); //NON-NLS + panel1.add(checkBox13, new CC().cell(3, 0)); + + //---- checkBox16 ---- + checkBox16.setText("PHOENIX"); //NON-NLS + panel1.add(checkBox16, new CC().cell(4, 0)); + + //---- checkBox19 ---- + checkBox19.setText("SRF"); //NON-NLS + panel1.add(checkBox19, new CC().cell(5, 0)); + + //---- checkBox22 ---- + checkBox22.setText("ZDF"); //NON-NLS + panel1.add(checkBox22, new CC().cell(6, 0)); + + //---- checkBox8 ---- + checkBox8.setText("ARD"); //NON-NLS + panel1.add(checkBox8, new CC().cell(0, 1)); + + //---- checkBox10 ---- + checkBox10.setText("ARTE.FR"); //NON-NLS + panel1.add(checkBox10, new CC().cell(1, 1)); + + //---- checkBox11 ---- + checkBox11.setText("DW"); //NON-NLS + panel1.add(checkBox11, new CC().cell(2, 1)); + + //---- checkBox3 ---- + checkBox3.setText("MDR"); //NON-NLS + panel1.add(checkBox3, new CC().cell(3, 1)); + + //---- checkBox4 ---- + checkBox4.setText("Radio Bremen TV"); //NON-NLS + panel1.add(checkBox4, new CC().cell(4, 1)); + + //---- checkBox5 ---- + checkBox5.setText("SRF.Podcast"); //NON-NLS + panel1.add(checkBox5, new CC().cell(5, 1)); + + //---- checkBox6 ---- + checkBox6.setText("ZDF-tivi"); //NON-NLS + panel1.add(checkBox6, new CC().cell(6, 1)); + + //---- checkBox9 ---- + checkBox9.setText("ARTE.DE"); //NON-NLS + panel1.add(checkBox9, new CC().cell(0, 2)); + + //---- checkBox25 ---- + checkBox25.setText("ARTE.IT"); //NON-NLS + panel1.add(checkBox25, new CC().cell(1, 2)); + + //---- checkBox7 ---- + checkBox7.setText("Funk.net"); //NON-NLS + panel1.add(checkBox7, new CC().cell(2, 2)); + + //---- checkBox14 ---- + checkBox14.setText("NDR"); //NON-NLS + panel1.add(checkBox14, new CC().cell(3, 2)); + + //---- checkBox17 ---- + checkBox17.setText("RBB"); //NON-NLS + panel1.add(checkBox17, new CC().cell(4, 2)); + + //---- checkBox20 ---- + checkBox20.setText("SWR"); //NON-NLS + panel1.add(checkBox20, new CC().cell(5, 2)); + + //---- checkBox23 ---- + checkBox23.setText("ARTE.EN"); //NON-NLS + panel1.add(checkBox23, new CC().cell(0, 3)); + + //---- checkBox26 ---- + checkBox26.setText("ARTE.PL"); //NON-NLS + panel1.add(checkBox26, new CC().cell(1, 3)); + + //---- checkBox12 ---- + checkBox12.setText("HR"); //NON-NLS + panel1.add(checkBox12, new CC().cell(2, 3)); + + //---- checkBox15 ---- + checkBox15.setText("ORF"); //NON-NLS + panel1.add(checkBox15, new CC().cell(3, 3)); + + //---- checkBox18 ---- + checkBox18.setText("SR"); //NON-NLS + panel1.add(checkBox18, new CC().cell(4, 3)); - //---- checkBox21 ---- - checkBox21.setText("WDR"); //NON-NLS - panel1.add(checkBox21, new CC().cell(5, 3)); + //---- checkBox21 ---- + checkBox21.setText("WDR"); //NON-NLS + panel1.add(checkBox21, new CC().cell(5, 3)); + } + panel3.add(panel1); } - add(panel1, new CC().cell(0, 4, 2, 1).growX()); + add(panel3, new CC().cell(0, 3, 2, 1).growX()); //---- buttonGroup1 ---- var buttonGroup1 = new ButtonGroup(); @@ -515,6 +567,7 @@ private void initComponents() { private JCheckBox jCheckBoxUpdate; private JRadioButton jRadioButtonAuto; private JRadioButton jRadioButtonManuell; + private JButton btnReloadFilmlist; private JCheckBox cbSign; private JCheckBox cbTrailer; private JCheckBox cbAudio; diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.jfd b/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.jfd index 6eb4611012..aac6e69eed 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.jfd +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.jfd @@ -1,16 +1,15 @@ -JFDML JFormDesigner: "7.0.5.1.409" Java: "11.0.13" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.7.0.1134" Java: "11.0.15" encoding: "UTF-8" new FormModel { contentType: "form/swing" root: new FormRoot { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$layoutConstraints": "insets 5,hidemode 3,gap 5 5" + "$layoutConstraints": "fillx,insets 5,hidemode 3,gap 5 5" "$columnConstraints": "[label][640,grow,fill]" - "$rowConstraints": "[][][][][]" + "$rowConstraints": "[][][][]" } ) { name: "this" - "minimumSize": sfield com.jformdesigner.model.FormObject NULL_VALUE - "preferredSize": new java.awt.Dimension( 740, 450 ) + "preferredSize": new java.awt.Dimension( 740, 506 ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "insets 5,hidemode 3,gap 5 5" "$columnConstraints": "[grow,fill][grow,fill]" @@ -41,6 +40,7 @@ new FormModel { } ) { name: "jPanelManuel" "border": new javax.swing.border.TitledBorder( "Filmliste nur manuell laden" ) + "maximumSize": new java.awt.Dimension( 750, 2147483647 ) auxiliary() { "JavaCodeGenerator.variableLocal": true } @@ -73,7 +73,6 @@ new FormModel { } ) add( new FormComponent( "javax.swing.JButton" ) { name: "jButtonDateiAuswaehlen" - "icon": new com.jformdesigner.model.SwingIcon( 0, "/mediathek/res/muster/button-file-open.png" ) "toolTipText": "URL oder lokale Filmliste auswählen" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 3 1,alignx left,growx 0,width 32:32:32,height 32:32:32" @@ -110,213 +109,260 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 0 2 2 1,growx" } ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$layoutConstraints": "insets 5,hidemode 3,gap 5 5" - "$columnConstraints": "[fill][fill][fill][fill]" - "$rowConstraints": "[fill]" - } ) { - name: "jPanel1" - "border": new javax.swing.border.TitledBorder( "Zusätzliche Filmdaten laden" ) - "toolTipText": "Alle nicht angewählten Einträge werden beim Laden der Filmliste aus dem Endergebnis herausgefiltert.
Die Einträge werden dauerhaft aus der lokalen Filmliste entfernt.
Sie werden erst wieder beim Laden einer neuen Liste vom Server hinzugefügt wenn die Einstellungen entsprechend angepasst wurden." + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.swingx.VerticalLayout ) ) { + name: "panel3" + "border": new javax.swing.border.TitledBorder( "Einschränkungen für das Laden der Filmliste" ) auxiliary() { "JavaCodeGenerator.variableLocal": true } - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "cbSign" - "text": "Gebärdensprache" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 2 0" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "cbTrailer" - "text": "Trailer/Teaser/Vorschau" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "cbAudio" - "text": "Hörfassungen" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 0" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "cbLivestreams" - "text": "Livestreams" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 3 0" + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 5,hidemode 3" + "$columnConstraints": "[fill][fill][fill][left]" + "$rowConstraints": "[fill]" + } ) { + name: "panel2" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label1" + "text": "Nur Filme der letzten" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0,alignx center,growx 0" + } ) + add( new FormComponent( "mediathek.gui.dialogEinstellungen.DaysSpinner" ) { + name: "jSpinnerDays" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label2" + "text": "Tage laden." + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 0,alignx center,growx 0" + } ) + add( new FormComponent( "javax.swing.JButton" ) { + name: "btnReloadFilmlist" + "toolTipText": "Filmliste jetzt aktualisieren" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 0" + } ) + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 5,hidemode 3,gap 5 5" + "$columnConstraints": "[fill][fill][fill][fill]" + "$rowConstraints": "[fill]" + } ) { + name: "jPanel1" + "border": new javax.swing.border.TitledBorder( "Zusätzliche Filmdaten laden" ) + "toolTipText": "Alle nicht angewählten Einträge werden beim Laden der Filmliste aus dem Endergebnis herausgefiltert.
Die Einträge werden dauerhaft aus der lokalen Filmliste entfernt.
Sie werden erst wieder beim Laden einer neuen Liste vom Server hinzugefügt wenn die Einstellungen entsprechend angepasst wurden." + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "cbSign" + "text": "Gebärdensprache" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "cbTrailer" + "text": "Trailer/Teaser/Vorschau" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "cbAudio" + "text": "Hörfassungen" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "cbLivestreams" + "text": "Livestreams" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 0" + } ) + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 5,hidemode 3,alignx left,gapx 10" + "$columnConstraints": "[fill][fill][fill][fill][fill][fill][fill]" + "$rowConstraints": "[][][][]" + } ) { + name: "panel1" + "border": new javax.swing.border.TitledBorder( "Diese Sender laden (Änderungen erfordern Programmneustart und eine vollständig neue Filmliste)" ) + "toolTipText": "Die Einstellungen beziehen sich auf den nächsten vollständigen Ladevorgang einer Fillmliste.
Es kann somit vorkommen dass die Aktualisierung erst nach Neustart des Programms
und dem Laden einer kompletten Liste vom Server (keine DIFF-Liste!) sichtbar wird.

Hier geänderte Einstellungen werden in der Senderliste des Filterdialogs erst nach Neustart sichtbar!" + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox1" + "text": "3Sat" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox24" + "text": "ARTE.ES" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox2" + "text": "BR" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox13" + "text": "KiKA" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox16" + "text": "PHOENIX" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 4 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox19" + "text": "SRF" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 5 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox22" + "text": "ZDF" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 6 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox8" + "text": "ARD" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox10" + "text": "ARTE.FR" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 1" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox11" + "text": "DW" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 1" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox3" + "text": "MDR" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 1" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox4" + "text": "Radio Bremen TV" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 4 1" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox5" + "text": "SRF.Podcast" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 5 1" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox6" + "text": "ZDF-tivi" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 6 1" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox9" + "text": "ARTE.DE" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox25" + "text": "ARTE.IT" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 2" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox7" + "text": "Funk.net" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 2" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox14" + "text": "NDR" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 2" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox17" + "text": "RBB" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 4 2" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox20" + "text": "SWR" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 5 2" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox23" + "text": "ARTE.EN" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 3" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox26" + "text": "ARTE.PL" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 3" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox12" + "text": "HR" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 3" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox15" + "text": "ORF" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 3" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox18" + "text": "SR" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 4 3" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "checkBox21" + "text": "WDR" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 5 3" + } ) } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 0 3 2 1,growx" } ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$layoutConstraints": "insets 5,hidemode 3,alignx left,gapx 10" - "$columnConstraints": "[fill][fill][fill][fill][fill][fill][fill]" - "$rowConstraints": "[][][][]" - } ) { - name: "panel1" - "border": new javax.swing.border.TitledBorder( "Diese Sender laden (Änderungen erfordern Programmneustart und eine vollständig neue Filmliste)" ) - "toolTipText": "Die Einstellungen beziehen sich auf den nächsten vollständigen Ladevorgang einer Fillmliste.
Es kann somit vorkommen dass die Aktualisierung erst nach Neustart des Programms
und dem Laden einer kompletten Liste vom Server (keine DIFF-Liste!) sichtbar wird.

Hier geänderte Einstellungen werden in der Senderliste des Filterdialogs erst nach Neustart sichtbar!" - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox1" - "text": "3Sat" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox24" - "text": "ARTE.ES" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 0" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox2" - "text": "BR" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 2 0" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox13" - "text": "KiKA" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 3 0" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox16" - "text": "PHOENIX" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 4 0" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox19" - "text": "SRF" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 5 0" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox22" - "text": "ZDF" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 6 0" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox8" - "text": "ARD" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 1" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox10" - "text": "ARTE.FR" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 1" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox11" - "text": "DW" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 2 1" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox3" - "text": "MDR" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 3 1" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox4" - "text": "Radio Bremen TV" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 4 1" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox5" - "text": "SRF.Podcast" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 5 1" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox6" - "text": "ZDF-tivi" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 6 1" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox9" - "text": "ARTE.DE" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 2" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox25" - "text": "ARTE.IT" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 2" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox7" - "text": "Funk.net" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 2 2" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox14" - "text": "NDR" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 3 2" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox17" - "text": "RBB" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 4 2" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox20" - "text": "SWR" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 5 2" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox23" - "text": "ARTE.EN" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 3" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox26" - "text": "ARTE.PL" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 3" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox12" - "text": "HR" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 2 3" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox15" - "text": "ORF" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 3 3" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox18" - "text": "SR" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 4 3" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "checkBox21" - "text": "WDR" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 5 3" - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 4 2 1,growx" - } ) }, new FormLayoutConstraints( null ) { - "size": new java.awt.Dimension( 740, 435 ) + "size": new java.awt.Dimension( 740, 515 ) "location": new java.awt.Point( 0, 0 ) } ) add( new FormNonVisual( "javax.swing.ButtonGroup" ) { diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java index c6a153d187..3f6c5bb66a 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java @@ -1,11 +1,10 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.MVConfig; import mediathek.file.GetFile; import mediathek.gui.dialog.DialogHilfe; import mediathek.tool.GuiFunktionenProgramme; +import mediathek.tool.SVGIconUtilities; import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -34,9 +33,9 @@ public PanelProgrammPfade(JFrame parentFrame, boolean vvlc, boolean fffmpeg) { } private void init() { - jButtonVlcPfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); - jButtonFFmpegPfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonVlcPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); + jButtonFFmpegPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jPanelVlc.setVisible(vlc); jPanelFFmpeg.setVisible(ffmpeg); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java index 008fca9f42..37e8a9e818 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java @@ -1,7 +1,5 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Config; import mediathek.config.Daten; import mediathek.daten.ListePset; @@ -9,6 +7,7 @@ import mediathek.gui.PanelVorlage; import mediathek.mainwindow.MediathekGui; import mediathek.tool.GuiFunktionenProgramme; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.TextCopyPasteHandler; import mediathek.tool.models.TModel; import org.apache.commons.lang3.SystemUtils; @@ -36,8 +35,8 @@ public PanelPsetImport(Daten d, JFrame parentComponent) { } private void init() { - jButtonAktualisieren.setIcon(IconFontSwing.buildIcon(FontAwesome.REFRESH, 16)); - jButtonPfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonAktualisieren.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrows-rotate.svg")); + jButtonPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jComboBoxBs.setModel(new DefaultComboBoxModel<>(ListePsetVorlagen.BS)); jComboBoxBs.addActionListener(e -> tabelleLaden()); jButtonImportDatei.setEnabled(false); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetKurz.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetKurz.java index 53d4d727ae..1e38d85ce8 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetKurz.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetKurz.java @@ -1,7 +1,5 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.daten.DatenProg; import mediathek.daten.DatenPset; @@ -10,6 +8,7 @@ import mediathek.gui.messages.ProgramSetChangedEvent; import mediathek.mainwindow.MediathekGui; import mediathek.tool.MessageBus; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.TextCopyPasteHandler; import net.engio.mbassy.listener.Handler; import org.apache.commons.lang3.SystemUtils; @@ -34,7 +33,7 @@ public class PanelPsetKurz extends PanelVorlage { public PanelPsetKurz(Daten d, JFrame parentComponent, ListePset llistePset) { super(d, parentComponent); initComponents(); - jButtonZiel.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonZiel.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); listePset = llistePset; jListPset.setModel(new DefaultComboBoxModel<>(listePset.getObjectDataCombo())); if (!listePset.isEmpty()) { @@ -176,7 +175,7 @@ private void setFeld(JPanel panel, String name, String[] arr) { c.gridx = 2; c.weightx = 0; JButton button = new JButton(); - button.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + button.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); button.addActionListener(new ZielBeobachter(textField, arr, DatenProg.PROGRAMM_PROGRAMMPFAD)); button.setToolTipText("Programm auswählen"); gridbag.setConstraints(button, c); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java index ce85373ae4..a20840a70c 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java @@ -1,9 +1,8 @@ package mediathek.gui.dialogEinstellungen; -import javafx.scene.control.Alert; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; -import mediathek.config.*; +import mediathek.config.Daten; +import mediathek.config.Konstanten; +import mediathek.config.MVConfig; import mediathek.controller.IoXmlSchreiben; import mediathek.controller.starter.RuntimeExec; import mediathek.daten.DatenProg; @@ -14,8 +13,6 @@ import mediathek.gui.PanelVorlage; import mediathek.gui.dialog.DialogHilfe; import mediathek.gui.messages.ProgramSetChangedEvent; -import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.javafx.tool.JavaFxUtils; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; import mediathek.tool.cellrenderer.CellRendererProgramme; @@ -74,19 +71,19 @@ private void handleProgramSetChanged(ProgramSetChangedEvent e) { } private void init() { - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); - jButtonGruppePfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); - jButtonProgPlus.setIcon(Icons.ICON_BUTTON_ADD); - jButtonProgMinus.setIcon(Icons.ICON_BUTTON_REMOVE); - jButtonProgAuf.setIcon(Icons.ICON_BUTTON_MOVE_UP); - jButtonProgAb.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); - jButtonProgPfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); - jButtonGruppeNeu.setIcon(Icons.ICON_BUTTON_ADD); - jButtonGruppeLoeschen.setIcon(Icons.ICON_BUTTON_REMOVE); - jButtonGruppeAuf.setIcon(Icons.ICON_BUTTON_MOVE_UP); - jButtonGruppeAb.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); - - var exclamationIcon = IconFontSwing.buildIcon(FontAwesome.EXCLAMATION_TRIANGLE, 16); + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); + jButtonGruppePfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); + jButtonProgPlus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); + jButtonProgMinus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); + jButtonProgAuf.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-up.svg")); + jButtonProgAb.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-down.svg")); + jButtonProgPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); + jButtonGruppeNeu.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); + jButtonGruppeLoeschen.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); + jButtonGruppeAuf.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-up.svg")); + jButtonGruppeAb.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-down.svg")); + + var exclamationIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/triangle-exclamation.svg"); jLabelMeldungAbspielen.setIcon(exclamationIcon); jLabelMeldungSeichern.setIcon(exclamationIcon); @@ -164,7 +161,7 @@ private void init() { tabelleProgramme(); } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } }); @@ -175,7 +172,7 @@ private void init() { DatenProg prog = getPset().getListeProg().get(row); progNeueZeile(prog.copy()); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } }); @@ -208,7 +205,6 @@ private void init() { //Pset jButtonAbspielen.addActionListener(e -> { - jButtonAbspielen.setBackground(MVColor.BUTTON_SET_ABSPIELEN.color); DatenPset pset = getPset(); if (pset != null) { pset.setAbspielen(); @@ -330,7 +326,7 @@ private void init() { tabellePset(); notifyProgramSetChanged(); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } }); @@ -587,7 +583,6 @@ private void tabelleProgramme() { jCheckBoxSpeichern.setSelected(pSet.istSpeichern()); jCheckBoxButton.setSelected(pSet.istButton()); jCheckBoxAbo.setSelected(pSet.istAbo()); - jButtonAbspielen.setBackground(pSet.istAbspielen() ? MVColor.BUTTON_SET_ABSPIELEN.color : null); switch (pSet.arr[DatenPset.PROGRAMMSET_AUFLOESUNG]) { case FilmResolution.HIGH_QUALITY -> jRadioButtonAufloesungHD.setSelected(true); case FilmResolution.LOW -> jRadioButtonAufloesungKlein.setSelected(true); @@ -706,7 +701,7 @@ private void setAufAb(boolean auf) { tabellePset.scrollRectToVisible(tabellePset.getCellRect(neu, 0, false)); notifyProgramSetChanged(); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } @@ -738,7 +733,7 @@ private void setLoeschen() { notifyProgramSetChanged(); } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } @@ -766,15 +761,12 @@ private void setExport() { IoXmlSchreiben configWriter = new IoXmlSchreiben(); configWriter.exportPset(liste.toArray(new DatenPset[0]), ziel); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setHeaderText("Programmset exportieren"); - alert.setContentText("Das Programmset wurde erfolgreich exportiert."); - JFXHiddenApplication.showAlert(alert, MediathekGui.ui()); - }); + JOptionPane.showMessageDialog(this, + "Das Programmset wurde erfolgreich exportiert.", + Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE); } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } @@ -795,7 +787,7 @@ private void progAufAb(boolean auf) { tabelleProgramme.setRowSelectionInterval(neu, neu); tabelleProgramme.scrollRectToVisible(tabelleProgramme.getCellRect(neu, 0, true)); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } @@ -906,7 +898,7 @@ private void eingabe() { notifyProgramSetChanged(); stopBeob = false; } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(null); } } setNamePruefen(); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/ModernSearchConfigPanel.java b/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/ModernSearchConfigPanel.java new file mode 100644 index 0000000000..6f44bac7dc --- /dev/null +++ b/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/ModernSearchConfigPanel.java @@ -0,0 +1,58 @@ +/* + * Created by JFormDesigner on Fri Aug 05 11:18:25 CEST 2022 + */ + +package mediathek.gui.dialogEinstellungen.allgemein; + +import com.jidesoft.swing.MultilineLabel; +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; + +import javax.swing.*; + +/** + * @author Christian Franzke + */ +public class ModernSearchConfigPanel extends JPanel { + public ModernSearchConfigPanel() { + initComponents(); + } + + public JCheckBox getCbActivateModernSearch() { + return cbActivateModernSearch; + } + + private void initComponents() { + // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents + // Generated using JFormDesigner non-commercial license + var multilineLabel1 = new MultilineLabel(); + cbActivateModernSearch = new JCheckBox(); + + //======== this ======== + setLayout(new MigLayout( + new LC().insets("5").hideMode(3), //NON-NLS + // columns + new AC() + .grow().fill(), + // rows + new AC() + .gap() + )); + + //---- multilineLabel1 ---- + multilineLabel1.setText("Mit der modernen Suche k\u00f6nnen komplexe Abfragen inkl. UND/ODER/NICHT Anteilen \u00fcber diverse Felder der Filmliste durchgef\u00fchrt werden. \nDie Bedienung ist komplett anders und deutlich komplexer als die bisher verwendete Suche und richtet sich daher nur an erfahrene Nutzer, die sich in die Materie einarbeiten wollen. Eine Suche im Internet zur Gestaltung der Abfragen ist unausweichlich.\nDie Lade-/Verarbeitungszeit der Filmliste erh\u00f6ht sich je nach verwendetem Rechner um ca. 10-30 Sekunden.\nEs ist nicht m\u00f6glich, die neue Suche und die alte parallel zu verwenden."); //NON-NLS + add(multilineLabel1, new CC().cell(0, 0).grow()); + + //---- cbActivateModernSearch ---- + cbActivateModernSearch.setText("Moderne Suchfunktion aktivieren (erfordert Neustart!)"); //NON-NLS + add(cbActivateModernSearch, new CC().cell(0, 1)); + // JFormDesigner - End of component initialization //GEN-END:initComponents + } + + // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables + // Generated using JFormDesigner non-commercial license + private JCheckBox cbActivateModernSearch; + // JFormDesigner - End of variables declaration //GEN-END:variables +} diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/ModernSearchConfigPanel.jfd b/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/ModernSearchConfigPanel.jfd new file mode 100644 index 0000000000..dbd027033d --- /dev/null +++ b/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/ModernSearchConfigPanel.jfd @@ -0,0 +1,35 @@ +JFDML JFormDesigner: "7.0.7.0.1134" Java: "17.0.3" encoding: "UTF-8" + +new FormModel { + contentType: "form/swing" + root: new FormRoot { + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 5,hidemode 3" + "$columnConstraints": "[grow,fill]" + "$rowConstraints": "[][]" + } ) { + name: "this" + add( new FormComponent( "com.jidesoft.swing.MultilineLabel" ) { + name: "multilineLabel1" + "text": "Mit der modernen Suche können komplexe Abfragen inkl. UND/ODER/NICHT Anteilen über diverse Felder der Filmliste durchgeführt werden. \nDie Bedienung ist komplett anders und deutlich komplexer als die bisher verwendete Suche und richtet sich daher nur an erfahrene Nutzer, die sich in die Materie einarbeiten wollen. Eine Suche im Internet zur Gestaltung der Abfragen ist unausweichlich.\nDie Lade-/Verarbeitungszeit der Filmliste erhöht sich je nach verwendetem Rechner um ca. 10-30 Sekunden.\nEs ist nicht möglich, die neue Suche und die alte parallel zu verwenden." + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0,grow" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "cbActivateModernSearch" + "text": "Moderne Suchfunktion aktivieren (erfordert Neustart!)" + auxiliary() { + "JavaCodeGenerator.variableGetter": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" + } ) + }, new FormLayoutConstraints( null ) { + "location": new java.awt.Point( 0, 0 ) + "size": new java.awt.Dimension( 665, 175 ) + } ) + } +} diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.java b/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.java index 446a12a991..62b2cc82c1 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.java @@ -1,29 +1,29 @@ package mediathek.gui.dialogEinstellungen.allgemein; -import mediathek.config.Daten; import mediathek.gui.messages.*; import mediathek.mainwindow.MediathekGui; import mediathek.tool.ApplicationConfiguration; +import mediathek.tool.GuiFunktionen; import mediathek.tool.MessageBus; import mediathek.tool.sender_icon_cache.MVSenderIconCache; import net.engio.mbassy.listener.Handler; +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; import org.apache.commons.configuration2.Configuration; import org.apache.commons.lang3.SystemUtils; +import org.jdesktop.swingx.JXTitledPanel; +import org.jdesktop.swingx.VerticalLayout; import javax.swing.*; import javax.swing.border.TitledBorder; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.ActionEvent; import java.util.NoSuchElementException; public class PanelEinstellungen extends JPanel { - private final static String ALLE = " Alle "; private final Configuration config = ApplicationConfiguration.getConfiguration(); - private final SpinnerListModel daySpinnerModel = new SpinnerListModel(new Object[]{ALLE, "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", - "12", "14", "16", "18", "20", "25", "30", "60", "90", "180", "365"}); - private void setupProxySettings() { @@ -60,13 +60,6 @@ private void cbAutomaticUpdateChecksActionPerformed(ActionEvent evt) { MessageBus.getMessageBus().publishAsync(new UpdateStateChangedEvent(cbAutomaticUpdateChecks.isSelected())); } - private void setupDays() { - jSpinnerDays.setModel(daySpinnerModel); - ((JSpinner.DefaultEditor) jSpinnerDays.getEditor()).getTextField().setEditable(false); - initSpinner(); - jSpinnerDays.addChangeListener(new BeobSpinnerDays()); - } - private void setupTabUI() { final boolean tabPositionTop = config.getBoolean(ApplicationConfiguration.APPLICATION_UI_TAB_POSITION_TOP, true); jCheckBoxTabsTop.setSelected(tabPositionTop); @@ -103,22 +96,28 @@ private void setupTray() { } } + private void setupModernSearch() { + var config = ApplicationConfiguration.getConfiguration(); + var useModernSearch = config.getBoolean(ApplicationConfiguration.APPLICATION_USE_MODERN_SEARCH, false); + + var searchPanel = new ModernSearchConfigPanel(); + searchPanel.getCbActivateModernSearch().setSelected(useModernSearch); + searchPanel.getCbActivateModernSearch().addActionListener(l -> { + var selected = searchPanel.getCbActivateModernSearch().isSelected(); + config.setProperty(ApplicationConfiguration.APPLICATION_USE_MODERN_SEARCH, selected); + }); + modernSearchTitlePanel.setContentContainer(searchPanel); + } + public PanelEinstellungen() { super(); initComponents(); + setupModernSearch(); setupUserAgentSettings(); setupProxySettings(); - jButtonLoad.addActionListener(ae -> { - final var daten = Daten.getInstance(); - daten.getListeFilme().clear(); // sonst wird evtl. nur eine Diff geladen - daten.getFilmeLaden().loadFilmlist("", false); - }); - - setupDays(); - setupTabUI(); setupTray(); @@ -131,11 +130,16 @@ public PanelEinstellungen() { cbAutomaticUpdateChecks.addActionListener(this::cbAutomaticUpdateChecksActionPerformed); cbAutomaticUpdateChecks.setSelected(ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.CONFIG_AUTOMATIC_UPDATE_CHECK,true)); - } + if (GuiFunktionen.isUsingExternalUpdater()) { + cbAutomaticUpdateChecks.setEnabled(false); + cbAutomaticUpdateChecks.setToolTipText("Diese Option ist deaktiviert, da ein externer Updater verwendet wird."); + } - @Handler - private void handleParallelDownloadNumberChanged(ParallelDownloadNumberChangedEvent e) { - SwingUtilities.invokeLater(this::initSpinner); + var restore = ApplicationConfiguration.getConfiguration() + .getBoolean(ApplicationConfiguration.APPLICATION_RESTORE_SELECTED_TAB, false); + cbRestoreSelectedTab.setSelected(restore); + cbRestoreSelectedTab.addActionListener(l -> ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.APPLICATION_RESTORE_SELECTED_TAB, + cbRestoreSelectedTab.isSelected())); } private void setupTabSwitchListener() { @@ -167,37 +171,6 @@ private void setupTabSwitchListener() { } } - private void initSpinner() { - String s; - final int num_days = config.getInt(ApplicationConfiguration.FilmList.LOAD_NUM_DAYS,0); - if (num_days == 0) - s = ALLE; - else - s = Integer.toString(num_days); - - jSpinnerDays.setValue(s); - } - - private class BeobSpinnerDays implements ChangeListener { - - @Override - public void stateChanged(ChangeEvent arg0) { - String s = jSpinnerDays.getModel().getValue().toString(); - if (s.equals(ALLE)) { - s = "0"; - } - - int num_days; - try { - num_days = Integer.parseInt(s); - } - catch (NumberFormatException e) { - num_days = 0; - } - config.setProperty(ApplicationConfiguration.FilmList.LOAD_NUM_DAYS, num_days); - } - } - // //GEN-BEGIN:initComponents // Generated using JFormDesigner non-commercial license private void initComponents() { @@ -205,6 +178,7 @@ private void initComponents() { jCheckBoxTabsTop = new JCheckBox(); jCheckBoxTabIcon = new JCheckBox(); cbAutomaticMenuTabSwitching = new JCheckBox(); + cbRestoreSelectedTab = new JCheckBox(); var jPanel3 = new JPanel(); var jLabel3 = new JLabel(); jtfUserAgent = new JTextField(); @@ -217,14 +191,11 @@ private void initComponents() { jtfProxyUser = new JTextField(); var jLabel8 = new JLabel(); jpfProxyPassword = new JPasswordField(); - var jPanel2 = new JPanel(); - var jPanel6 = new JPanel(); - var jLabel6 = new JLabel(); - jSpinnerDays = new JSpinner(); - jButtonLoad = new JButton(); + var panel1 = new JPanel(); jCheckBoxTray = new JCheckBox(); cbUseWikipediaSenderLogos = new JCheckBox(); cbAutomaticUpdateChecks = new JCheckBox(); + modernSearchTitlePanel = new JXTitledPanel(); //======== this ======== setMaximumSize(new Dimension(10, 10)); @@ -232,39 +203,35 @@ private void initComponents() { //======== jPanel5 ======== { jPanel5.setBorder(new TitledBorder("Tab-Verhalten")); //NON-NLS + jPanel5.setLayout(new MigLayout( + new LC().insets("0").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .fill().gap() + .fill().gap() + .fill(), + // rows + new AC() + .gap() + .fill())); //---- jCheckBoxTabsTop ---- jCheckBoxTabsTop.setText("Tabs oben anzeigen"); //NON-NLS + jPanel5.add(jCheckBoxTabsTop, new CC().cell(0, 0)); //---- jCheckBoxTabIcon ---- jCheckBoxTabIcon.setText("Icons anzeigen"); //NON-NLS jCheckBoxTabIcon.setToolTipText("Im Tab keine Icons anzeigen"); //NON-NLS + jPanel5.add(jCheckBoxTabIcon, new CC().cell(1, 0)); //---- cbAutomaticMenuTabSwitching ---- cbAutomaticMenuTabSwitching.setText("Tabs schalten automatisch bei Men\u00fcnutzung um"); //NON-NLS + jPanel5.add(cbAutomaticMenuTabSwitching, new CC().cell(2, 0)); - GroupLayout jPanel5Layout = new GroupLayout(jPanel5); - jPanel5.setLayout(jPanel5Layout); - jPanel5Layout.setHorizontalGroup( - jPanel5Layout.createParallelGroup() - .addGroup(jPanel5Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jCheckBoxTabsTop) - .addGap(5, 5, 5) - .addComponent(jCheckBoxTabIcon) - .addGap(5, 5, 5) - .addComponent(cbAutomaticMenuTabSwitching) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - jPanel5Layout.setVerticalGroup( - jPanel5Layout.createParallelGroup() - .addGroup(jPanel5Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addGroup(jPanel5Layout.createParallelGroup() - .addComponent(jCheckBoxTabsTop) - .addComponent(jCheckBoxTabIcon) - .addComponent(cbAutomaticMenuTabSwitching))) - ); + //---- cbRestoreSelectedTab ---- + cbRestoreSelectedTab.setText("Letzte Auswahl beim Start wiederherstellen"); //NON-NLS + cbRestoreSelectedTab.setToolTipText("Wenn gew\u00e4hlt wird beim Start des Programms automatisch das zuletzt genutzte Tab aktiviert."); //NON-NLS + jPanel5.add(cbRestoreSelectedTab, new CC().cell(0, 1, 3, 1)); } //======== jPanel3 ======== @@ -340,7 +307,7 @@ private void initComponents() { .addGroup(jPanel4Layout.createParallelGroup() .addGroup(jPanel4Layout.createSequentialGroup() .addComponent(jtfProxyPort, GroupLayout.PREFERRED_SIZE, 72, GroupLayout.PREFERRED_SIZE) - .addGap(0, 175, Short.MAX_VALUE)) + .addGap(0, 187, Short.MAX_VALUE)) .addComponent(jpfProxyPassword)) .addContainerGap()) ); @@ -363,79 +330,25 @@ private void initComponents() { ); } - //======== jPanel2 ======== + //======== panel1 ======== { - jPanel2.setBorder(new TitledBorder("Einschr\u00e4nkungen f\u00fcr das Laden der Filmliste")); //NON-NLS - - //======== jPanel6 ======== - { - - //---- jLabel6 ---- - jLabel6.setText("Nur die Filme der letzten Tage laden:"); //NON-NLS - - //---- jSpinnerDays ---- - jSpinnerDays.setToolTipText("Es werden nur Filme der letzten xx Tage geladen.
Bei \"Alle\" werden alle Filme geladen.
(Eine kleinere Filmliste kann bei Rechnern mit wenig Speicher hilfreich sein.)

\nAuswirkung hat das erst nach dem Neuladen der kompletten Filmliste."); //NON-NLS - jSpinnerDays.setMinimumSize(new Dimension(100, 30)); - jSpinnerDays.setPreferredSize(new Dimension(100, 30)); - jSpinnerDays.setMaximumSize(new Dimension(150, 30)); - - //---- jButtonLoad ---- - jButtonLoad.setText("Filmliste jetzt neu laden"); //NON-NLS - - GroupLayout jPanel6Layout = new GroupLayout(jPanel6); - jPanel6.setLayout(jPanel6Layout); - jPanel6Layout.setHorizontalGroup( - jPanel6Layout.createParallelGroup() - .addGroup(jPanel6Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jLabel6) - .addGap(5, 5, 5) - .addComponent(jSpinnerDays, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButtonLoad) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - jPanel6Layout.setVerticalGroup( - jPanel6Layout.createParallelGroup() - .addGroup(jPanel6Layout.createSequentialGroup() - .addGroup(jPanel6Layout.createParallelGroup() - .addGroup(jPanel6Layout.createSequentialGroup() - .addGap(11, 11, 11) - .addComponent(jLabel6)) - .addGroup(jPanel6Layout.createSequentialGroup() - .addGap(6, 6, 6) - .addGroup(jPanel6Layout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(jSpinnerDays, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addComponent(jButtonLoad)))) - .addGap(2, 2, 2)) - ); - } + panel1.setLayout(new VerticalLayout()); - GroupLayout jPanel2Layout = new GroupLayout(jPanel2); - jPanel2.setLayout(jPanel2Layout); - jPanel2Layout.setHorizontalGroup( - jPanel2Layout.createParallelGroup() - .addGroup(jPanel2Layout.createSequentialGroup() - .addContainerGap() - .addComponent(jPanel6, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addContainerGap(102, Short.MAX_VALUE)) - ); - jPanel2Layout.setVerticalGroup( - jPanel2Layout.createParallelGroup(GroupLayout.Alignment.TRAILING) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) - .addComponent(jPanel6, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) - ); - } + //---- jCheckBoxTray ---- + jCheckBoxTray.setText("Programm ins Tray minimieren"); //NON-NLS + panel1.add(jCheckBoxTray); - //---- jCheckBoxTray ---- - jCheckBoxTray.setText("Programm ins Tray minimieren"); //NON-NLS + //---- cbUseWikipediaSenderLogos ---- + cbUseWikipediaSenderLogos.setText("Senderlogos von Wikipedia verwenden"); //NON-NLS + panel1.add(cbUseWikipediaSenderLogos); - //---- cbUseWikipediaSenderLogos ---- - cbUseWikipediaSenderLogos.setText("Senderlogos von Wikipedia verwenden"); //NON-NLS + //---- cbAutomaticUpdateChecks ---- + cbAutomaticUpdateChecks.setText("Programmupdates t\u00e4glich suchen"); //NON-NLS + panel1.add(cbAutomaticUpdateChecks); + } - //---- cbAutomaticUpdateChecks ---- - cbAutomaticUpdateChecks.setText("Programmupdates t\u00e4glich suchen"); //NON-NLS + //---- modernSearchTitlePanel ---- + modernSearchTitlePanel.setTitle("Moderne Suche"); //NON-NLS GroupLayout layout = new GroupLayout(this); setLayout(layout); @@ -448,13 +361,10 @@ private void initComponents() { .addComponent(jPanel5, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING, false) - .addComponent(jPanel4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jPanel2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addComponent(jCheckBoxTray) - .addComponent(cbUseWikipediaSenderLogos) - .addComponent(cbAutomaticUpdateChecks)) - .addGap(0, 0, Short.MAX_VALUE))) + .addComponent(jPanel4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(panel1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .addGap(0, 20, Short.MAX_VALUE)) + .addComponent(modernSearchTitlePanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); layout.setVerticalGroup( @@ -467,14 +377,10 @@ private void initComponents() { .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addComponent(jPanel4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jPanel2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jCheckBoxTray) + .addComponent(panel1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cbUseWikipediaSenderLogos) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cbAutomaticUpdateChecks) - .addContainerGap(16, Short.MAX_VALUE)) + .addComponent(modernSearchTitlePanel, GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE) + .addContainerGap()) ); }//
//GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables @@ -482,15 +388,15 @@ private void initComponents() { private JCheckBox jCheckBoxTabsTop; private JCheckBox jCheckBoxTabIcon; private JCheckBox cbAutomaticMenuTabSwitching; + private JCheckBox cbRestoreSelectedTab; private JTextField jtfUserAgent; private JTextField jtfProxyHost; private JTextField jtfProxyPort; private JTextField jtfProxyUser; private JPasswordField jpfProxyPassword; - private JSpinner jSpinnerDays; - private JButton jButtonLoad; private JCheckBox jCheckBoxTray; private JCheckBox cbUseWikipediaSenderLogos; private JCheckBox cbAutomaticUpdateChecks; + private JXTitledPanel modernSearchTitlePanel; // End of variables declaration//GEN-END:variables } diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.jfd b/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.jfd index 359e37ca62..c99291b654 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.jfd +++ b/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.jfd @@ -1,17 +1,18 @@ -JFDML JFormDesigner: "7.0.4.0.360" Java: "11.0.12" encoding: "UTF-8" +JFDML JFormDesigner: "8.0.3.0.246" Java: "17.0.6" encoding: "UTF-8" new FormModel { contentType: "form/swing" root: new FormRoot { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { - "$horizontalGroup": "par l {seq {space :::p, par l {comp jPanel3:::::x, comp jPanel5::t:::x, seq {par l {par l:::p {comp jPanel4::l:::x, comp jPanel2::l:::x}, comp jCheckBoxTray::l:p::p, comp cbUseWikipediaSenderLogos::l:p::p, comp cbAutomaticUpdateChecks::l:p::p}, space :0:0:x}}, space :::p}}" - "$verticalGroup": "par l {seq l {space :::p, comp jPanel5:::p::p, space :::p, comp jPanel3:::p::p, space :::p, comp jPanel4:::p::p, space :::p, comp jPanel2:::p::p, space :::p, comp jCheckBoxTray:::p::p, space :::p, comp cbUseWikipediaSenderLogos:::p::p, space :::p, comp cbAutomaticUpdateChecks:::p::p, space ::16:x}}" + "$horizontalGroup": "par l {seq l {space :::p, par l {comp jPanel3:::::x, comp jPanel5::t:::x, seq {par l {comp jPanel4:::p::p, comp panel1:::p::p}, space :0:20:x}, comp modernSearchTitlePanel::l:::x}, space :::p}}" + "$verticalGroup": "par l {seq l {space :::p, comp jPanel5:::p::p, space :::p, comp jPanel3:::p::p, space :::p, comp jPanel4:::p::p, space :::p, comp panel1:::p::p, space :::p, comp modernSearchTitlePanel::::143:x, space :::p}}" } ) { name: "this" "maximumSize": new java.awt.Dimension( 10, 10 ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { - "$horizontalGroup": "par l {seq {space :5:5:p, comp jCheckBoxTabsTop:::p::p, space :5:5:p, comp jCheckBoxTabIcon:::p::p, space :5:5:p, comp cbAutomaticMenuTabSwitching:::p::p, space :::x}}" - "$verticalGroup": "par l {seq {space :5:5:p, par l {comp jCheckBoxTabsTop:::p::p, comp jCheckBoxTabIcon:::p::p, comp cbAutomaticMenuTabSwitching:::p::p}}}" + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 0,hidemode 3,gap 5 5" + "$columnConstraints": "[fill][fill][fill]" + "$rowConstraints": "[][fill]" } ) { name: "jPanel5" "border": new javax.swing.border.TitledBorder( "Tab-Verhalten" ) @@ -21,15 +22,28 @@ new FormModel { add( new FormComponent( "javax.swing.JCheckBox" ) { name: "jCheckBoxTabsTop" "text": "Tabs oben anzeigen" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "jCheckBoxTabIcon" "text": "Icons anzeigen" "toolTipText": "Im Tab keine Icons anzeigen" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "cbAutomaticMenuTabSwitching" "text": "Tabs schalten automatisch bei Menünutzung um" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "cbRestoreSelectedTab" + "text": "Letzte Auswahl beim Start wiederherstellen" + "toolTipText": "Wenn gewählt wird beim Start des Programms automatisch das zuletzt genutzte Tab aktiviert." + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1 3 1" } ) } ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { @@ -55,7 +69,7 @@ new FormModel { } ) } ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { - "$horizontalGroup": "par l {seq {space :::p, par l:::p {seq l {comp jLabel4:::p::p, space :::p, comp jtfProxyHost:::p:250:p}, seq l {comp jLabel7:::p::p, space :::p, comp jtfProxyUser:::::x}}, space u:::p, par t {comp jLabel8:::p::p, comp jLabel5:::p::p}, space :::p, par l {seq {comp jtfProxyPort:::p:72:p, space :0:175:x}, comp jpfProxyPassword:::::x}, space :::p}}" + "$horizontalGroup": "par l {seq {space :::p, par l:::p {seq l {comp jLabel4:::p::p, space :::p, comp jtfProxyHost:::p:250:p}, seq l {comp jLabel7:::p::p, space :::p, comp jtfProxyUser:::::x}}, space u:::p, par t {comp jLabel8:::p::p, comp jLabel5:::p::p}, space :::p, par l {seq {comp jtfProxyPort:::p:72:p, space :0:187:x}, comp jpfProxyPassword:::::x}, space :::p}}" "$verticalGroup": "par l {seq l {space :::p, par b {comp jLabel4::b:p::p, comp jtfProxyHost::b:p::p, comp jLabel5::b:p::p, comp jtfProxyPort::b:p::p}, space :::p, par b {comp jLabel7::b:p::p, comp jtfProxyUser::b:p::p, comp jLabel8::b:p::p, comp jpfProxyPassword::b:p::p}, space :::x}}" } ) { name: "jPanel4" @@ -105,69 +119,30 @@ new FormModel { name: "jpfProxyPassword" } ) } ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { - "$horizontalGroup": "par l {seq l {space :::p, comp jPanel6:::p::p, space ::102:x}}" - "$verticalGroup": "par t {seq t {space :0:0:x, comp jPanel6:::p::p}}" - } ) { - name: "jPanel2" - "border": new javax.swing.border.TitledBorder( "Einschränkungen für das Laden der Filmliste" ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.swingx.VerticalLayout ) ) { + name: "panel1" auxiliary() { "JavaCodeGenerator.variableLocal": true } - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { - "$horizontalGroup": "par l {seq {space :5:5:p, comp jLabel6:::p::p, space :5:5:p, comp jSpinnerDays:::p::p, space :::p, comp jButtonLoad:::p::p, space :::x}}" - "$verticalGroup": "par l {seq {par l {seq {space :11:11:p, comp jLabel6:::p::p}, seq l {space :6:6:p, par b {comp jSpinnerDays::b:p::p, comp jButtonLoad::b:p::p}}}, space :p:2:p}}" - } ) { - name: "jPanel6" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - add( new FormComponent( "javax.swing.JLabel" ) { - name: "jLabel6" - "text": "Nur die Filme der letzten Tage laden:" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - } ) - add( new FormComponent( "javax.swing.JSpinner" ) { - name: "jSpinnerDays" - "model": new javax.swing.SpinnerListModel { - list: new java.util.ArrayList { - add( "Alles" ) - add( "1" ) - add( "2" ) - add( "10" ) - add( "15" ) - } - } - "toolTipText": "Es werden nur Filme der letzten xx Tage geladen.
Bei \"Alle\" werden alle Filme geladen.
(Eine kleinere Filmliste kann bei Rechnern mit wenig Speicher hilfreich sein.)

\nAuswirkung hat das erst nach dem Neuladen der kompletten Filmliste." - "minimumSize": new java.awt.Dimension( 100, 30 ) - "preferredSize": new java.awt.Dimension( 100, 30 ) - "maximumSize": new java.awt.Dimension( 150, 30 ) - auxiliary() { - "model.noCode": true - } - } ) - add( new FormComponent( "javax.swing.JButton" ) { - name: "jButtonLoad" - "text": "Filmliste jetzt neu laden" - } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "jCheckBoxTray" + "text": "Programm ins Tray minimieren" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "cbUseWikipediaSenderLogos" + "text": "Senderlogos von Wikipedia verwenden" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "cbAutomaticUpdateChecks" + "text": "Programmupdates täglich suchen" } ) } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "jCheckBoxTray" - "text": "Programm ins Tray minimieren" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "cbUseWikipediaSenderLogos" - "text": "Senderlogos von Wikipedia verwenden" - } ) - add( new FormComponent( "javax.swing.JCheckBox" ) { - name: "cbAutomaticUpdateChecks" - "text": "Programmupdates täglich suchen" + add( new FormComponent( "org.jdesktop.swingx.JXTitledPanel" ) { + name: "modernSearchTitlePanel" + "title": "Moderne Suche" } ) }, new FormLayoutConstraints( null ) { - "size": new java.awt.Dimension( 645, 390 ) + "size": new java.awt.Dimension( 680, 470 ) "location": new java.awt.Point( 0, 0 ) } ) } diff --git a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt index 1aacfb5435..ced393bd17 100644 --- a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt +++ b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt @@ -1,60 +1,59 @@ package mediathek.gui.filmInformation -import javafx.application.Platform -import javafx.embed.swing.JFXPanel -import javafx.embed.swing.SwingFXUtils -import javafx.event.EventHandler -import javafx.geometry.Pos -import javafx.scene.Scene -import javafx.scene.control.* -import javafx.scene.image.ImageView +import com.formdev.flatlaf.util.ScaledImageIcon +import com.jidesoft.swing.MultilineLabel +import mediathek.daten.Country import mediathek.daten.DatenFilm import mediathek.gui.actions.UrlHyperlinkAction -import mediathek.javafx.tool.JavaFxUtils +import mediathek.mainwindow.MediathekGui import mediathek.tool.ApplicationConfiguration import mediathek.tool.GuiFunktionen +import mediathek.tool.SwingErrorDialog import mediathek.tool.sender_icon_cache.MVSenderIconCache +import net.miginfocom.layout.AC import net.miginfocom.layout.CC +import net.miginfocom.layout.LC +import net.miginfocom.swing.MigLayout import org.apache.commons.configuration2.sync.LockMode -import org.tbee.javafx.scene.layout.MigPane -import java.awt.BorderLayout +import org.apache.commons.lang3.SystemUtils +import org.jdesktop.swingx.JXHyperlink +import java.awt.Desktop +import java.awt.Dimension import java.awt.Point import java.awt.Window import java.awt.event.ComponentAdapter import java.awt.event.ComponentEvent import java.awt.event.WindowAdapter import java.awt.event.WindowEvent -import java.awt.image.BufferedImage +import java.net.URI import java.net.URISyntaxException -import javax.swing.ImageIcon -import javax.swing.JDialog -import javax.swing.SwingUtilities +import java.util.stream.Collectors +import javax.swing.* + class InfoDialog(parent: Window?) : JDialog(parent) { private val config = ApplicationConfiguration.getConfiguration() private var currentFilm: DatenFilm? = null - private val lblSender = Label() - private val lblThema = Label() - private val lblTitle = Label() - private val lblDate = Label() - private val lblUhrzeit = Label() - private val lblDuration = Label() - private val lblSize = Label() - private val cbHq = DisabledCheckBox() - private val cbSubtitle = DisabledCheckBox() - private val lblGeo = Label() - private val lblAbo = Label() - private val hyperlink = Hyperlink("Link zur Webseite") - private val lblDescription = TextArea() + private val lblSender = JLabel() + private val lblThema = MultilineLabel() + private val lblTitel = MultilineLabel() + private val lblDate = JLabel() + private val lblUhrzeit = JLabel() + private val lblDuration = JLabel() + private val lblSize = JLabel() + private val lblGeo = JLabel() + private val cbHq = SwingDisabledCheckBox() + private val cbSubtitle = SwingDisabledCheckBox() + private val lblAbo = JLabel() + private val hyperlink = SwingHyperlink() + private val descScrollPane = JScrollPane() + private val lblDescription = JTextArea() + - private fun installContextMenu(component: Label) { - val ctMenu = ContextMenu() - val mi = MenuItem("Text in die Zwischenablage kopieren") - mi.onAction = EventHandler { - GuiFunktionen.copyToClipboard(component.text) + internal class SwingDisabledCheckBox : JCheckBox() { + init { + isEnabled = false } - ctMenu.items.add(mi) - component.contextMenu = ctMenu } /** @@ -64,9 +63,16 @@ class InfoDialog(parent: Window?) : JDialog(parent) { config.lock(LockMode.READ) try { val newLocation = Point() - newLocation.x = config.getInt(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_LOCATION_X) - newLocation.y = config.getInt(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_LOCATION_Y) + newLocation.x = config.getInt(ApplicationConfiguration.FilmInfoDialog.X) + newLocation.y = config.getInt(ApplicationConfiguration.FilmInfoDialog.Y) location = newLocation + + if (SystemUtils.IS_OS_LINUX) { + val w: Int = config.getInt(ApplicationConfiguration.FilmInfoDialog.WIDTH) + val h: Int = config.getInt(ApplicationConfiguration.FilmInfoDialog.HEIGHT) + if (w > 50 && h > 50) + size = Dimension(w, h) + } } catch (ignored: NoSuchElementException) { } finally { config.unlock(LockMode.READ) @@ -82,8 +88,12 @@ class InfoDialog(parent: Window?) : JDialog(parent) { config.lock(LockMode.WRITE) try { val location = locationOnScreen - config.setProperty(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_LOCATION_X, location.x) - config.setProperty(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_LOCATION_Y, location.y) + config.setProperty(ApplicationConfiguration.FilmInfoDialog.X, location.x) + config.setProperty(ApplicationConfiguration.FilmInfoDialog.Y, location.y) + if (SystemUtils.IS_OS_LINUX) { + config.setProperty(ApplicationConfiguration.FilmInfoDialog.WIDTH, width) + config.setProperty(ApplicationConfiguration.FilmInfoDialog.HEIGHT, height) + } } finally { config.unlock(LockMode.WRITE) } @@ -95,61 +105,64 @@ class InfoDialog(parent: Window?) : JDialog(parent) { } private fun clearControls() { - Platform.runLater { - lblDescription.text = "" - lblAbo.text = "" - lblGeo.text = "" - lblSender.text = "" - lblSender.graphic = null - lblSize.text = "" - lblThema.text = "" - lblTitle.text = "" - lblDate.text = "" - lblUhrzeit.text = "" - lblDuration.text = "" - cbHq.isSelected = false - cbSubtitle.isSelected = false - hyperlink.tooltip = null - hyperlink.isDisable = true + lblThema.text = "" + lblSender.text = "" + lblSender.icon = null + lblTitel.text = "" + lblDate.text = "" + lblUhrzeit.text = "" + lblDuration.text = "" + lblSender.text = "" + lblGeo.text = "" + cbHq.isSelected = false + cbSubtitle.isSelected = false + lblAbo.text = "" + hyperlink.toolTipText = "" + hyperlink.isEnabled = false + lblDescription.text = "" + SwingUtilities.invokeLater { + checkedPack() } } - /** - * A sender icon with a fixed height of 32 pixel while maintaining aspect ratio. - */ - internal class SenderIcon(b_img: BufferedImage) : ImageView() { - init { - fitHeight = 32.0 - isPreserveRatio = true - image = SwingFXUtils.toFXImage(b_img, null) - } + companion object { + private val DEFAULT_SENDER_DIMENSION = Dimension(64, 64) } private fun updateTextFields() { if (currentFilm == null) { clearControls() } else { - Platform.runLater { - val desc = currentFilm!!.description.trim { it <= ' ' } - lblDescription.text = desc - lblDescription.scrollTop = 0.0 - lblDescription.scrollLeft = 0.0 - MVSenderIconCache[currentFilm!!.sender].ifPresent { icon: ImageIcon? -> - lblSender.text = "" - lblSender.graphic = SenderIcon(JavaFxUtils.toBufferedImage(icon)) - } - lblGeo.text = currentFilm!!.geo.orElse("") - lblSize.text = currentFilm!!.size - lblThema.text = currentFilm!!.thema - lblTitle.text = currentFilm!!.title - lblDate.text = currentFilm!!.sendeDatum - lblUhrzeit.text = currentFilm!!.sendeZeit - lblDuration.text = currentFilm!!.dauer - cbHq.isSelected = currentFilm!!.isHighQuality - cbSubtitle.isSelected = currentFilm!!.hasSubtitle() - hyperlink.tooltip = Tooltip(currentFilm!!.websiteLink) - hyperlink.isDisable = false - lblAbo.text = currentFilm!!.abo?.name + MVSenderIconCache[currentFilm!!.sender].ifPresent { icon: ImageIcon? -> + lblSender.text = "" + val imageDim = Dimension(icon!!.iconWidth, icon.iconHeight) + val destDim = GuiFunktionen.calculateFittedDimension(imageDim, DEFAULT_SENDER_DIMENSION) + lblSender.icon = ScaledImageIcon(icon, destDim.width, destDim.height) + } + lblThema.text = currentFilm!!.thema + lblTitel.text = currentFilm!!.title + lblDate.text = currentFilm!!.sendeDatum + lblUhrzeit.text = currentFilm!!.sendeZeit + lblDuration.text = currentFilm!!.filmLengthAsString + lblSize.text = currentFilm!!.fileSize.toString() + + if (currentFilm!!.countrySet.isEmpty()) + lblGeo.text = "" + else + lblGeo.text = currentFilm!!.countrySet.stream().map(Country::toString).collect(Collectors.joining("-")) + + cbHq.isSelected = currentFilm!!.isHighQuality + cbSubtitle.isSelected = currentFilm!!.hasSubtitle() + lblAbo.text = currentFilm!!.abo?.name + hyperlink.isEnabled = true + hyperlink.toolTipText = currentFilm!!.websiteUrl + hyperlink.isClicked = false + + val desc = currentFilm!!.description.trim { it <= ' ' } + lblDescription.text = desc + SwingUtilities.invokeLater { + descScrollPane.verticalScrollBar.value = 0 + checkedPack() } } } @@ -161,134 +174,158 @@ class InfoDialog(parent: Window?) : JDialog(parent) { */ fun updateCurrentFilm(film: DatenFilm?) { currentFilm = film - if (isVisible) updateTextFields() + if (isVisible) { + updateTextFields() + } } - private fun buildLayout() { - contentPane.layout = BorderLayout() - val newFxPanel = JFXPanel() - contentPane.add(newFxPanel, BorderLayout.CENTER) - Platform.runLater { - val migPane = MigPane( - "hidemode 3", //columns - "[fill,shrink 0]" + - "[fill]", //rows - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[fill,grow]") - migPane.add(RightOrientedLabel("Sender:"), CC().cell(0, 0)) - migPane.add(lblSender, CC().cell(1, 0)) - - migPane.add(RightOrientedLabel("Thema:"), CC().cell(0, 1)) - lblThema.isWrapText = true - installContextMenu(lblThema) - migPane.add(lblThema, CC().cell(1, 1).growY()) - - migPane.add(RightOrientedLabel("Titel:"), CC().cell(0, 2)) - lblTitle.isWrapText = true - installContextMenu(lblTitle) - migPane.add(lblTitle, CC().cell(1, 2).growY()) + internal class SwingHyperlink : JXHyperlink() { + private fun openUrl(url: String) { + try { + UrlHyperlinkAction.openURL(null, url) + } catch (ex: URISyntaxException) { + ex.printStackTrace() + } + } - migPane.add(RightOrientedLabel("Datum:"), CC().cell(0, 3)) - migPane.add(lblDate, CC().cell(1, 3)) - migPane.add(RightOrientedLabel("Uhrzeit:"), CC().cell(0, 4)) - migPane.add(lblUhrzeit, CC().cell(1, 4)) - migPane.add(RightOrientedLabel("Dauer:"), CC().cell(0, 5)) - migPane.add(lblDuration, CC().cell(1, 5)) - migPane.add(RightOrientedLabel("Größe (MB):"), CC().cell(0, 6)) - migPane.add(lblSize, CC().cell(1, 6)) - migPane.add(RightOrientedLabel("HQ:"), CC().cell(0, 7)) - migPane.add(cbHq, CC().cell(1, 7)) - migPane.add(RightOrientedLabel("Untertitel:"), CC().cell(0, 8)) - migPane.add(cbSubtitle, CC().cell(1, 8)) - migPane.add(RightOrientedLabel("Geo:"), CC().cell(0, 9)) - migPane.add(lblGeo, CC().cell(1, 9)) - migPane.add(RightOrientedLabel("Abo:"), CC().cell(0, 10)) - migPane.add(lblAbo, CC().cell(1, 10)) - migPane.add(RightOrientedLabel("Beschreibung:"), CC().cell(0, 12)) + init { + this.text = "Link zur Webseite" - hyperlink.contextMenu = createCopyUrlContextMenu() - hyperlink.isUnderline = true - hyperlink.onAction = EventHandler { - SwingUtilities.invokeLater { - if (currentFilm != null) { - try { - UrlHyperlinkAction.openURL(null, currentFilm!!.websiteLink) - } catch (ex: URISyntaxException) { - ex.printStackTrace() + addActionListener { + if (toolTipText.isNotEmpty()) { + if (Desktop.isDesktopSupported()) { + val d = Desktop.getDesktop() + if (d.isSupported(Desktop.Action.BROWSE)) { + try { + d.browse(URI(toolTipText)) + } catch (ex: Exception) { + SwingErrorDialog.showExceptionMessage( + MediathekGui.ui(), + "Es trat ein Fehler beim Öffnen des Links auf.\nSollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", + ex + ) + } + } else { + openUrl(toolTipText) } + } else { + openUrl(toolTipText) } + } } - migPane.add(hyperlink, CC().cell(0, 11).spanX(2)) - - lblDescription.isWrapText = true - lblDescription.prefRowCount = 4 - lblDescription.isEditable = false - migPane.add(lblDescription, CC().cell(0, 13).spanX(2).growY().growX().minHeight("60")) - newFxPanel.scene = Scene(migPane) } } - private fun createCopyUrlContextMenu() : ContextMenu { - val contextMenu = ContextMenu() - val mi = MenuItem("URL kopieren") - mi.onAction = EventHandler { SwingUtilities.invokeLater { GuiFunktionen.copyToClipboard(currentFilm!!.websiteLink) } } - contextMenu.items.add(mi) - return contextMenu - } - - internal class RightOrientedLabel(label: String?) : Label(label) { - init { - alignment = Pos.BASELINE_RIGHT - } + private fun buildLayout() { + layout = MigLayout( + LC().insets("5").hideMode(3), + // columns + AC() + .fill().gap() + .size("250!").fill(), + // rows + AC() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + ) + + add(JLabel("Sender:"), CC().cell(0, 0)) + add(lblSender, CC().cell(1, 0)) + add(JLabel("Thema:"), CC().cell(0, 1)) + lblThema.lineWrap = true + add(lblThema, CC().cell(1, 1).growY()) + add(JLabel("Titel:"), CC().cell(0, 2)) + lblTitel.lineWrap = true + add(lblTitel, CC().cell(1, 2).growY()) + + add(JLabel("Datum:"), CC().cell(0, 3)) + add(lblDate, CC().cell(1, 3)) + add(JLabel("Uhrzeit:"), CC().cell(0, 4)) + add(lblUhrzeit, CC().cell(1, 4)) + add(JLabel("Dauer:"), CC().cell(0, 5)) + add(lblDuration, CC().cell(1, 5)) + add(JLabel("Größe (MB):"), CC().cell(0, 6)) + add(lblSize, CC().cell(1, 6)) + add(JLabel("HQ:"), CC().cell(0, 7)) + add(cbHq, CC().cell(1, 7)) + add(JLabel("Untertitel:"), CC().cell(0, 8)) + add(cbSubtitle, CC().cell(1, 8)) + add(JLabel("Geo:"), CC().cell(0, 9)) + add(lblGeo, CC().cell(1, 9)) + add(JLabel("Abo:"), CC().cell(0, 10)) + add(lblAbo, CC().cell(1, 10)) + add(hyperlink, CC().cell(0, 11).spanX(2)) + add(JLabel("Beschreibung:"), CC().cell(0, 12)) + lblDescription.isEditable = false + lblDescription.lineWrap = true + lblDescription.wrapStyleWord = true + descScrollPane.setViewportView(lblDescription) + add(descScrollPane, CC().cell(0, 13).spanX(2).growY().growX().height("90!")) } - - internal class DisabledCheckBox : CheckBox() { - init { - isDisable = true - } + + /** + * Only pack dialog when we are NOT running linux... + */ + private fun checkedPack() { + //only pack only OS OTHER THAN Linux + if (!SystemUtils.IS_OS_LINUX) + pack() } init { type = Type.UTILITY title = "Filminformation" - isResizable = false - //hardcode size as linux hates pack() - setSize(325, 520) + if (!SystemUtils.IS_OS_LINUX) + isResizable = false defaultCloseOperation = DISPOSE_ON_CLOSE + buildLayout() + checkedPack() + if (SystemUtils.IS_OS_LINUX) { + val linuxSize = Dimension(350, 450) + setSize(linuxSize.width, linuxSize.height) + minimumSize = linuxSize + preferredSize = linuxSize + maximumSize = linuxSize + } + updateTextFields() restoreLocation() - val wasVisible = config.getBoolean(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_VISIBLE, false) - if (wasVisible) { - isVisible = true - } + isVisible = config.getBoolean(ApplicationConfiguration.FilmInfoDialog.VISIBLE, false) + addWindowListener(object : WindowAdapter() { override fun windowOpened(e: WindowEvent) { - config.setProperty(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_VISIBLE, true) + config.setProperty(ApplicationConfiguration.FilmInfoDialog.VISIBLE, true) } override fun windowClosed(e: WindowEvent) { - config.setProperty(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_VISIBLE, false) + config.setProperty(ApplicationConfiguration.FilmInfoDialog.VISIBLE, false) } }) //addFilmlistLoadListener(); addComponentListener(object : ComponentAdapter() { + override fun componentResized(e: ComponentEvent?) { + saveLocation() + } + override fun componentMoved(e: ComponentEvent) { - if (isVisible) saveLocation() + if (isVisible) { + saveLocation() + } } }) } diff --git a/src/main/java/mediathek/gui/messages/BandwidthMonitorStateChangedEvent.java b/src/main/java/mediathek/gui/messages/BandwidthMonitorStateChangedEvent.java deleted file mode 100644 index 95cb5f5b47..0000000000 --- a/src/main/java/mediathek/gui/messages/BandwidthMonitorStateChangedEvent.java +++ /dev/null @@ -1,4 +0,0 @@ -package mediathek.gui.messages; - -public class BandwidthMonitorStateChangedEvent extends BaseEvent { -} diff --git a/src/main/java/mediathek/gui/messages/ButtonsPanelVisibilityChangedEvent.java b/src/main/java/mediathek/gui/messages/ButtonsPanelVisibilityChangedEvent.java deleted file mode 100644 index 277a48bd00..0000000000 --- a/src/main/java/mediathek/gui/messages/ButtonsPanelVisibilityChangedEvent.java +++ /dev/null @@ -1,9 +0,0 @@ -package mediathek.gui.messages; - -public class ButtonsPanelVisibilityChangedEvent extends BaseEvent { - public boolean visible; - - public ButtonsPanelVisibilityChangedEvent(boolean visible) { - this.visible = visible; - } -} diff --git a/src/main/java/mediathek/gui/messages/DarkModeChangeEvent.java b/src/main/java/mediathek/gui/messages/DarkModeChangeEvent.java new file mode 100644 index 0000000000..1104352233 --- /dev/null +++ b/src/main/java/mediathek/gui/messages/DarkModeChangeEvent.java @@ -0,0 +1,4 @@ +package mediathek.gui.messages; + +public class DarkModeChangeEvent extends BaseEvent { +} diff --git a/src/main/java/mediathek/gui/messages/DownloadFilterVisibilityChangedEvent.java b/src/main/java/mediathek/gui/messages/DownloadFilterVisibilityChangedEvent.java deleted file mode 100644 index b8d4a0d0c8..0000000000 --- a/src/main/java/mediathek/gui/messages/DownloadFilterVisibilityChangedEvent.java +++ /dev/null @@ -1,4 +0,0 @@ -package mediathek.gui.messages; - -public class DownloadFilterVisibilityChangedEvent extends BaseEvent { -} diff --git a/src/main/java/mediathek/gui/messages/DownloadRateLimitChangedEvent.java b/src/main/java/mediathek/gui/messages/DownloadRateLimitChangedEvent.java index d233ef2aa6..61d12c7bd9 100644 --- a/src/main/java/mediathek/gui/messages/DownloadRateLimitChangedEvent.java +++ b/src/main/java/mediathek/gui/messages/DownloadRateLimitChangedEvent.java @@ -5,4 +5,5 @@ public class DownloadRateLimitChangedEvent extends BaseEvent { * new limit in KBytes */ public int newLimit; + public boolean active; } diff --git a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java index bfbaca379d..e68aade68b 100644 --- a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java +++ b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java @@ -3,16 +3,68 @@ import mediathek.config.Daten; import mediathek.controller.history.SeenHistoryController; import mediathek.daten.DatenFilm; +import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; +import mediathek.gui.tabs.tab_film.FilmDescriptionPanel; import mediathek.mainwindow.MediathekGui; +import mediathek.tool.ApplicationConfiguration; +import mediathek.tool.MessageBus; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.event.ActionEvent; import java.util.List; import java.util.Optional; +import java.util.function.IntConsumer; public abstract class AGuiTabPanel extends JPanel { protected Daten daten; protected MediathekGui mediathekGui; + protected JTabbedPane descriptionTab = new JTabbedPane(); + protected FilmDescriptionPanel descriptionPanel; + + /** + * Show description panel based on settings. + */ + protected void makeDescriptionTabVisible(boolean visible) { + if (visible) { + if (descriptionTab.indexOfComponent(descriptionPanel) == -1) { + descriptionTab.add(descriptionPanel, 0); + descriptionTab.setTitleAt(0, "Beschreibung"); + } + } else { + if (descriptionTab.indexOfComponent(descriptionPanel) != -1) { + descriptionTab.remove(descriptionPanel); + } + } + } + + protected void setupDescriptionTab(@NotNull JTable table, @NotNull JCheckBoxMenuItem cbmi, + @NotNull String configKey) { + descriptionPanel.install(descriptionTab, table); + descriptionTab.putClientProperty("JTabbedPane.tabClosable", true); + descriptionTab.putClientProperty("JTabbedPane.tabCloseCallback", + (IntConsumer) tabIndex -> { + // close description tab here + // must use doClick to trigger model change + cbmi.doClick(); + }); + + setupShowFilmDescriptionMenuItem(); + initDescriptionTabVisibility(configKey); + } + + protected void updateSelectedListItemsCount(@NotNull JTable table) { + final int sel = table.getSelectedRowCount(); + mediathekGui.selectedListItemsProperty.setSelectedItems(sel); + } + + protected abstract void setupShowFilmDescriptionMenuItem(); + + protected void initDescriptionTabVisibility(@NotNull String configKey) { + boolean visible = ApplicationConfiguration.getConfiguration().getBoolean(configKey, true); + + makeDescriptionTabVisible(visible); + } public abstract void tabelleSpeichern(); @@ -23,10 +75,13 @@ public abstract class AGuiTabPanel extends JPanel { */ protected abstract List getSelFilme(); - protected abstract Optional getCurrentlySelectedFilm(); + public abstract Optional getCurrentlySelectedFilm(); + + protected void updateStartInfoProperty() { + MessageBus.getMessageBus().publishAsync(new UpdateStatusBarLeftDisplayEvent()); + } public abstract void installMenuEntries(JMenu menu); - protected abstract void installTabInfoStatusBarControl(); public class MarkFilmAsSeenAction extends AbstractAction { diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/AboLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/AboLabel.java new file mode 100644 index 0000000000..3fda09dade --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/AboLabel.java @@ -0,0 +1,18 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class AboLabel extends JLabel { + public AboLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der Abos in der Liste"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + + } + private void process(@NotNull DownloadStartInfo info) { + String abo = (info.num_abos == 1) ? "1 Abo" : info.num_abos + " Abos"; + setText(abo); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/ActiveDownloadsInfoLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/ActiveDownloadsInfoLabel.java new file mode 100644 index 0000000000..09e1ef2010 --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/ActiveDownloadsInfoLabel.java @@ -0,0 +1,21 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class ActiveDownloadsInfoLabel extends JLabel { + public ActiveDownloadsInfoLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der aktiven Downloads"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + if (info.hasValues()) { + var numDownloads = (info.running == 1) ? "1 läuft" : info.running + " laufen"; + setText(numDownloads); + } else + setText("0 laufen"); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadStartInfoProperty.java b/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadStartInfoProperty.java new file mode 100644 index 0000000000..1d5715c3be --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadStartInfoProperty.java @@ -0,0 +1,40 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.config.Daten; +import mediathek.daten.DownloadStartInfo; +import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; +import mediathek.tool.MessageBus; +import net.engio.mbassy.listener.Handler; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +public class DownloadStartInfoProperty { + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private DownloadStartInfo info; + + public DownloadStartInfoProperty() { + setInfo(Daten.getInstance().getListeDownloads().getStarts()); + + MessageBus.getMessageBus().subscribe(this); + } + + @Handler + private void handleLeftDisplayUpdate(UpdateStatusBarLeftDisplayEvent e) { + setInfo(Daten.getInstance().getListeDownloads().getStarts()); + } + + public DownloadStartInfo getInfo() { + return info; + } + + public void setInfo(DownloadStartInfo info) { + var oldValue = this.info; + this.info = info; + this.pcs.firePropertyChange("info", oldValue, this.info); + } + + public void addStartInfoChangeListener(PropertyChangeListener listener) { + this.pcs.addPropertyChangeListener(listener); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadsConfigPanel.kt b/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadsConfigPanel.kt new file mode 100644 index 0000000000..c6ef846e82 --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadsConfigPanel.kt @@ -0,0 +1,112 @@ +package mediathek.gui.tabs.tab_downloads + +import mediathek.gui.messages.DownloadRateLimitChangedEvent +import mediathek.gui.messages.ParallelDownloadNumberChangedEvent +import mediathek.tool.ApplicationConfiguration +import mediathek.tool.MessageBus.messageBus +import net.engio.mbassy.listener.Handler +import net.miginfocom.layout.AC +import net.miginfocom.layout.CC +import net.miginfocom.layout.LC +import net.miginfocom.swing.MigLayout +import javax.swing.* +import javax.swing.border.TitledBorder + +/** + * @author Christian Franzke + */ +class DownloadsConfigPanel : JPanel() { + private fun setupDownloadRateLimitCheckBox() { + val config = ApplicationConfiguration.getConfiguration() + val active = config.getBoolean( + ApplicationConfiguration.DownloadRateLimiter.ACTIVE, false) + cbMaxBandwidth.isSelected = active + cbMaxBandwidth.addActionListener { + config.setProperty(ApplicationConfiguration.DownloadRateLimiter.ACTIVE, cbMaxBandwidth.isSelected) + fireDownloadRateLimitChangedEvent() + } + } + + private fun fireDownloadRateLimitChangedEvent() { + val downloadLimit = spinnerMaxBandwidth.value as Int + ApplicationConfiguration.getConfiguration() + .setProperty(ApplicationConfiguration.DownloadRateLimiter.LIMIT, downloadLimit) + val evt = DownloadRateLimitChangedEvent() + evt.newLimit = downloadLimit + evt.active = cbMaxBandwidth.isSelected + messageBus.publishAsync(evt) + } + + private fun setupDownloadRateLimitSpinner() { + spinnerMaxBandwidth.putClientProperty("JComponent.roundRect", true) + spinnerMaxBandwidth.model = SpinnerNumberModel(0, 0, 1048576, 1) + spinnerMaxBandwidth.toolTipText = + "Bandbreitenbegrenzung eines Downloads in XX Kilobytes pro Sekunde.\n
WICHTIG:
ENTWEDER
den Wert \u00fcber die Pfeiltasten \u00e4ndern
ODER
Zahlen eingeben UND ENTER-Taste dr\u00fccken!
\n" //NON-NLS + + //restore spinner setting from config + val oldDownloadLimit = + ApplicationConfiguration.getConfiguration().getLong(ApplicationConfiguration.DownloadRateLimiter.LIMIT, 0) + spinnerMaxBandwidth.value = oldDownloadLimit + spinnerMaxBandwidth.addChangeListener { fireDownloadRateLimitChangedEvent() } + } + + private fun setupNumDownloadsSpinner() { + val config = ApplicationConfiguration.getConfiguration() + spinnerNumDownloads.putClientProperty("JComponent.roundRect", true) + spinnerNumDownloads.model = SpinnerNumberModel(1, 1, 9, 1) + spinnerNumDownloads.value = config.getInt(ApplicationConfiguration.DOWNLOAD_MAX_SIMULTANEOUS_NUM, 1) + spinnerNumDownloads.addChangeListener { + val maxNumDownloads = (spinnerNumDownloads.model.value as Number).toInt() + config.setProperty(ApplicationConfiguration.DOWNLOAD_MAX_SIMULTANEOUS_NUM, maxNumDownloads) + messageBus.publishAsync(ParallelDownloadNumberChangedEvent()) + } + } + + @Suppress("UNUSED_PARAMETER") + @Handler + private fun handleParallelDownloadNumberChange(e: ParallelDownloadNumberChangedEvent) { + SwingUtilities.invokeLater { + val maxNumDownloads = ApplicationConfiguration.getConfiguration() + .getInt(ApplicationConfiguration.DOWNLOAD_MAX_SIMULTANEOUS_NUM, 1) + spinnerNumDownloads.value = maxNumDownloads + } + } + + private fun initComponents() { + border = TitledBorder("Downloads") + layout = MigLayout( + LC().insets("0").hideMode(3).align("center", "center"), + // columns + AC() + .align("center").gap() + .align("label").gap() + .align("left").gap() + .align("center"), + // rows + AC() + .gap() + ) + + add(JLabel("gleichzeitig:"), CC().cell(1, 0)) + add(spinnerNumDownloads, CC().cell(2, 0).width("80:100")) //NON-NLS + add(cbMaxBandwidth, CC().cell(0, 1)) + + add(JLabel("max. Bandbreite:"), CC().cell(1, 1)) + add(spinnerMaxBandwidth, CC().cell(2, 1).width("80:100")) //NON-NLS + + add(JLabel("KiB/s"), CC().cell(3, 1)) + } + + private val spinnerNumDownloads: JSpinner = JSpinner() + private val cbMaxBandwidth: JCheckBox = JCheckBox() + private val spinnerMaxBandwidth: JSpinner = JSpinner() + + init { + initComponents() + cbMaxBandwidth.toolTipText = "Bandbreitenbegrenzung aktiviert?" + setupNumDownloadsSpinner() + setupDownloadRateLimitCheckBox() + setupDownloadRateLimitSpinner() + messageBus.subscribe(this) + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/FailedDownloadsInfoLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/FailedDownloadsInfoLabel.java new file mode 100644 index 0000000000..0049fb1cd4 --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/FailedDownloadsInfoLabel.java @@ -0,0 +1,20 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class FailedDownloadsInfoLabel extends JLabel { + public FailedDownloadsInfoLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der fehlerhaften Downloads"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + if (info.hasValues()) { + setText(info.error + " fehlerhaft"); + } else + setText("0 fehlerhaft"); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/FinishedDownloadsInfoLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/FinishedDownloadsInfoLabel.java new file mode 100644 index 0000000000..715bba102f --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/FinishedDownloadsInfoLabel.java @@ -0,0 +1,21 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class FinishedDownloadsInfoLabel extends JLabel { + public FinishedDownloadsInfoLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der abgeschlossenen Downloads"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + if (info.hasValues()) { + String fin = (info.finished == 1) ? "1 fertig" : info.finished + " fertig"; + setText(fin); + } else + setText("0 fertig"); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 1340d412ee..4d9dd996b6 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -3,17 +3,7 @@ import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.swing.GlazedListsSwing; -import javafx.application.Platform; -import javafx.embed.swing.JFXPanel; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.control.Alert; -import javafx.scene.control.TabPane; -import javafx.stage.Modality; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; -import mediathek.config.Icons; import mediathek.config.Konstanten; import mediathek.config.MVConfig; import mediathek.controller.history.MVUsedUrl; @@ -24,17 +14,13 @@ import mediathek.daten.abo.DatenAbo; import mediathek.filmeSuchen.ListenerFilmeLaden; import mediathek.filmeSuchen.ListenerFilmeLadenEvent; -import mediathek.gui.TabPaneIndex; -import mediathek.gui.actions.ShowFilmInformationAction; +import mediathek.gui.actions.*; import mediathek.gui.dialog.DialogBeendenZeit; import mediathek.gui.dialog.DialogEditAbo; import mediathek.gui.dialog.DialogEditDownload; import mediathek.gui.messages.*; import mediathek.gui.tabs.AGuiTabPanel; -import mediathek.gui.toolbar.FXDownloadToolBar; -import mediathek.javafx.descriptionPanel.DescriptionPanelController; -import mediathek.javafx.downloadtab.DownloadTabInformationLabel; -import mediathek.javafx.tool.JavaFxUtils; +import mediathek.gui.tabs.tab_film.FilmDescriptionPanel; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; import mediathek.tool.cellrenderer.CellRendererDownloads; @@ -51,6 +37,7 @@ import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jdesktop.swingx.JXStatusBar; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -65,23 +52,21 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; public class GuiDownloads extends AGuiTabPanel { public static final String NAME = "Downloads"; + public static final int DIVIDER_LOCATION = -1; private static final String COMBO_DISPLAY_ALL = "alle"; private static final String COMBO_DISPLAY_DOWNLOADS_ONLY = "nur Downloads"; private static final String COMBO_DISPLAY_ABOS_ONLY = "nur Abos"; - private static final String COMBO_VIEW_ALL = "alle"; private static final String COMBO_VIEW_NOT_STARTED = "nicht gestartet"; private static final String COMBO_VIEW_STARTED = "gestartet"; private static final String COMBO_VIEW_WAITING = "nur wartende"; private static final String COMBO_VIEW_RUN_ONLY = "nur laufende"; private static final String COMBO_VIEW_FINISHED_ONLY = "nur abgeschlossene"; - - private static final String MENU_ITEM_TEXT_CLEANUP_DOWNLOADS = "Liste säubern"; private static final String ACTION_MAP_KEY_EDIT_DOWNLOAD = "dl_aendern"; private static final String ACTION_MAP_KEY_DELETE_DOWNLOAD = "dl_delete"; private static final String ACTION_MAP_KEY_MARK_AS_SEEN = "seen"; @@ -90,18 +75,41 @@ public class GuiDownloads extends AGuiTabPanel { private final static int[] COLUMNS_DISABLED = {DatenDownload.DOWNLOAD_BUTTON_START, DatenDownload.DOWNLOAD_BUTTON_DEL, DatenDownload.DOWNLOAD_REF, DatenDownload.DOWNLOAD_URL_RTMP}; private static final Logger logger = LogManager.getLogger(GuiDownloads.class); - private static final String HEAD = "\n" - + "" - + ""; - private static final String END = ""; private final AtomicLong _lastUpdate = new AtomicLong(0); - private final AtomicBoolean tabVisible = new AtomicBoolean(false); private final JCheckBoxMenuItem cbShowDownloadDescription = new JCheckBoxMenuItem("Filmbeschreibung anzeigen"); private final Configuration config = ApplicationConfiguration.getConfiguration(); - + private final MarkFilmAsSeenAction markFilmAsSeenAction = new MarkFilmAsSeenAction(); + private final MarkFilmAsUnseenAction markFilmAsUnseenAction = new MarkFilmAsUnseenAction(); + private final JXStatusBar statusBar = new JXStatusBar(); + private final DownloadStartInfoProperty startInfoProperty = new DownloadStartInfoProperty(); + private final AboLabel lblAbos = new AboLabel(startInfoProperty); + private final TotalDownloadsLabel totalDownloadsLabel = new TotalDownloadsLabel(startInfoProperty); + private final ManualDownloadsInfoLabel manualDownloadsInfoLabel = new ManualDownloadsInfoLabel(startInfoProperty); + private final WaitingDownloadsInfoLabel waitingDownloadsInfoLabel = new WaitingDownloadsInfoLabel(startInfoProperty); + private final ActiveDownloadsInfoLabel activeDownloadsInfoLabel = new ActiveDownloadsInfoLabel(startInfoProperty); + private final FinishedDownloadsInfoLabel finishedDownloadsInfoLabel = new FinishedDownloadsInfoLabel(startInfoProperty); + private final FailedDownloadsInfoLabel failedDownloadsInfoLabel = new FailedDownloadsInfoLabel(startInfoProperty); + private final DownloadsConfigPanel dlConfigPanel = new DownloadsConfigPanel(); + protected StartAllDownloadsAction startAllDownloadsAction = new StartAllDownloadsAction(this); + protected StartAllDownloadsTimedAction startAllDownloadsTimedAction = new StartAllDownloadsTimedAction(this); + protected StopAllDownloadsAction stopAllDownloadsAction = new StopAllDownloadsAction(this); + protected StopAllWaitingDownloadsAction stopAllWaitingDownloadsAction = new StopAllWaitingDownloadsAction(this); + protected RefreshDownloadListAction refreshDownloadListAction = new RefreshDownloadListAction(this); + protected CleanupDownloadListAction cleanupDownloadListAction = new CleanupDownloadListAction(this); + protected InvertSelectionAction invertSelectionAction = new InvertSelectionAction(this); + protected PlayDownloadAction playDownloadAction = new PlayDownloadAction(this); + protected StopDownloadsAction stopDownloadsAction = new StopDownloadsAction(this); + protected StartDownloadsAction startDownloadsAction = new StartDownloadsAction(this); + protected DeferDownloadsAction deferDownloadsAction = new DeferDownloadsAction(this); + protected AdvanceDownloadsAction advanceDownloadsAction = new AdvanceDownloadsAction(this); + protected DeleteDownloadsAction deleteDownloadsAction = new DeleteDownloadsAction(this); + protected EditDownloadAction editDownloadAction = new EditDownloadAction(this); + protected DeleteDownloadAction deleteDownloadAction = new DeleteDownloadAction(this); + protected OpenTargetFolderAction openTargetFolderAction = new OpenTargetFolderAction(this); + protected ToggleFilterPanelAction toggleFilterPanelAction = new ToggleFilterPanelAction(); + protected JToolBar swingToolBar = new JToolBar(); private boolean onlyAbos; private boolean onlyDownloads; - private boolean onlyWaiting; private boolean onlyNotStarted; private boolean onlyStarted; @@ -112,28 +120,38 @@ public class GuiDownloads extends AGuiTabPanel { * The internally used model. */ private TModelDownload model; - private DownloadTabInformationLabel filmInfoLabel; private MVDownloadsTable tabelle; + private JSplitPane jSplitPane1; + private JPanel jPanelFilterExtern; + private JComboBox cbDisplayCategories; + private JComboBox cbView; + private JButton btnClear; + private JScrollPane downloadListScrollPane; public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { super(); daten = aDaten; this.mediathekGui = mediathekGui; + descriptionPanel = new FilmDescriptionPanel(this); + initComponents(); + // use rounded combo boxes + cbDisplayCategories.putClientProperty( "JComponent.roundRect", true ); + cbView.putClientProperty( "JComponent.roundRect", true ); + + setupDownloadListStatusBar(); setupF4Key(mediathekGui); setupDownloadListTable(); - setupDescriptionPanel(); - showDescriptionPanel(); + setupDescriptionTab(tabelle, cbShowDownloadDescription, ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION); init(); - installTabInfoStatusBarControl(); - - setupFilmSelectionPropertyListener(mediathekGui); + setupFilmSelectionPropertyListener(); + setupDownloadSizeSelectionUpdater(); initTable(); @@ -142,18 +160,22 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { setupCheckboxView(); - setupToolBar(); - - setupDownloadRateLimitSpinner(); - setupFilterPanel(); - setupComponentListener(); - if (Taskbar.isTaskbarSupported()) setupTaskbarMenu(); } + private void setupDownloadListStatusBar() { + statusBar.add(totalDownloadsLabel); + statusBar.add(lblAbos); + statusBar.add(manualDownloadsInfoLabel); + statusBar.add(activeDownloadsInfoLabel); + statusBar.add(waitingDownloadsInfoLabel); + statusBar.add(finishedDownloadsInfoLabel); + statusBar.add(failedDownloadsInfoLabel); + } + @Override public void tabelleSpeichern() { if (tabelle != null) { @@ -161,34 +183,6 @@ public void tabelleSpeichern() { } } - private void setupToolBar() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - var toolBar = new FXDownloadToolBar(); - toolBar.btnFilmInfo.setOnAction(e -> SwingUtilities.invokeLater(() -> MediathekGui.ui().getFilmInfoDialog().showInfo())); - toolBar.btnUpdateDownloads.setOnAction(e -> SwingUtilities.invokeLater(this::updateDownloads)); - toolBar.btnStartAllDownloads.setOnAction(e -> SwingUtilities.invokeLater(() -> starten(true))); - toolBar.btnPlayFilm.setOnAction(e -> SwingUtilities.invokeLater(this::filmAbspielen)); - toolBar.btnZurueckstellen.setOnAction(e -> SwingUtilities.invokeLater(() -> downloadLoeschen(false))); - toolBar.btnRemoveDownload.setOnAction(e -> SwingUtilities.invokeLater(() -> downloadLoeschen(true))); - toolBar.btnCleanup.setOnAction(e -> SwingUtilities.invokeLater(this::cleanupDownloads)); - toolBar.btnFilter.setOnAction(e -> SwingUtilities.invokeLater(() -> MessageBus.getMessageBus().publishAsync(new DownloadFilterVisibilityChangedEvent()))); - - Daten.getInstance().getFilmeLaden().addAdListener(new ListenerFilmeLaden() { - @Override - public void start(ListenerFilmeLadenEvent event) { - Platform.runLater(() -> toolBar.btnUpdateDownloads.setDisable(true)); - } - - @Override - public void fertig(ListenerFilmeLadenEvent event) { - Platform.runLater(() -> toolBar.btnUpdateDownloads.setDisable(false)); - } - }); - - toolBarPanel.setScene(new Scene(toolBar)); - }); - } - private void setupF4Key(MediathekGui mediathekGui) { if (SystemUtils.IS_OS_WINDOWS) { // zum Abfangen der Win-F4 für comboboxen @@ -204,55 +198,58 @@ public void actionPerformed(ActionEvent e) { } } + private void updateFilmSizes(int[] rows) { + boolean updateNeeded = false; + + for (var row : rows) { + var indexRow = tabelle.convertRowIndexToModel(row); + var listeDownloads = daten.getListeDownloads(); + var dlInfo = listeDownloads.get(indexRow); + if (dlInfo != null) { + if (dlInfo.mVFilmSize.getSize() != 0) + continue; + + if (dlInfo.film != null) { + var oldSize = dlInfo.mVFilmSize.getSize(); + dlInfo.queryLiveSize(); + if (dlInfo.mVFilmSize.getSize() != oldSize) + updateNeeded = true; + } + } else + logger.error("Could not get download object"); + } + + if (updateNeeded) + reloadTable(); + } + + private void setupDownloadSizeSelectionUpdater() { + tabelle.getSelectionModel().addListSelectionListener(l -> { + if (!l.getValueIsAdjusting()) { + var rows = tabelle.getSelectedRows(); + updateFilmSizes(rows); + } + }); + } + /** * Update the property with the current number of selected entries from the JTable. */ - private void setupFilmSelectionPropertyListener(MediathekGui mediathekGui) { + private void setupFilmSelectionPropertyListener() { tabelle.getSelectionModel().addListSelectionListener(e -> { if (!e.getValueIsAdjusting()) { - final int sel = tabelle.getSelectedRowCount(); - Platform.runLater(() -> mediathekGui.getSelectedItemsProperty().setValue(sel)); + updateSelectedListItemsCount(tabelle); } }); addComponentListener(new ComponentAdapter() { @Override public void componentShown(ComponentEvent e) { - final int sel = tabelle.getSelectedRowCount(); - Platform.runLater(() -> mediathekGui.getSelectedItemsProperty().setValue(sel)); + updateSelectedListItemsCount(tabelle); onComponentShown(); } }); } - @Override - protected void installTabInfoStatusBarControl() { - final var leftItems = mediathekGui.getStatusBarController().getStatusBar().getLeftItems(); - - Platform.runLater(() -> { - filmInfoLabel = new DownloadTabInformationLabel(daten); - if (isVisible()) - leftItems.add(filmInfoLabel); - }); - - addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - Platform.runLater(() -> { - filmInfoLabel.setVisible(true); - leftItems.add(filmInfoLabel); - }); - } - - @Override - public void componentHidden(ComponentEvent e) { - Platform.runLater(() -> { - filmInfoLabel.setVisible(false); - leftItems.remove(filmInfoLabel); - }); - } - }); - } - private void setupDownloadListTable() { tabelle = new MVDownloadsTable(); downloadListScrollPane.setViewportView(tabelle); @@ -281,56 +278,37 @@ private void initTable() { } } - private void setupComponentListener() { - addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - tabVisible.set(true); - } - - @Override - public void componentHidden(ComponentEvent e) { - tabVisible.set(false); - } - }); - } - private void setupFilterPanel() { final boolean visible = MVConfig.getBool(MVConfig.Configs.SYSTEM_TAB_DOWNLOAD_FILTER_VIS); updateFilterVisibility(visible); - var config = ApplicationConfiguration.getConfiguration(); - - final int location = config.getInt(ApplicationConfiguration.APPLICATION_UI_DOWNLOAD_TAB_DIVIDER_LOCATION, Konstanten.GUIDOWNLOAD_DIVIDER_LOCATION); - jSplitPane1.setDividerLocation(location); + setSplitDividerLocation(); jSplitPane1.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, pce -> { if (jPanelFilterExtern.isVisible()) { - config.setProperty(ApplicationConfiguration.APPLICATION_UI_DOWNLOAD_TAB_DIVIDER_LOCATION,jSplitPane1.getDividerLocation()); + config.setProperty(ApplicationConfiguration.APPLICATION_UI_DOWNLOAD_TAB_DIVIDER_LOCATION, jSplitPane1.getDividerLocation()); } }); } - @Handler - private void handleParallelDownloadNumberChange(ParallelDownloadNumberChangedEvent e) { - SwingUtilities.invokeLater(() -> { - final int maxNumDownloads = ApplicationConfiguration.getConfiguration().getInt(ApplicationConfiguration.DOWNLOAD_MAX_SIMULTANEOUS_NUM,1); - jSpinnerAnzahlDownloads.setValue(maxNumDownloads); - }); - } - - @Handler - private void handleDownloadFilterVisibilityChanged(DownloadFilterVisibilityChangedEvent e) { - SwingUtilities.invokeLater(() -> { - boolean visibility = !jPanelFilterExtern.isVisible(); - updateFilterVisibility(visibility); - MVConfig.add(MVConfig.Configs.SYSTEM_TAB_DOWNLOAD_FILTER_VIS, Boolean.toString(visibility)); - }); + protected void toggleDownloadFilterPanel() { + boolean visibility = !jPanelFilterExtern.isVisible(); + updateFilterVisibility(visibility); + MVConfig.add(MVConfig.Configs.SYSTEM_TAB_DOWNLOAD_FILTER_VIS, Boolean.toString(visibility)); } private void updateFilterVisibility(boolean visible) { jPanelFilterExtern.setVisible(visible); if (visible) { - final int location = config.getInt(ApplicationConfiguration.APPLICATION_UI_DOWNLOAD_TAB_DIVIDER_LOCATION, Konstanten.GUIDOWNLOAD_DIVIDER_LOCATION); + setSplitDividerLocation(); + } + } + + private void setSplitDividerLocation() { + var location = config.getInt(ApplicationConfiguration.APPLICATION_UI_DOWNLOAD_TAB_DIVIDER_LOCATION, DIVIDER_LOCATION); + if (location == DIVIDER_LOCATION) { + jSplitPane1.resetToPreferredSizes(); + } + else { jSplitPane1.setDividerLocation(location); } } @@ -353,89 +331,12 @@ private void setupTaskbarMenu() { } } - @Handler - private void handleDownloadInfoUpdate(DownloadInfoUpdateAvailableEvent e) { - if (tabVisible.get()) { - SwingUtilities.invokeLater(() -> { - if (txtDownload.isShowing()) - setInfoText(); - }); - } - } - - private void setupDownloadRateLimitSpinner() { - //restore spinner setting from config - final int oldDownloadLimit = ApplicationConfiguration.getConfiguration().getInt(ApplicationConfiguration.DOWNLOAD_RATE_LIMIT, 0); - jSpinner1.setValue(oldDownloadLimit); - - jSpinner1.addChangeListener(e -> { - final int downloadLimit = (int) jSpinner1.getValue(); - logger.info("Saving download rate limit {} to config", downloadLimit); - ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.DOWNLOAD_RATE_LIMIT, downloadLimit); - DownloadRateLimitChangedEvent evt = new DownloadRateLimitChangedEvent(); - evt.newLimit = downloadLimit; - MessageBus.getMessageBus().publishAsync(evt); - }); + public MVDownloadsTable getTableComponent() { + return tabelle; } @Override public void installMenuEntries(JMenu menu) { - JMenuItem miDownloadsStartAll = new JMenuItem("Alle Downloads starten"); - miDownloadsStartAll.setIcon(IconFontSwing.buildIcon(FontAwesome.ANGLE_DOUBLE_DOWN, 16)); - miDownloadsStartAll.addActionListener(e -> starten(true)); - - JMenuItem miDownloadStartTimed = new JMenuItem("Alle Downloads zeitverzögert starten..."); - miDownloadStartTimed.addActionListener(e -> startAllDownloadsAtSpecificTime()); - - JMenuItem miStopAllDownloads = new JMenuItem("Alle Downloads stoppen"); - miStopAllDownloads.addActionListener(e -> stoppen(true)); - - JMenuItem miStopWaitingDownloads = new JMenuItem("Wartende Downloads stoppen"); - miStopWaitingDownloads.addActionListener(e -> stopAllWaitingDownloads()); - - JMenuItem miUpdateDownloads = new JMenuItem("Liste der Downloads aktualisieren"); - miUpdateDownloads.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK)); - miUpdateDownloads.setIcon(IconFontSwing.buildIcon(FontAwesome.REFRESH, 16)); - miUpdateDownloads.addActionListener(e -> updateDownloads()); - daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { - @Override - public void start(ListenerFilmeLadenEvent event) { - miUpdateDownloads.setEnabled(false); - } - - @Override - public void fertig(ListenerFilmeLadenEvent event) { - miUpdateDownloads.setEnabled(true); - } - }); - - JMenuItem miCleanupDownloads = new JMenuItem(MENU_ITEM_TEXT_CLEANUP_DOWNLOADS); - miCleanupDownloads.setIcon(IconFontSwing.buildIcon(FontAwesome.ERASER, 16)); - miCleanupDownloads.addActionListener(e -> cleanupDownloads()); - - JMenuItem miStartDownloads = new JMenuItem("Ausgewählte Downloads starten"); - miStartDownloads.setIcon(IconFontSwing.buildIcon(FontAwesome.CARET_DOWN, 16)); - miStartDownloads.addActionListener(e -> starten(false)); - - JMenuItem miStopDownloads = new JMenuItem("Ausgewählte Downloads stoppen"); - miStopDownloads.addActionListener(e -> stoppen(false)); - - JMenuItem miDownloadsVorziehen = new JMenuItem("Downloads vorziehen"); - miDownloadsVorziehen.setIcon(Icons.ICON_MENUE_VORZIEHEN); - miDownloadsVorziehen.addActionListener(e -> downloadsVorziehen()); - - JMenuItem miDownloadsZurueckstellen = new JMenuItem("Downloads zurückstellen"); - miDownloadsZurueckstellen.setIcon(IconFontSwing.buildIcon(FontAwesome.CLOCK_O, 16)); - miDownloadsZurueckstellen.addActionListener(e -> downloadLoeschen(false)); - - JMenuItem miDownloadsLoeschen = new JMenuItem("Downloads aus Liste entfernen"); - miDownloadsLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); - miDownloadsLoeschen.addActionListener(e -> downloadLoeschen(true)); - - JMenuItem miEditDownload = new JMenuItem("Download ändern"); - miEditDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.PENCIL_SQUARE_O, 16)); - miEditDownload.addActionListener(e -> editDownload()); - JMenuItem miMarkFilmAsSeen = new JMenuItem("Filme als gesehen markieren"); miMarkFilmAsSeen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.CTRL_DOWN_MASK)); miMarkFilmAsSeen.addActionListener(markFilmAsSeenAction); @@ -444,79 +345,30 @@ public void fertig(ListenerFilmeLadenEvent event) { miMarkFilmAsUnseen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK)); miMarkFilmAsUnseen.addActionListener(markFilmAsUnseenAction); - JMenuItem miPlayDownload = new JMenuItem("Gespeicherten Film abspielen"); - miPlayDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.PLAY, 16)); - miPlayDownload.addActionListener(e -> filmAbspielen()); - - JMenuItem miInvertSelection = new JMenuItem("Auswahl umkehren"); - miInvertSelection.addActionListener(e -> tabelle.invertSelection()); - - JMenuItem miShutdownAfterDownload = new JMenuItem("Aktion nach abgeschlossenen Downloads..."); - miShutdownAfterDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.POWER_OFF, 16)); - miShutdownAfterDownload.addActionListener(e -> { - if (daten.getListeDownloads().unfinishedDownloads() > 0) { - // ansonsten gibts keine laufenden Downloads auf die man warten sollte - mediathekGui.beenden(true, false); - } else { - JOptionPane.showMessageDialog(this, - "Die Downloads müssen zuerst gestartet werden.", - "Keine laufenden Downloads", - JOptionPane.ERROR_MESSAGE); - - } - }); - - menu.add(miDownloadsStartAll); - menu.add(miDownloadStartTimed); - menu.add(miStopAllDownloads); - menu.add(miStopWaitingDownloads); - menu.add(miUpdateDownloads); - menu.add(miCleanupDownloads); + menu.add(startAllDownloadsAction); + menu.add(startAllDownloadsTimedAction); + menu.add(stopAllDownloadsAction); + menu.add(stopAllWaitingDownloadsAction); + menu.add(refreshDownloadListAction); + menu.add(cleanupDownloadListAction); menu.addSeparator(); - menu.add(miStartDownloads); - menu.add(miStopDownloads); - menu.add(miDownloadsVorziehen); - menu.add(miDownloadsZurueckstellen); - menu.add(miDownloadsLoeschen); - menu.add(miEditDownload); + menu.add(startDownloadsAction); + menu.add(stopDownloadsAction); + menu.add(advanceDownloadsAction); + menu.add(deferDownloadsAction); + menu.add(deleteDownloadsAction); + menu.add(editDownloadAction); menu.addSeparator(); menu.add(cbShowDownloadDescription); menu.addSeparator(); menu.add(miMarkFilmAsSeen); menu.add(miMarkFilmAsUnseen); - menu.add(miPlayDownload); + menu.add(playDownloadAction); menu.addSeparator(); - menu.add(miInvertSelection); - menu.addSeparator(); - menu.add(miShutdownAfterDownload); - } - - private void setupDescriptionPanel() { - Platform.runLater(() -> { - try { - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(Konstanten.FXML_FILM_DESCRIPTION_PANEL_URL); - - TabPane descriptionPane = loader.load(); - final DescriptionPanelController descriptionPanelController = loader.getController(); - descriptionPanelController.setOnCloseRequest(e -> { - SwingUtilities.invokeLater(() -> fxDescriptionPanel.setVisible(false)); - e.consume(); - }); - - fxDescriptionPanel.setScene(new Scene(descriptionPane)); - SwingUtilities.invokeLater(() -> tabelle.getSelectionModel().addListSelectionListener(e -> { - Optional optFilm = getCurrentlySelectedFilm(); - Platform.runLater(() -> descriptionPanelController.showFilmDescription(optFilm)); - })); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); + menu.add(invertSelectionAction); } public void onComponentShown() { - mediathekGui.tabPaneIndexProperty().setValue(TabPaneIndex.DOWNLOAD); updateFilmData(); } @@ -528,10 +380,6 @@ public void stoppen(boolean alle) { filmStartenWiederholenStoppen(alle, false, true, false); } - private final MarkFilmAsSeenAction markFilmAsSeenAction = new MarkFilmAsSeenAction(); - - private final MarkFilmAsUnseenAction markFilmAsUnseenAction = new MarkFilmAsUnseenAction(); - private void setupKeyMappings() { final InputMap im = tabelle.getInputMap(); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ACTION_MAP_KEY_EDIT_DOWNLOAD); @@ -589,114 +437,12 @@ private void init() { new int[]{DatenDownload.DOWNLOAD_BUTTON_START, DatenDownload.DOWNLOAD_BUTTON_DEL}, true, MVConfig.Configs.SYSTEM_TAB_DOWNLOAD_LINEBREAK)); - btnClear.setIcon(Icons.ICON_BUTTON_CLEAR); btnClear.addActionListener(l -> { cbDisplayCategories.setSelectedIndex(0); cbView.setSelectedIndex(0); }); - jSpinnerAnzahlDownloads.setModel(new SpinnerNumberModel(1, 1, 9, 1)); - jSpinnerAnzahlDownloads.setValue(config.getInt(ApplicationConfiguration.DOWNLOAD_MAX_SIMULTANEOUS_NUM,1)); - jSpinnerAnzahlDownloads.addChangeListener(l -> { - final int maxNumDownloads = ((Number)jSpinnerAnzahlDownloads.getModel().getValue()).intValue(); - config.setProperty(ApplicationConfiguration.DOWNLOAD_MAX_SIMULTANEOUS_NUM, maxNumDownloads); - MessageBus.getMessageBus().publishAsync(new ParallelDownloadNumberChangedEvent()); - }); - - final int location = config.getInt(ApplicationConfiguration.APPLICATION_UI_DOWNLOAD_TAB_DIVIDER_LOCATION, Konstanten.GUIDOWNLOAD_DIVIDER_LOCATION); - jSplitPane1.setDividerLocation(location); - - setupInfoPanel(); - daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { - @Override - public void start(ListenerFilmeLadenEvent event) { - loadFilmlist = true; - } - - @Override - public void fertig(ListenerFilmeLadenEvent event) { - loadFilmlist = false; - daten.getListeDownloads().filmEintragen(); - if (Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_ABOS_SOFORT_SUCHEN))) { - updateDownloads(); - } else { - reloadTable(); // damit die Filmnummern richtig angezeigt werden - } - } - }); - } - - private void setupInfoPanel() { - txtDownload.setText(""); - txtDownload.setEditable(false); - txtDownload.setFocusable(false); - txtDownload.setContentType("text/html"); - } - - private void setInfoText() { - if (daten.getListeDownloads().getStarts().total_starts == 0) { - txtDownload.setText(""); - return; - } - - String info = HEAD; - - // Downloads - info += getInfoText(); - - final var downloadInfos = daten.getDownloadInfos(); - // Größe - final long byteAlleDownloads = downloadInfos.getByteAlleDownloads(); - final long byteAktDownloads = downloadInfos.getByteAktDownloads(); - if (byteAlleDownloads > 0 || byteAktDownloads > 0) { - info += "
"; - info += "Größe:
"; - if (byteAktDownloads > 0) { - info += FileSize.convertSize(byteAktDownloads) + " von " - + FileSize.convertSize(byteAlleDownloads) + " MByte" + "
"; - } else { - info += FileSize.convertSize(byteAlleDownloads) + " MByte" + ""; - } - } - // Restzeit - final long timeRestAktDownloads = downloadInfos.getTimeRestAktDownloads(); - final long timeRestAllDownloads = downloadInfos.getTimeRestAllDownloads(); - if (timeRestAktDownloads > 0 && timeRestAllDownloads > 0) { - info += "
"; - info += "Restzeit:
" + "laufende: " - + downloadInfos.getRestzeit() + ",
alle: " + downloadInfos.getGesamtRestzeit() + "
"; - } else if (timeRestAktDownloads > 0) { - info += "
"; - info += "Restzeit:
laufende: " + downloadInfos.getRestzeit() + "
"; - } else if (timeRestAllDownloads > 0) { - info += "
"; - info += "Restzeit:
alle: " + downloadInfos.getGesamtRestzeit() + "
"; - } - - info += END; - - txtDownload.setText(info); - } - - private String getInfoText() { - String textLinks; - final var info = daten.getListeDownloads().getStarts(); - textLinks = "Downloads: " + info.total_starts + "
"; - - if (info.hasValues()) { - textLinks += "( "; - textLinks += (info.running == 1) ? "1 läuft" : info.running + " laufen"; - textLinks += (info.initialized == 1) ? ", 1 wartet" : ", " + info.initialized + " warten"; - if (info.finished > 0) - textLinks += (info.finished == 1) ? ", 1 fertig" : ", " + info.finished + " fertig"; - - if (info.error > 0) - textLinks += (info.error == 1) ? ", 1 fehlerhaft" : ", " + info.error + " fehlerhaft"; - - textLinks += " )"; - } - textLinks += "
"; - return textLinks; + setSplitDividerLocation(); } @Handler @@ -755,8 +501,6 @@ public void ping() { } } }); - - setupShowFilmDescriptionMenuItem(); } @Handler @@ -773,7 +517,7 @@ private void handleDownloadProgressChanged(DownloadProgressChangedEvent e) { private void handleGeoStateChangedEvent(GeoStateChangedEvent e) { SwingUtilities.invokeLater(() -> { tabelle.fireTableDataChanged(true); - setInfo(); + updateStartInfoProperty(); }); } @@ -782,30 +526,16 @@ private void handleGeoStateChangedEvent(GeoStateChangedEvent e) { * Most of the setup is done in {@link GuiDownloads} function. * Here we just display the panel */ - private void setupShowFilmDescriptionMenuItem() { + @Override + protected void setupShowFilmDescriptionMenuItem() { cbShowDownloadDescription.setSelected(ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION, true)); - cbShowDownloadDescription.addActionListener(l -> fxDescriptionPanel.setVisible(cbShowDownloadDescription.isSelected())); - cbShowDownloadDescription.addItemListener(e -> ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION, cbShowDownloadDescription.isSelected())); - fxDescriptionPanel.addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - cbShowDownloadDescription.setSelected(true); - } - - @Override - public void componentHidden(ComponentEvent e) { - cbShowDownloadDescription.setSelected(false); - } + cbShowDownloadDescription.addActionListener(l -> { + boolean visible = cbShowDownloadDescription.isSelected(); + makeDescriptionTabVisible(visible); + config.setProperty(ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION, visible); }); } - /** - * Show description panel based on settings. - */ - private void showDescriptionPanel() { - fxDescriptionPanel.setVisible(ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION, true)); - } - private synchronized void reloadTable() { // nur Downloads die schon in der Liste sind werden geladen tabelle.getSpalten(); @@ -813,7 +543,7 @@ private synchronized void reloadTable() { daten.getListeDownloads().getModel(model, onlyAbos, onlyDownloads, onlyNotStarted, onlyStarted, onlyWaiting, onlyRun, onlyFinished); tabelle.setSpalten(); updateFilmData(); - setInfo(); + updateStartInfoProperty(); } @Handler @@ -861,13 +591,13 @@ private ArrayList getSelDownloads() { arrayDownloads.add(datenDownload); } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } return arrayDownloads; } @Override - protected Optional getCurrentlySelectedFilm() { + public Optional getCurrentlySelectedFilm() { final int selectedTableRow = tabelle.getSelectedRow(); if (selectedTableRow != -1) { Optional optRet; @@ -889,7 +619,7 @@ private DatenDownload getSelDownload() { if (row != -1) { datenDownload = (DatenDownload) tabelle.getModel().getValueAt(tabelle.convertRowIndexToModel(row), DatenDownload.DOWNLOAD_REF); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } return datenDownload; } @@ -914,7 +644,7 @@ public synchronized void editDownload() { } } - private void downloadsVorziehen() { + public void downloadsVorziehen() { ArrayList arrayDownloads = getSelDownloads(); if (arrayDownloads.isEmpty()) { return; @@ -922,7 +652,7 @@ private void downloadsVorziehen() { daten.getListeDownloads().downloadsVorziehen(arrayDownloads); } - private void zielordnerOeffnen() { + public void zielordnerOeffnen() { DatenDownload datenDownload = getSelDownload(); if (datenDownload == null) { return; @@ -940,7 +670,7 @@ public void filmAbspielen() { OpenPlayerAction.filmAbspielen(mediathekGui, s); } - private void filmLoeschen_() { + public void filmLoeschen_() { DatenDownload datenDownload = getSelDownload(); if (datenDownload == null) { return; @@ -1016,8 +746,7 @@ public void downloadLoeschen(boolean permanentDeletion) { } } - private @NotNull List addAllDownloadsToList() - { + private @NotNull List addAllDownloadsToList() { final var rowCount = tabelle.getRowCount(); final var tableModel = tabelle.getModel(); List destList = new ArrayList<>(); @@ -1038,14 +767,10 @@ public void startAllDownloadsAtSpecificTime() { // Film dessen Start schon auf fertig/fehler steht wird wieder gestartet // wird immer vom Benutzer aufgerufen if (tabelle.getRowCount() == 0) { - Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText("Keine Downloads vorhanden"); - alert.setContentText("Es sind keine Downloads in der Liste zum Starten vorhanden."); - alert.initModality(Modality.APPLICATION_MODAL); - alert.showAndWait(); - }); + JOptionPane.showMessageDialog(this, + "Es sind keine Downloads in der Liste zum Starten vorhanden.", + Konstanten.PROGRAMMNAME, + JOptionPane.INFORMATION_MESSAGE); return; } @@ -1070,16 +795,17 @@ public void startAllDownloadsAtSpecificTime() { if (download.start.status > Start.STATUS_RUN) { // wenn er noch läuft gibts nix // wenn er schon fertig ist, erst mal fragen vor dem erneuten Starten - //TODO in auto dialog umwandeln! - int a = JOptionPane.showConfirmDialog(mediathekGui, "Film nochmal starten? ==> " + download.arr[DatenDownload.DOWNLOAD_TITEL], - "Fertiger Download", JOptionPane.YES_NO_OPTION); - if (a != JOptionPane.YES_OPTION) { + int reply = GuiFunktionen.createDismissableMessageDialog(mediathekGui, "Fertiger Download", + "Film nochmal starten? ==> " + download.arr[DatenDownload.DOWNLOAD_TITEL], + JOptionPane.YES_NO_OPTION, JOptionPane.NO_OPTION, 10, TimeUnit.SECONDS, + JOptionPane.QUESTION_MESSAGE); + if (reply != JOptionPane.YES_OPTION) { // weiter mit der nächsten URL continue; } listeUrlsDownloadsAbbrechen.add(download); if (download.isFromAbo()) { - // wenn er schon feritg ist und ein Abos ist, Url auch aus dem Logfile löschen, der Film ist damit wieder auf "Anfang" + // wenn er schon fertig ist und ein Abo ist, Url auch aus dem Logfile löschen, der Film ist damit wieder auf "Anfang" daten.getAboHistoryController().removeUrl(download.arr[DatenDownload.DOWNLOAD_HISTORY_URL]); } } @@ -1096,7 +822,8 @@ public void startAllDownloadsAtSpecificTime() { dialogBeenden.setVisible(true); if (dialogBeenden.applicationCanTerminate()) { // fertig und beenden - mediathekGui.beenden(false, dialogBeenden.isShutdownRequested()); + mediathekGui.setShutdownRequested(dialogBeenden.isShutdownRequested()); + mediathekGui.quitApplication(); } reloadTable(); @@ -1148,7 +875,6 @@ private void filmStartenWiederholenStoppen(boolean processAllDownloads, boolean } if (download.start.status > Start.STATUS_RUN) { // wenn er schon fertig ist, erst mal fragen vor dem erneuten Starten - //TODO in auto dialog umwandeln! if (antwort == -1) { // nur einmal fragen String text; @@ -1158,8 +884,10 @@ private void filmStartenWiederholenStoppen(boolean processAllDownloads, boolean } else { text = "Film nochmal starten? ==> " + download.arr[DatenDownload.DOWNLOAD_TITEL]; } - antwort = JOptionPane.showConfirmDialog(mediathekGui, text, - "Fertiger Download", JOptionPane.YES_NO_CANCEL_OPTION); + antwort = GuiFunktionen.createDismissableMessageDialog(mediathekGui, "Fertiger Download", + text, + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.NO_OPTION, 10, TimeUnit.SECONDS, + JOptionPane.QUESTION_MESSAGE); } if (antwort == JOptionPane.CANCEL_OPTION) { //============================= @@ -1219,23 +947,19 @@ public void stopAllWaitingDownloads() { daten.getListeDownloads().downloadAbbrechen(listeStopDownload); } - private void setInfo() { - MessageBus.getMessageBus().publishAsync(new UpdateStatusBarLeftDisplayEvent()); - } - private void updateFilmData() { if (isShowing()) { - DatenFilm aktFilm = null; + DatenFilm selectedFilm = null; final int selectedTableRow = tabelle.getSelectedRow(); - if (selectedTableRow >= 0) { + if (selectedTableRow != -1) { final DatenDownload datenDownload = (DatenDownload) tabelle.getModel().getValueAt(tabelle.convertRowIndexToModel(selectedTableRow), DatenDownload.DOWNLOAD_REF); if (datenDownload != null) { - aktFilm = datenDownload.film; + selectedFilm = datenDownload.film; } } var infoDialog = mediathekGui.getFilmInfoDialog(); if (infoDialog != null) { - infoDialog.updateCurrentFilm(aktFilm); + infoDialog.updateCurrentFilm(selectedFilm); } } } @@ -1252,15 +976,146 @@ protected List getSelFilme() { } } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } return arrayFilme; } - public class BeobMausTabelle extends MouseAdapter { + private void initComponents() { + jSplitPane1 = new JSplitPane(); + jPanelFilterExtern = new JPanel(); + var panel3 = new JPanel(); + var label1 = new JLabel(); + cbDisplayCategories = new JComboBox<>(); + var label2 = new JLabel(); + cbView = new JComboBox<>(); + btnClear = new JButton(); + var downloadListArea = new JPanel(); + downloadListScrollPane = new JScrollPane(); - private final ShowFilmInformationAction showFilmInformationAction = new ShowFilmInformationAction(false); - DatenDownload datenDownload; + //======== this ======== + setLayout(new BorderLayout()); + + //======== jSplitPane1 ======== + { + jSplitPane1.setDividerLocation(330); + + //======== jPanelFilterExtern ======== + { + jPanelFilterExtern.setPreferredSize(new Dimension(200, 644)); + jPanelFilterExtern.setLayout(new MigLayout( + new LC().insets("0").hideMode(3).gridGap("0", "0"), //NON-NLS + // columns + new AC() + .grow().fill(), + // rows + new AC() + .gap() + .fill().gap() + .grow().fill())); + + //======== panel3 ======== + { + panel3.setBorder(new TitledBorder("Anzeige")); //NON-NLS + panel3.setLayout(new MigLayout( + new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .fill().gap() + .grow().fill(), + // rows + new AC() + .fill().gap() + .fill().gap() + .fill())); + + //---- label1 ---- + label1.setText("Typ:"); //NON-NLS + panel3.add(label1, new CC().cell(0, 0)); + panel3.add(cbDisplayCategories, new CC().cell(1, 0)); + + //---- label2 ---- + label2.setText("Status:"); //NON-NLS + panel3.add(label2, new CC().cell(0, 1)); + panel3.add(cbView, new CC().cell(1, 1)); + + //---- btnClear ---- + btnClear.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/broom.svg")); //NON-NLS + btnClear.setToolTipText("Filter zur\u00fccksetzen"); //NON-NLS + panel3.add(btnClear, new CC().cell(0, 2, 2, 1).alignX("right").growX(0).width("32:32:32").height("32:32:32")); //NON-NLS + } + jPanelFilterExtern.add(panel3, new CC().cell(0, 0)); + jPanelFilterExtern.add(dlConfigPanel, new CC().cell(0, 1)); + } + jSplitPane1.setLeftComponent(jPanelFilterExtern); + + //======== downloadListArea ======== + { + downloadListArea.setLayout(new BorderLayout()); + JPanel tempPanel = new JPanel(); + tempPanel.setLayout(new BorderLayout()); + tempPanel.add(downloadListScrollPane, BorderLayout.CENTER); + tempPanel.add(statusBar, BorderLayout.SOUTH); + downloadListArea.add(tempPanel, BorderLayout.CENTER); + downloadListArea.add(descriptionTab, BorderLayout.SOUTH); + } + jSplitPane1.setRightComponent(downloadListArea); + } + add(jSplitPane1, BorderLayout.CENTER); + add(swingToolBar, BorderLayout.NORTH); + + createSwingToolBar(); + + daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { + @Override + public void start(ListenerFilmeLadenEvent event) { + loadFilmlist = true; + SwingUtilities.invokeLater(() -> refreshDownloadListAction.setEnabled(false)); + } + + @Override + public void fertig(ListenerFilmeLadenEvent event) { + loadFilmlist = false; + SwingUtilities.invokeLater(() -> refreshDownloadListAction.setEnabled(true)); + daten.getListeDownloads().filmEintragen(); + if (Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_ABOS_SOFORT_SUCHEN))) { + updateDownloads(); + } else { + reloadTable(); // damit die Filmnummern richtig angezeigt werden + } + } + }); + } + + protected void createSwingToolBar() { + swingToolBar.setFloatable(true); + swingToolBar.setName("Downloads"); + + swingToolBar.add(refreshDownloadListAction); + swingToolBar.add(startAllDownloadsAction); + swingToolBar.add(playDownloadAction); + swingToolBar.add(deferDownloadsAction); + swingToolBar.add(deleteDownloadsAction); + swingToolBar.add(cleanupDownloadListAction); + swingToolBar.addSeparator(); + swingToolBar.add(toggleFilterPanelAction); + } + + class ToggleFilterPanelAction extends AbstractAction { + public ToggleFilterPanelAction() { + putValue(Action.NAME, "Filter anzeigen/ausblenden"); + putValue(Action.SHORT_DESCRIPTION, "Filter anzeigen/ausblenden"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/filter.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + toggleDownloadFilterPanel(); + } + } + + public class BeobMausTabelle extends MouseAdapter { + private DatenDownload datenDownload; private Point p; @Override @@ -1353,7 +1208,7 @@ private void showMenu(MouseEvent evt) { } // Download starten JMenuItem itemStarten = new JMenuItem("Download starten"); - itemStarten.setIcon(IconFontSwing.buildIcon(FontAwesome.CARET_DOWN, 16)); + itemStarten.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/caret-down.svg")); itemStarten.setEnabled(!wartenOderLaufen); jPopupMenu.add(itemStarten); itemStarten.addActionListener(arg0 -> filmStartenWiederholenStoppen(false, true, true, false)); @@ -1365,72 +1220,25 @@ private void showMenu(MouseEvent evt) { itemStoppen.addActionListener(arg0 -> filmStartenWiederholenStoppen(false, false, true, false)); jPopupMenu.addSeparator(); - - JMenuItem itemVorziehen = new JMenuItem("Download vorziehen"); - itemVorziehen.setIcon(Icons.ICON_MENUE_VORZIEHEN); - jPopupMenu.add(itemVorziehen); - itemVorziehen.addActionListener(arg0 -> downloadsVorziehen()); - - JMenuItem itemLoeschen = new JMenuItem("Download zurückstellen"); - itemLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.CLOCK_O, 16)); - jPopupMenu.add(itemLoeschen); - itemLoeschen.addActionListener(arg0 -> downloadLoeschen(false /* dauerhaft */)); - //dauerhaft löschen - JMenuItem itemDauerhaftLoeschen = new JMenuItem("Download aus Liste entfernen"); - itemDauerhaftLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); - jPopupMenu.add(itemDauerhaftLoeschen); - itemDauerhaftLoeschen.addActionListener(arg0 -> downloadLoeschen(true /* dauerhaft */)); - //Download ändern - JMenuItem itemAendern = new JMenuItem("Download ändern"); - itemAendern.setIcon(IconFontSwing.buildIcon(FontAwesome.PENCIL_SQUARE_O, 16)); - jPopupMenu.add(itemAendern); - itemAendern.addActionListener(arg0 -> editDownload()); + jPopupMenu.add(advanceDownloadsAction); + jPopupMenu.add(deferDownloadsAction); + jPopupMenu.add(deleteDownloadsAction); + jPopupMenu.add(editDownloadAction); jPopupMenu.addSeparator(); - - JMenuItem itemAlleStarten = new JMenuItem("Alle Downloads starten"); - itemAlleStarten.setIcon(IconFontSwing.buildIcon(FontAwesome.ANGLE_DOUBLE_DOWN, 16)); - itemAlleStarten.addActionListener(arg0 -> starten(true)); - jPopupMenu.add(itemAlleStarten); - - JMenuItem itemAlleStoppen = new JMenuItem("Alle Downloads stoppen"); - itemAlleStoppen.addActionListener(arg0 -> stoppen(true)); - jPopupMenu.add(itemAlleStoppen); + jPopupMenu.add(startAllDownloadsAction); + jPopupMenu.add(stopAllDownloadsAction); JMenuItem itemWartendeStoppen = new JMenuItem("wartende Downloads stoppen"); jPopupMenu.add(itemWartendeStoppen); itemWartendeStoppen.addActionListener(arg0 -> stopAllWaitingDownloads()); - JMenuItem itemAktualisieren = new JMenuItem("Liste der Downloads aktualisieren"); - itemAktualisieren.setIcon(IconFontSwing.buildIcon(FontAwesome.REFRESH, 16)); - jPopupMenu.add(itemAktualisieren); - itemAktualisieren.addActionListener(arg0 -> updateDownloads()); - - JMenuItem itemAufraeumen = new JMenuItem(MENU_ITEM_TEXT_CLEANUP_DOWNLOADS); - itemAufraeumen.setIcon(IconFontSwing.buildIcon(FontAwesome.ERASER, 16)); - jPopupMenu.add(itemAufraeumen); - itemAufraeumen.addActionListener(arg0 -> cleanupDownloads()); - + jPopupMenu.add(refreshDownloadListAction); + jPopupMenu.add(cleanupDownloadListAction); jPopupMenu.addSeparator(); - - // Film abspielen - JMenuItem itemPlayerDownload = new JMenuItem("gespeicherten Film (Datei) abspielen"); - itemPlayerDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.PLAY, 16)); - - itemPlayerDownload.addActionListener(e -> filmAbspielen()); - jPopupMenu.add(itemPlayerDownload); - // Film löschen - JMenuItem itemDeleteDownload = new JMenuItem("gespeicherten Film (Datei) löschen"); - itemDeleteDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.TIMES, 16)); - - itemDeleteDownload.addActionListener(e -> filmLoeschen_()); - jPopupMenu.add(itemDeleteDownload); - // Zielordner öffnen - JMenuItem itemOeffnen = new JMenuItem("Zielordner öffnen"); - itemOeffnen.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); - jPopupMenu.add(itemOeffnen); - itemOeffnen.addActionListener(e -> zielordnerOeffnen()); - + jPopupMenu.add(playDownloadAction); + jPopupMenu.add(deleteDownloadAction); + jPopupMenu.add(openTargetFolderAction); jPopupMenu.addSeparator(); //Abo ändern @@ -1481,8 +1289,8 @@ private void showMenu(MouseEvent evt) { if (dl.film != null) { DatenFilm filmClone = new DatenFilm(dl.film); // und jetzt die tatsächlichen URLs des Downloads eintragen - filmClone.setUrlNormalQuality(dl.arr[DatenDownload.DOWNLOAD_URL]); - filmClone.setUrlLowQuality(""); + filmClone.setNormalQualityUrl(dl.arr[DatenDownload.DOWNLOAD_URL]); + filmClone.setLowQualityUrl(""); // und starten daten.getStarterClass().urlMitProgrammStarten(gruppe, filmClone, ""); } @@ -1494,7 +1302,7 @@ private void showMenu(MouseEvent evt) { } else { menuPath = "Datei->Einstellungen->Set bearbeiten"; } - MVMessageDialog.showMessageDialog(mediathekGui, "Bitte legen Sie im Menü \"" + menuPath + "\" ein Programm zum Abspielen fest.", + JOptionPane.showMessageDialog(mediathekGui, "Bitte legen Sie im Menü \"" + menuPath + "\" ein Programm zum Abspielen fest.", "Kein Videoplayer!", JOptionPane.INFORMATION_MESSAGE); }); } @@ -1513,7 +1321,7 @@ private void showMenu(MouseEvent evt) { }); jPopupMenu.add(itemUrl); - jPopupMenu.add(showFilmInformationAction); + jPopupMenu.add(mediathekGui.showFilmInformationAction); jPopupMenu.show(evt.getComponent(), evt.getX(), evt.getY()); } @@ -1527,7 +1335,7 @@ private final class ViewCategoryListener implements ActionListener { public void actionPerformed(ActionEvent e) { JComboBox source = (JComboBox) e.getSource(); - switch ((String)source.getModel().getSelectedItem()) { + switch ((String) source.getModel().getSelectedItem()) { case COMBO_VIEW_ALL -> { onlyNotStarted = false; onlyStarted = false; @@ -1583,7 +1391,7 @@ private final class DisplayCategoryListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { JComboBox source = (JComboBox) e.getSource(); - switch ((String)source.getModel().getSelectedItem()) { + switch ((String) source.getModel().getSelectedItem()) { case COMBO_DISPLAY_ALL -> { onlyAbos = false; onlyDownloads = false; @@ -1601,162 +1409,4 @@ public void actionPerformed(ActionEvent e) { reloadTable(); } } - - /** - * This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. - */ - // //GEN-BEGIN:initComponents - // Generated using JFormDesigner non-commercial license - private void initComponents() { - jSplitPane1 = new JSplitPane(); - jPanelFilterExtern = new JPanel(); - var panel3 = new JPanel(); - var label1 = new JLabel(); - cbDisplayCategories = new JComboBox<>(); - var label2 = new JLabel(); - cbView = new JComboBox<>(); - btnClear = new JButton(); - var panel2 = new JPanel(); - var jLabel3 = new JLabel(); - jSpinnerAnzahlDownloads = new JSpinner(); - var lblBandwidth = new JLabel(); - var jLabel1 = new JLabel(); - jSpinner1 = new JSpinner(); - var spDownload = new JScrollPane(); - txtDownload = new JEditorPane(); - var downloadListArea = new JPanel(); - downloadListScrollPane = new JScrollPane(); - fxDescriptionPanel = new JFXPanel(); - toolBarPanel = new JFXPanel(); - - //======== this ======== - setLayout(new BorderLayout()); - - //======== jSplitPane1 ======== - { - jSplitPane1.setDividerLocation(330); - - //======== jPanelFilterExtern ======== - { - jPanelFilterExtern.setPreferredSize(new Dimension(200, 644)); - jPanelFilterExtern.setLayout(new MigLayout( - new LC().insets("0").hideMode(3).gridGap("0", "0"), //NON-NLS - // columns - new AC() - .grow().fill(), - // rows - new AC() - .gap() - .fill().gap() - .grow().fill())); - - //======== panel3 ======== - { - panel3.setBorder(new TitledBorder("Anzeige")); //NON-NLS - panel3.setLayout(new MigLayout( - new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS - // columns - new AC() - .fill().gap() - .grow().fill(), - // rows - new AC() - .fill().gap() - .fill().gap() - .fill())); - - //---- label1 ---- - label1.setText("Typ:"); //NON-NLS - panel3.add(label1, new CC().cell(0, 0)); - panel3.add(cbDisplayCategories, new CC().cell(1, 0)); - - //---- label2 ---- - label2.setText("Status:"); //NON-NLS - panel3.add(label2, new CC().cell(0, 1)); - panel3.add(cbView, new CC().cell(1, 1)); - - //---- btnClear ---- - btnClear.setIcon(new ImageIcon(getClass().getResource("/mediathek/res/muster/button-clear.png"))); //NON-NLS - btnClear.setToolTipText("Filter zur\u00fccksetzen"); //NON-NLS - panel3.add(btnClear, new CC().cell(0, 2, 2, 1).alignX("right").growX(0).width("32:32:32").height("32:32:32")); //NON-NLS - } - jPanelFilterExtern.add(panel3, new CC().cell(0, 0)); - - //======== panel2 ======== - { - panel2.setBorder(new TitledBorder("Downloads")); //NON-NLS - panel2.setLayout(new MigLayout( - new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS - // columns - new AC() - .fill().gap() - .fill(), - // rows - new AC() - .gap() - .fill())); - - //---- jLabel3 ---- - jLabel3.setText("gleichzeitig:"); //NON-NLS - panel2.add(jLabel3, new CC().cell(0, 0)); - panel2.add(jSpinnerAnzahlDownloads, new CC().cell(1, 0)); - - //---- lblBandwidth ---- - lblBandwidth.setText("max. Bandbreite:"); //NON-NLS - panel2.add(lblBandwidth, new CC().cell(0, 1)); - - //---- jLabel1 ---- - jLabel1.setText("KiB/s"); //NON-NLS - panel2.add(jLabel1, new CC().cell(2, 1)); - - //---- jSpinner1 ---- - jSpinner1.setModel(new SpinnerNumberModel(0, 0, 1048576, 1)); - jSpinner1.setToolTipText("\nBandbreitenbegrenzung eines Downloads in XX Kilobytes pro Sekunde.\n
WICHTIG:
ENTWEDER
den Wert \u00fcber die Pfeiltasten \u00e4ndern
ODER
Zahlen eingeben UND ENTER-Taste dr\u00fccken!
\n"); //NON-NLS - panel2.add(jSpinner1, new CC().cell(1, 1)); - } - jPanelFilterExtern.add(panel2, new CC().cell(0, 1)); - - //======== spDownload ======== - { - spDownload.setPreferredSize(new Dimension(14, 150)); - - //---- txtDownload ---- - txtDownload.setEditable(false); - txtDownload.setOpaque(false); - txtDownload.setPreferredSize(new Dimension(10, 500)); - spDownload.setViewportView(txtDownload); - } - jPanelFilterExtern.add(spDownload, new CC().cell(0, 2)); - } - jSplitPane1.setLeftComponent(jPanelFilterExtern); - - //======== downloadListArea ======== - { - downloadListArea.setLayout(new BorderLayout()); - downloadListArea.add(downloadListScrollPane, BorderLayout.CENTER); - downloadListArea.add(fxDescriptionPanel, BorderLayout.SOUTH); - } - jSplitPane1.setRightComponent(downloadListArea); - } - add(jSplitPane1, BorderLayout.CENTER); - add(toolBarPanel, BorderLayout.NORTH); - }//
//GEN-END:initComponents - - // Variables declaration - do not modify//GEN-BEGIN:variables - // Generated using JFormDesigner non-commercial license - private JSplitPane jSplitPane1; - private JPanel jPanelFilterExtern; - private JComboBox cbDisplayCategories; - private JComboBox cbView; - private JButton btnClear; - private JSpinner jSpinnerAnzahlDownloads; - private JSpinner jSpinner1; - private JEditorPane txtDownload; - private JScrollPane downloadListScrollPane; - private JFXPanel fxDescriptionPanel; - private JFXPanel toolBarPanel; - // End of variables declaration//GEN-END:variables } diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.jfd b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.jfd deleted file mode 100644 index b30569b8f3..0000000000 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.jfd +++ /dev/null @@ -1,177 +0,0 @@ -JFDML JFormDesigner: "7.0.2.0.298" Java: "11.0.7" encoding: "UTF-8" - -new FormModel { - contentType: "form/swing" - root: new FormRoot { - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { - name: "this" - add( new FormContainer( "javax.swing.JSplitPane", new FormLayoutManager( class javax.swing.JSplitPane ) ) { - name: "jSplitPane1" - "dividerLocation": 330 - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$layoutConstraints": "insets 0,hidemode 3,gap 0 0" - "$columnConstraints": "[grow,fill]" - "$rowConstraints": "[][fill][grow,fill]" - } ) { - name: "jPanelFilterExtern" - "preferredSize": new java.awt.Dimension( 200, 644 ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$columnConstraints": "[fill][grow,fill]" - "$rowConstraints": "[fill][fill][fill]" - "$layoutConstraints": "insets 5,hidemode 3,gap 5 5" - } ) { - name: "panel3" - "border": new javax.swing.border.TitledBorder( "Anzeige" ) - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - add( new FormComponent( "javax.swing.JLabel" ) { - name: "label1" - "text": "Typ:" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" - } ) - add( new FormComponent( "javax.swing.JComboBox" ) { - name: "cbDisplayCategories" - auxiliary() { - "JavaCodeGenerator.typeParameters": "String" - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 0" - } ) - add( new FormComponent( "javax.swing.JLabel" ) { - name: "label2" - "text": "Status:" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 1" - } ) - add( new FormComponent( "javax.swing.JComboBox" ) { - name: "cbView" - auxiliary() { - "JavaCodeGenerator.typeParameters": "String" - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 1" - } ) - add( new FormComponent( "javax.swing.JButton" ) { - name: "btnClear" - "icon": new com.jformdesigner.model.SwingIcon( 0, "/mediathek/res/muster/button-clear.png" ) - "toolTipText": "Filter zurücksetzen" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 2 2 1,alignx right,growx 0,width 32:32:32,height 32:32:32" - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" - } ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$columnConstraints": "[fill][fill]" - "$rowConstraints": "[][fill]" - "$layoutConstraints": "insets 5,hidemode 3,gap 5 5" - } ) { - name: "panel2" - "border": new javax.swing.border.TitledBorder( "Downloads" ) - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - add( new FormComponent( "javax.swing.JLabel" ) { - name: "jLabel3" - "text": "gleichzeitig:" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" - } ) - add( new FormComponent( "javax.swing.JSpinner" ) { - name: "jSpinnerAnzahlDownloads" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 0" - } ) - add( new FormComponent( "javax.swing.JLabel" ) { - name: "lblBandwidth" - "text": "max. Bandbreite:" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 1" - } ) - add( new FormComponent( "javax.swing.JLabel" ) { - name: "jLabel1" - "text": "KiB/s" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 2 1" - } ) - add( new FormComponent( "javax.swing.JSpinner" ) { - name: "jSpinner1" - "model": new javax.swing.SpinnerNumberModel( 0, 0, 1048576, 1 ) - "toolTipText": "\nBandbreitenbegrenzung eines Downloads in XX Kilobytes pro Sekunde.\n
WICHTIG:
ENTWEDER
den Wert über die Pfeiltasten ändern
ODER
Zahlen eingeben UND ENTER-Taste drücken!
\n" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 1" - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 1" - } ) - add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { - name: "spDownload" - "preferredSize": new java.awt.Dimension( 14, 150 ) - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - add( new FormComponent( "javax.swing.JEditorPane" ) { - name: "txtDownload" - "editable": false - "opaque": false - "preferredSize": new java.awt.Dimension( 10, 500 ) - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 2" - } ) - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "left" - } ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { - name: "downloadListArea" - auxiliary() { - "JavaCodeGenerator.variableName": "downloadListArea" - "JavaCodeGenerator.variableLocal": true - } - add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { - name: "downloadListScrollPane" - auxiliary() { - "JavaCodeGenerator.variableName": "downloadListScrollPane" - } - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "Center" - } ) - add( new FormComponent( "javafx.embed.swing.JFXPanel" ) { - name: "fxDescriptionPanel" - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "South" - } ) - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "right" - } ) - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "Center" - } ) - add( new FormComponent( "javafx.embed.swing.JFXPanel" ) { - name: "toolBarPanel" - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "North" - } ) - }, new FormLayoutConstraints( null ) { - "size": new java.awt.Dimension( 640, 640 ) - "location": new java.awt.Point( 0, 0 ) - } ) - } -} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/ManualDownloadsInfoLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/ManualDownloadsInfoLabel.java new file mode 100644 index 0000000000..8110359ecf --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/ManualDownloadsInfoLabel.java @@ -0,0 +1,18 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class ManualDownloadsInfoLabel extends JLabel { + public ManualDownloadsInfoLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der manuellen Downloads in der Downloadliste"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + String numDownloads = (info.num_downloads == 1) ? "1 Download" : info.num_downloads + " Downloads"; + setText(numDownloads); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/TotalDownloadsLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/TotalDownloadsLabel.java new file mode 100644 index 0000000000..9211ed9435 --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/TotalDownloadsLabel.java @@ -0,0 +1,24 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class TotalDownloadsLabel extends JLabel { + public TotalDownloadsLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Gesamtzahl aller Downloads"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + final int anz = info.total_num_download_list_entries; + final int diff = anz - info.total_starts; + String download = "Gesamtdownloads: " + anz; + if (diff >= 1) { + download += " (" + diff + " zurückgestellt)"; + } + setText(download); + } + +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/WaitingDownloadsInfoLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/WaitingDownloadsInfoLabel.java new file mode 100644 index 0000000000..2b43fc9efc --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/WaitingDownloadsInfoLabel.java @@ -0,0 +1,21 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class WaitingDownloadsInfoLabel extends JLabel { + public WaitingDownloadsInfoLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der wartenden Downloads"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + if (info.hasValues()) { + String waiting = (info.initialized == 1) ? "1 wartet" : info.initialized + " warten"; + setText(waiting); + } else + setText(""); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/DownloadSubtitleAction.java b/src/main/java/mediathek/gui/tabs/tab_film/DownloadSubtitleAction.java new file mode 100644 index 0000000000..5e6995dd6a --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_film/DownloadSubtitleAction.java @@ -0,0 +1,40 @@ +package mediathek.gui.tabs.tab_film; + +import mediathek.config.Konstanten; +import mediathek.mainwindow.MediathekGui; +import mediathek.tool.FileDialogs; +import mediathek.tool.MVSubtitle; +import mediathek.tool.SwingErrorDialog; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class DownloadSubtitleAction extends AbstractAction { + private final GuiFilme guiFilme; + + public DownloadSubtitleAction(GuiFilme guiFilme) { + this.guiFilme = guiFilme; + putValue(Action.NAME, "Untertitel-Datei sofort laden..."); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiFilme.getCurrentlySelectedFilm().ifPresent(film -> { + var selectedFile = FileDialogs.chooseSaveFileLocation(MediathekGui.ui(), "Untertitel speichern", ""); + if (selectedFile != null) { + try { + MVSubtitle subtitleFile = new MVSubtitle(); + subtitleFile.writeSubtitle(film.getSubtitleUrl(), selectedFile); + JOptionPane.showMessageDialog(MediathekGui.ui(), "Untertitel wurde erfolgreich geladen.", + Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE); + } catch (Exception ex) { + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), + "Untertitel konnte nicht geladen werden.", ex); + } + } + else + JOptionPane.showMessageDialog(MediathekGui.ui(), "Vorgang wurde abgebrochen.", + Konstanten.PROGRAMMNAME, JOptionPane.WARNING_MESSAGE); + }); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java new file mode 100644 index 0000000000..f0693d3a71 --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java @@ -0,0 +1,183 @@ +package mediathek.gui.tabs.tab_film; + +import com.formdev.flatlaf.util.ScaledImageIcon; +import mediathek.daten.DatenFilm; +import mediathek.gui.dialog.DialogFilmBeschreibung; +import mediathek.gui.tabs.AGuiTabPanel; +import mediathek.mainwindow.MediathekGui; +import mediathek.tool.GuiFunktionen; +import mediathek.tool.sender_icon_cache.MVSenderIconCache; +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; +import org.jdesktop.swingx.JXHyperlink; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.net.URI; + +public class FilmDescriptionPanel extends JPanel { + private final AGuiTabPanel currentTab; + private final JScrollPane scrollPane1 = new JScrollPane(); + private final JPopupMenu popupMenu = new JPopupMenu(); + private final SenderIconLabel lblIcon = new SenderIconLabel(); + private final JLabel lblThema = new JLabel(); + private final JLabel lblTitel = new JLabel(); + private final JTextArea textArea = new JTextArea(); + private final JXHyperlink hyperlink = new JXHyperlink(); + private DatenFilm currentFilm; + + public FilmDescriptionPanel(@NotNull AGuiTabPanel currentTab) { + this.currentTab = currentTab; + + initComponents(); + + createPopupMenu(); + + setAllFieldsEmpty(); + } + + private void createPopupMenu() { + var item = new JMenuItem("Beschreibung ändern..."); + item.addActionListener(l -> { + DialogFilmBeschreibung dialog = new DialogFilmBeschreibung(MediathekGui.ui(), currentFilm); + dialog.setVisible(true); + }); + popupMenu.add(item); + popupMenu.addSeparator(); + + item = new JMenuItem("Beschreibung in Zwischenablage kopieren"); + item.addActionListener(l -> GuiFunktionen.copyToClipboard(currentFilm.getDescription())); + popupMenu.add(item); + + item = new JMenuItem("Filmbasisinformationen in Zwischenablage kopieren"); + item.addActionListener(l -> { + String sb = currentFilm.getSender() + + " - " + + currentFilm.getThema() + + " - " + + currentFilm.getTitle(); + + GuiFunktionen.copyToClipboard(sb); + }); + popupMenu.add(item); + + setComponentPopupMenu(popupMenu); + textArea.setComponentPopupMenu(popupMenu); + } + + private void initComponents() { + setLayout(new MigLayout( + new LC().hideMode(3), + new AC() + .fill().gap() + .grow().fill(), + new AC() + .gap() + .gap() + .gap() + )); + + lblIcon.setPreferredSize(new Dimension(96, 96)); + lblIcon.setVerticalAlignment(SwingConstants.TOP); + add(lblIcon, new CC().cell(0, 0, 1, 3).alignX("center").alignY("top").grow(0, 0)); //NON-NLS + + lblThema.setFont(lblThema.getFont().deriveFont(lblThema.getFont().getStyle() | Font.BOLD)); + add(lblThema, new CC().cell(1, 0)); + + lblTitel.setFont(lblTitel.getFont().deriveFont(lblTitel.getFont().getStyle() | Font.BOLD)); + add(lblTitel, new CC().cell(1, 1)); + + scrollPane1.setPreferredSize(new Dimension(299, 75)); + scrollPane1.setMaximumSize(new Dimension(Integer.MAX_VALUE, 75)); + scrollPane1.setMinimumSize(new Dimension(23, 75)); + scrollPane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + textArea.setEditable(false); + textArea.setWrapStyleWord(true); + textArea.setLineWrap(true); + scrollPane1.setViewportView(textArea); + add(scrollPane1, new CC().cell(1, 2).grow()); + + //add(hyperlink, new CC().cell(0, 3, 2, 1)); + add(hyperlink, new CC().cell(1, 3)); + } + + public void install(@NotNull JTabbedPane tabbedPane, @NotNull JTable tabelle) { + tabbedPane.add("Beschreibung", this); + + tabelle.getSelectionModel().addListSelectionListener(e -> + currentTab.getCurrentlySelectedFilm().ifPresentOrElse(film -> { + showFilmDescription(film); + currentFilm = film; + }, () -> { + setAllFieldsEmpty(); + currentFilm = null; + })); + } + + private void setAllFieldsEmpty() { + hyperlink.setVisible(false); + hyperlink.setText(""); + hyperlink.setToolTipText(""); + + textArea.setText(""); + lblIcon.setIcon(null); + lblThema.setText(""); + lblTitel.setText(""); + } + + private void showFilmDescription(@NotNull DatenFilm film) { + lblThema.setText(film.getThema()); + lblTitel.setText(film.getTitle()); + + hyperlink.setVisible(true); + try { + hyperlink.setURI(new URI(film.getWebsiteUrl())); + hyperlink.setText("Link zur Webseite"); + hyperlink.setClicked(false); + } catch (Exception e) { + //logger + hyperlink.setText("Link nicht verfügbar"); + } + hyperlink.setToolTipText(film.getWebsiteUrl()); + + textArea.setText(film.getDescription()); + SwingUtilities.invokeLater(() -> scrollPane1.getVerticalScrollBar().setValue(0)); + lblIcon.setSender(film.getSender()); + } + + static class SenderIconLabel extends JLabel { + private static final Dimension ICON_DIMENSION = new Dimension(96, 96); + + public SenderIconLabel() { + setText(""); + setIcon(null); + } + + private void sizeToIcon(@NotNull Icon icon) { + int height = icon.getIconHeight(); + int width = icon.getIconWidth(); + + Dimension d = new Dimension(width, height); + setPreferredSize(d); + } + + public void setSender(@Nullable String sender) { + if (sender == null) { + setIcon(null); + } else { + MVSenderIconCache.get(sender).ifPresentOrElse(icon -> { + var imageDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); + var destDim = GuiFunktionen.calculateFittedDimension(imageDim, ICON_DIMENSION); + var origIcon = new ScaledImageIcon(icon, destDim.width, destDim.height); + setIcon(origIcon); + sizeToIcon(origIcon); + }, () -> setIcon(null)); + } + } + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index bc62210fcf..4241dad0d7 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -1,20 +1,21 @@ package mediathek.gui.tabs.tab_film; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; +import com.formdev.flatlaf.icons.FlatSearchWithHistoryIcon; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import javafx.animation.PauseTransition; import javafx.application.Platform; import javafx.beans.value.ChangeListener; -import javafx.embed.swing.JFXPanel; -import javafx.scene.control.Alert; -import javafx.scene.control.ButtonBar; -import javafx.scene.control.ButtonType; import javafx.util.Duration; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.Konstanten; +import mediathek.config.MVColor; import mediathek.config.MVConfig; import mediathek.controller.history.SeenHistoryController; import mediathek.controller.starter.Start; @@ -23,23 +24,21 @@ import mediathek.daten.blacklist.BlacklistRule; import mediathek.filmeSuchen.ListenerFilmeLaden; import mediathek.filmeSuchen.ListenerFilmeLadenEvent; -import mediathek.gui.TabPaneIndex; -import mediathek.gui.actions.ShowBlacklistDialogAction; -import mediathek.gui.actions.ShowFilmInformationAction; +import mediathek.gui.FilterSelectionComboBoxModel; +import mediathek.gui.actions.PlayFilmAction; import mediathek.gui.actions.UrlHyperlinkAction; import mediathek.gui.dialog.DialogAboNoSet; import mediathek.gui.dialog.DialogAddDownload; import mediathek.gui.dialog.DialogAddMoreDownload; -import mediathek.gui.dialog.DialogEditAbo; import mediathek.gui.messages.*; import mediathek.gui.messages.history.DownloadHistoryChangedEvent; import mediathek.gui.tabs.AGuiTabPanel; +import mediathek.gui.tabs.tab_film.helpers.GuiFilmeModelHelper; +import mediathek.gui.tabs.tab_film.helpers.GuiModelHelper; +import mediathek.gui.tabs.tab_film.helpers.LuceneGuiFilmeModelHelper; import mediathek.javafx.bookmark.BookmarkWindowController; -import mediathek.javafx.buttonsPanel.ButtonsPanelController; -import mediathek.javafx.descriptionPanel.DescriptionPanelController; -import mediathek.javafx.filmtab.FilmTabInfoPane; import mediathek.javafx.filterpanel.FilmActionPanel; -import mediathek.javafx.tool.JavaFxUtils; +import mediathek.javafx.filterpanel.SearchControlFieldMode; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; import mediathek.tool.cellrenderer.CellRendererFilme; @@ -53,12 +52,18 @@ import org.apache.logging.log4j.Logger; import org.jdesktop.swingx.VerticalLayout; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import javax.swing.table.TableModel; +import javax.swing.text.JTextComponent; import java.awt.*; import java.awt.event.*; import java.awt.print.PrinterException; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.net.URLEncoder; @@ -67,6 +72,8 @@ import java.util.EnumSet; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.IntConsumer; public class GuiFilme extends AGuiTabPanel { @@ -81,65 +88,67 @@ public class GuiFilme extends AGuiTabPanel { private static final String ACTION_MAP_KEY_MARK_UNSEEN = "unseen"; private static final int[] HIDDEN_COLUMNS = {DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN}; - private static final Logger logger = LogManager.getLogger(GuiFilme.class); + private static final Logger logger = LogManager.getLogger(); private static final int[] BUTTON_COLUMNS = {DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN}; public static boolean[] VISIBLE_COLUMNS = new boolean[DatenFilm.MAX_ELEM]; - public final PlayFilmAction playAction = new PlayFilmAction(); + public final PlayFilmAction playFilmAction = new PlayFilmAction(this); public final SaveFilmAction saveFilmAction = new SaveFilmAction(); public final BookmarkFilmAction bookmarkFilmAction = new BookmarkFilmAction(); - public final BookmarkManageListAction bookmarkManageListAction = new BookmarkManageListAction(); + protected final JTabbedPane psetButtonsTab = new JTabbedPane(); + private final PauseTransition reloadTableDataTransition = new PauseTransition(Duration.millis(250d)); private final MarkFilmAsSeenAction markFilmAsSeenAction = new MarkFilmAsSeenAction(); private final MarkFilmAsUnseenAction markFilmAsUnseenAction = new MarkFilmAsUnseenAction(); private final JScrollPane filmListScrollPane = new JScrollPane(); private final JPanel extensionArea = new JPanel(); private final JCheckBoxMenuItem cbkShowDescription = new JCheckBoxMenuItem("Beschreibung anzeigen"); - private final JFXPanel fxDescriptionPanel = new JFXPanel(); - private final JFXPanel fxPsetButtonsPanel = new JFXPanel(); private final SeenHistoryController historyController = new SeenHistoryController(); + private final JToolBar toolBar = new JToolBar(); + private final JCheckBoxMenuItem cbShowButtons = new JCheckBoxMenuItem("Buttons anzeigen"); + private final PauseTransition zeitraumTransition = new PauseTransition(Duration.millis(250)); /** * The JavaFx Film action popup panel. */ public FilmActionPanel filmActionPanel; + public ToggleFilterDialogVisibilityAction toggleFilterDialogVisibilityAction = new ToggleFilterDialogVisibilityAction(); + protected SearchField searchField; + protected JComboBox filterSelectionComboBox = new JComboBox<>(new FilterSelectionComboBoxModel()); + protected FilterVisibilityToggleButton btnToggleFilterDialogVisibility = new FilterVisibilityToggleButton(toggleFilterDialogVisibilityAction); + protected PsetButtonsPanel psetButtonsPanel; private Optional bookmarkWindowController = Optional.empty(); - /** - * The swing helper panel FilmAction bar. - */ - private JFXPanel fxFilmActionPanel; private boolean stopBeob; - private FilmTabInfoPane filmInfoLabel; - private JCheckBoxMenuItem cbShowButtons; + private MVFilmTable tabelle; /** - * We need a strong reference here for message bus to work properly. Otherwise the buttons - * panel controller will not receive change messages. + * We perform model filtering in the background the keep UI thread alive. */ - private ButtonsPanelController psetController; - private MVFilmTable tabelle; + private ListenableFuture modelFuture; public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { super(); daten = aDaten; this.mediathekGui = mediathekGui; + descriptionPanel = new FilmDescriptionPanel(this); + + if (daten.getListeFilmeNachBlackList() instanceof IndexedFilmList) + searchField = new LuceneSearchField(); + else + searchField = new RegularSearchField(); setLayout(new BorderLayout()); - createFilmListArea(); + add(filmListScrollPane, BorderLayout.CENTER); createExtensionArea(); - createFilmActionPanel(); + createToolBar(); // add film description panel - extensionArea.add(fxDescriptionPanel); - extensionArea.add(fxPsetButtonsPanel); + extensionArea.add(descriptionTab); + extensionArea.add(psetButtonsTab); setupFilmListTable(); - - installTabInfoStatusBarControl(); - - setupFilmSelectionPropertyListener(mediathekGui); - - setupDescriptionPanel(); - setupPsetButtonsPanel(); + setupFilmSelectionPropertyListener(); + setupDescriptionTab(tabelle, cbkShowDescription, ApplicationConfiguration.FILM_SHOW_DESCRIPTION); + setupPsetButtonsTab(); setupFilmActionPanel(); start_init(); @@ -149,6 +158,49 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { setupActionListeners(); } + private void createToolBar() { + add(toolBar, BorderLayout.NORTH); + + toolBar.setFloatable(true); + toolBar.setName("Filme"); + + toolBar.add(playFilmAction); + toolBar.add(saveFilmAction); + toolBar.add(bookmarkFilmAction); + toolBar.addSeparator(); + filterSelectionComboBox.setMaximumSize(new Dimension(150, 100)); + toolBar.add(filterSelectionComboBox); + toolBar.addSeparator(); + toolBar.add(new JLabel("Suche:")); + toolBar.add(searchField); + toolBar.addSeparator(); + + toolBar.add(btnToggleFilterDialogVisibility); + } + + @Handler + public void handleTableModelChange(TableModelChangeEvent e) { + if (e.active) { + SwingUtilities.invokeLater(() -> { + playFilmAction.setEnabled(false); + saveFilmAction.setEnabled(false); + bookmarkFilmAction.setEnabled(false); + toggleFilterDialogVisibilityAction.setEnabled(false); + searchField.setEnabled(false); + filterSelectionComboBox.setEnabled(false); + }); + } else { + SwingUtilities.invokeLater(() -> { + playFilmAction.setEnabled(true); + saveFilmAction.setEnabled(true); + bookmarkFilmAction.setEnabled(true); + toggleFilterDialogVisibilityAction.setEnabled(true); + searchField.setEnabled(true); + filterSelectionComboBox.setEnabled(true); + }); + } + } + @Override public void tabelleSpeichern() { if (tabelle != null) { @@ -161,127 +213,52 @@ private void setupFilmListTable() { filmListScrollPane.setViewportView(tabelle); } - private void createFilmActionPanel() { - fxFilmActionPanel = new JFXPanel(); - add(fxFilmActionPanel, BorderLayout.NORTH); - } - /** * Update the property with the current number of selected entries from the JTable. */ - private void setupFilmSelectionPropertyListener(MediathekGui mediathekGui) { + private void setupFilmSelectionPropertyListener() { tabelle.getSelectionModel().addListSelectionListener(e -> { if (!e.getValueIsAdjusting()) { - final int sel = tabelle.getSelectedRowCount(); - Platform.runLater(() -> mediathekGui.getSelectedItemsProperty().setValue(sel)); + updateSelectedListItemsCount(tabelle); } }); addComponentListener(new ComponentAdapter() { @Override public void componentShown(ComponentEvent e) { - final int sel = tabelle.getSelectedRowCount(); - Platform.runLater(() -> mediathekGui.getSelectedItemsProperty().setValue(sel)); + updateSelectedListItemsCount(tabelle); onComponentShown(); } }); } - @Override - protected void installTabInfoStatusBarControl() { - final var leftItems = mediathekGui.getStatusBarController().getStatusBar().getLeftItems(); - - Platform.runLater(() -> { - filmInfoLabel = new FilmTabInfoPane(daten, this); - if (isVisible()) leftItems.add(filmInfoLabel); - }); - - addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - Platform.runLater( - () -> { - filmInfoLabel.setVisible(true); - leftItems.add(filmInfoLabel); - }); - } - - @Override - public void componentHidden(ComponentEvent e) { - Platform.runLater( - () -> { - filmInfoLabel.setVisible(false); - leftItems.remove(filmInfoLabel); - }); - } - }); - } - - private void createFilmListArea() { - add(filmListScrollPane, BorderLayout.CENTER); - } - private void createExtensionArea() { extensionArea.setLayout(new VerticalLayout()); add(extensionArea, BorderLayout.SOUTH); } - @Handler - private void handleButtonsPanelVisibilityChanged(ButtonsPanelVisibilityChangedEvent evt) { - SwingUtilities.invokeLater(() -> cbShowButtons.setSelected(evt.visible)); - SwingUtilities.invokeLater(() -> fxPsetButtonsPanel.setVisible(evt.visible)); - } - public void installViewMenuEntry(JMenu jMenuAnsicht) { - cbShowButtons = new JCheckBoxMenuItem("Buttons anzeigen"); - if (!SystemUtils.IS_OS_MAC_OSX) - cbShowButtons.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0)); - cbShowButtons.setSelected(ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, false)); - cbShowButtons.addActionListener(e -> MessageBus.getMessageBus().publishAsync(new ButtonsPanelVisibilityChangedEvent(cbShowButtons.isSelected()))); - jMenuAnsicht.add(cbShowButtons, 0); } - @Override - public void installMenuEntries(JMenu menu) { - JMenuItem miPlayFilm = new JMenuItem("Film abspielen"); - if (SystemUtils.IS_OS_MAC_OSX) - miPlayFilm.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F6, InputEvent.META_DOWN_MASK)); - else miPlayFilm.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK)); - miPlayFilm.setIcon(IconFontSwing.buildIcon(FontAwesome.PLAY, 16)); - miPlayFilm.addActionListener(playAction); - - JMenuItem miRecordFilm = new JMenuItem("Film aufzeichnen"); - if (SystemUtils.IS_OS_MAC_OSX) - miRecordFilm.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F7, InputEvent.META_DOWN_MASK)); - else - miRecordFilm.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK)); - miRecordFilm.setIcon(IconFontSwing.buildIcon(FontAwesome.DOWNLOAD, 16)); - miRecordFilm.addActionListener(saveFilmAction); - - JMenuItem miBookmarkFilm = new JMenuItem("Film merken"); - if (SystemUtils.IS_OS_MAC_OSX) { - miBookmarkFilm.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F8, InputEvent.META_DOWN_MASK)); + /** + * Show description panel based on settings. + */ + protected void makeButtonsTabVisible(boolean visible) { + if (visible) { + if (psetButtonsTab.indexOfComponent(psetButtonsPanel) == -1) { + psetButtonsTab.add(psetButtonsPanel, 0); + psetButtonsTab.setTitleAt(0, "Buttons"); + } } else { - miBookmarkFilm.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK)); + if (psetButtonsTab.indexOfComponent(psetButtonsPanel) != -1) { + psetButtonsTab.remove(psetButtonsPanel); + } } - miBookmarkFilm.setIcon(IconFontSwing.buildIcon(FontAwesome.BOOKMARK_O, 16)); - miBookmarkFilm.addActionListener(bookmarkFilmAction); - - JMenuItem miOpenBlacklist = new JMenuItem("Blacklist öffnen"); - if (SystemUtils.IS_OS_MAC_OSX) - miOpenBlacklist.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F9, InputEvent.META_DOWN_MASK)); - else - miOpenBlacklist.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK)); - miOpenBlacklist.setAction(new ShowBlacklistDialogAction(mediathekGui, daten)); - - if (!SystemUtils.IS_OS_MAC_OSX) - cbkShowDescription.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F10, 0)); + } + @Override + public void installMenuEntries(JMenu menu) { JMenuItem miMarkFilmAsSeen = new JMenuItem("Filme als gesehen markieren"); miMarkFilmAsSeen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.CTRL_DOWN_MASK)); miMarkFilmAsSeen.addActionListener(markFilmAsSeenAction); @@ -291,82 +268,51 @@ public void installMenuEntries(JMenu menu) { KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK)); miMarkFilmAsUnseen.addActionListener(markFilmAsUnseenAction); - menu.add(miPlayFilm); - menu.add(miRecordFilm); - menu.add(miBookmarkFilm); + menu.add(playFilmAction); + menu.add(saveFilmAction); + menu.add(bookmarkFilmAction); menu.addSeparator(); menu.add(miMarkFilmAsSeen); menu.add(miMarkFilmAsUnseen); menu.addSeparator(); - menu.add(miOpenBlacklist); + menu.add(mediathekGui.toggleBlacklistAction); + menu.add(mediathekGui.editBlacklistAction); menu.addSeparator(); menu.add(cbkShowDescription); } private void setupFilmActionPanel() { - filmActionPanel = new FilmActionPanel(reloadTableDataTransition); - JavaFxUtils.invokeInFxThreadAndWait( - () -> fxFilmActionPanel.setScene(filmActionPanel.getFilmActionPanelScene())); + filmActionPanel = new FilmActionPanel(btnToggleFilterDialogVisibility); } - private void setupPsetButtonsPanel() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - try { - psetController = ButtonsPanelController.install(fxPsetButtonsPanel,this); - psetController.setOnCloseRequest(e -> { - MessageBus.getMessageBus().publishAsync(new ButtonsPanelVisibilityChangedEvent(false)); - e.consume(); - }); - psetController.setupButtonLayout(); - } catch (Exception ex) { - logger.error("setupPsetButtonsPanel", ex); - } - }); - - var config = ApplicationConfiguration.getConfiguration(); - SwingUtilities.invokeLater(() -> fxPsetButtonsPanel.addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - config.setProperty(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, true); - } + private void setupPsetButtonsTab() { + var initialVisibility = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, false); + setupButtonsMenuItem(initialVisibility); - @Override - public void componentHidden(ComponentEvent e) { - config.setProperty(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, false); - } - })); + psetButtonsPanel = new PsetButtonsPanel(this); + psetButtonsPanel.putClientProperty("JTabbedPane.tabClosable", true); + psetButtonsPanel.putClientProperty("JTabbedPane.tabCloseCallback", (IntConsumer) tabIndex -> cbShowButtons.doClick()); + psetButtonsPanel.install(psetButtonsTab); - final var visible = config.getBoolean(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, false); - fxPsetButtonsPanel.setVisible(visible); + makeButtonsTabVisible(initialVisibility); } - private void setupDescriptionPanel() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - try { - var descriptionPanelController = DescriptionPanelController.install(fxDescriptionPanel); - SwingUtilities.invokeLater(() -> tabelle.getSelectionModel().addListSelectionListener(e -> { - Optional optFilm = getCurrentlySelectedFilm(); - Platform.runLater(() -> descriptionPanelController.showFilmDescription(optFilm)); - })); - } catch (Exception ex) { - logger.error("setupDescriptionPanel", ex); - } - }); - } + private void setupButtonsMenuItem(boolean initialVisibility) { + var config = ApplicationConfiguration.getConfiguration(); - /** - * Show description panel based on settings. - */ - private void showDescriptionPanel() { - fxDescriptionPanel.setVisible(ApplicationConfiguration.getConfiguration() - .getBoolean(ApplicationConfiguration.FILM_SHOW_DESCRIPTION, true)); + if (!SystemUtils.IS_OS_MAC_OSX) + cbShowButtons.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0)); + cbShowButtons.setSelected(initialVisibility); + cbShowButtons.addActionListener(l -> { + boolean visible = cbShowButtons.isSelected(); + makeButtonsTabVisible(visible); + config.setProperty(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, visible); + }); } private void onComponentShown() { - mediathekGui.tabPaneIndexProperty().setValue(TabPaneIndex.FILME); - updateFilmData(); - setInfoStatusbar(); + updateStartInfoProperty(); } public int getTableRowCount() { @@ -391,7 +337,7 @@ private void setupKeyMapping() { focusedWindowMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_U, 0), ACTION_MAP_KEY_MARK_UNSEEN); final ActionMap actionMap = tabelle.getActionMap(); - actionMap.put(ACTION_MAP_KEY_PLAY_FILM, playAction); + actionMap.put(ACTION_MAP_KEY_PLAY_FILM, playFilmAction); actionMap.put(ACTION_MAP_KEY_SAVE_FILM, saveFilmAction); actionMap.put(ACTION_MAP_KEY_BOOKMARK_FILM, bookmarkFilmAction); actionMap.put(ACTION_MAP_KEY_COPY_NORMAL_URL, new CopyUrlToClipboardAction(FilmResolution.Enum.NORMAL)); @@ -402,18 +348,17 @@ private void setupKeyMapping() { } private void setupCellRenderer() { - final CellRendererFilme cellRenderer = new CellRendererFilme(); + CellRendererFilme cellRenderer = new CellRendererFilme(); tabelle.setDefaultRenderer(Object.class, cellRenderer); tabelle.setDefaultRenderer(DatumFilm.class, cellRenderer); tabelle.setDefaultRenderer(Integer.class, cellRenderer); } private void start_init() { - showDescriptionPanel(); daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { @Override public void fertig(ListenerFilmeLadenEvent event) { - Platform.runLater(() -> filmActionPanel.updateThemaBox()); + Platform.runLater(() -> filmActionPanel.updateThemaComboBox()); } }); @@ -450,15 +395,14 @@ private void setupHeaderPopupMenu() { true, MVConfig.Configs.SYSTEM_TAB_FILME_LINEBREAK); - headerListener.setFontSizeChangeCapable(SystemUtils.IS_OS_LINUX); tabelle.getTableHeader().addMouseListener(headerListener); } @Handler private void handleDownloadHistoryChangedEvent(DownloadHistoryChangedEvent e) { SwingUtilities.invokeLater(() -> { - if (filmActionPanel.showUnseenOnly.getValue()) { - Platform.runLater(() -> reloadTableDataTransition.playFromStart()); + if (filmActionPanel.isShowUnseenOnly()) { + Platform.runLater(reloadTableDataTransition::playFromStart); } else { tabelle.fireTableDataChanged(true); } @@ -469,23 +413,23 @@ private void handleDownloadHistoryChangedEvent(DownloadHistoryChangedEvent e) { private void handleButtonStart(ButtonStartEvent e) { SwingUtilities.invokeLater(() -> { tabelle.fireTableDataChanged(true); - setInfoStatusbar(); + updateStartInfoProperty(); }); } @Handler private void handleAboListChanged(AboListChangedEvent e) { - Platform.runLater(() -> reloadTableDataTransition.playFromStart()); + Platform.runLater(reloadTableDataTransition::playFromStart); } @Handler private void handleBlacklistChangedEvent(BlacklistChangedEvent e) { - Platform.runLater(() -> reloadTableDataTransition.playFromStart()); + Platform.runLater(reloadTableDataTransition::playFromStart); } @Handler private void handleStartEvent(StartEvent msg) { - SwingUtilities.invokeLater(this::setInfoStatusbar); + SwingUtilities.invokeLater(this::updateStartInfoProperty); } private synchronized void saveFilm(DatenPset pSet) { @@ -558,41 +502,26 @@ private void saveFilm(DatenFilm datenFilm, DatenPset pSet) { "Ohne Programm-Sets können keine Downloads gestartet werden.", Konstanten.PROGRAMMNAME, JOptionPane.ERROR_MESSAGE); } else { - //FIXME remove for production!!! - /*SaveDownloadDialog dlg = new SaveDownloadDialog(datenFilm, pSet); - dlg.setVisible(true); - if (dlg.controller.success()) - System.out.println("SUCCESS"); - else - System.out.println("NO SUCCESS");*/ // dann alle Downloads im Dialog abfragen Optional res = - filmActionPanel.showOnlyHd.getValue() ? Optional.of(FilmResolution.Enum.HIGH_QUALITY) : Optional.empty(); + filmActionPanel.isShowOnlyHighQuality() ? Optional.of(FilmResolution.Enum.HIGH_QUALITY) : Optional.empty(); DialogAddDownload dialog = new DialogAddDownload(mediathekGui, datenFilm, pSet, res); dialog.setVisible(true); } } - private synchronized void bookmarkFilm() { + private void bookmarkFilm() { var movies = getSelFilme(); final long size = movies.size(); if (size > 250) { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - ButtonType yes = new ButtonType("Ja", ButtonBar.ButtonData.YES); - ButtonType no = new ButtonType("Nein", ButtonBar.ButtonData.NO); - Alert alert = new Alert(Alert.AlertType.WARNING, - String.format( - "Möchten Sie wirklich %d Einträge bearbeiten?%nDas Programm könnte während der Operation nicht reagieren.", - size), - yes, - no); - alert.setTitle("Merkliste"); - alert.showAndWait().filter(r -> r == yes).ifPresent(response -> - SwingUtilities.invokeLater(() -> { - daten.getListeBookmarkList().checkAndBookmarkMovies(movies); - repaint(); - })); - }); + var reply = JOptionPane.showConfirmDialog(this, + String.format("Möchten Sie wirklich %d Einträge der Merkliste bearbeiten?%nDas Programm könnte während der Operation nicht reagieren.", size), + Konstanten.PROGRAMMNAME, + JOptionPane.YES_NO_OPTION); + if (reply == JOptionPane.YES_OPTION) { + daten.getListeBookmarkList().checkAndBookmarkMovies(movies); + repaint(); + } } else { daten.getListeBookmarkList().checkAndBookmarkMovies(movies); repaint(); @@ -613,7 +542,7 @@ public void showBookmarkWindow() { public void playerStarten(DatenPset pSet) { // Url mit Prognr. starten if (tabelle.getSelectedRow() == -1) { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } else if (pSet.istSpeichern()) { // wenn das pSet zum Speichern (über die Button) gewählt wurde, // weiter mit dem Dialog "Speichern" @@ -621,7 +550,7 @@ public void playerStarten(DatenPset pSet) { } else { // mit dem flvstreamer immer nur einen Filme starten final String aufloesung; - if (filmActionPanel.showOnlyHd.getValue()) { + if (filmActionPanel.isShowOnlyHighQuality()) { aufloesung = FilmResolution.Enum.HIGH_QUALITY.toString(); } else aufloesung = ""; @@ -655,11 +584,16 @@ private Optional getFilm(final int zeileTabelle) { } @Override - protected Optional getCurrentlySelectedFilm() { + public Optional getCurrentlySelectedFilm() { final int selectedTableRow = tabelle.getSelectedRow(); if (selectedTableRow != -1) { - final int modelIndex = tabelle.convertRowIndexToModel(selectedTableRow); - return Optional.of((DatenFilm) tabelle.getModel().getValueAt(modelIndex, DatenFilm.FILM_REF)); + try { + final int modelIndex = tabelle.convertRowIndexToModel(selectedTableRow); + return Optional.of((DatenFilm) tabelle.getModel().getValueAt(modelIndex, DatenFilm.FILM_REF)); + } + catch (Exception e) { + return Optional.empty(); + } } else { return Optional.empty(); } @@ -676,7 +610,7 @@ protected List getSelFilme() { arrayFilme.add(datenFilm); } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } return arrayFilme; } @@ -688,108 +622,80 @@ private void updateFilmData() { var infoDialog = mediathekGui.getFilmInfoDialog(); if (infoDialog != null) { final Optional filmSelection = getCurrentlySelectedFilm(); - filmSelection.ifPresent(mediathekGui.getFilmInfoDialog()::updateCurrentFilm); + filmSelection.ifPresent(infoDialog::updateCurrentFilm); } } - private void setInfoStatusbar() { - MessageBus.getMessageBus().publishAsync(new UpdateStatusBarLeftDisplayEvent()); - } + private void setupDataTransitions() { + //execute on JavaFX thread! + reloadTableDataTransition.setOnFinished(e -> { + try { + SwingUtilities.invokeAndWait(this::loadTable); + } catch (InterruptedException | InvocationTargetException ex) { + ex.printStackTrace(); + logger.error("Table reload failed", ex); + } + }); - protected PauseTransition reloadTableDataTransition = new PauseTransition(Duration.millis(250d)); + zeitraumTransition.setOnFinished(evt -> { + // reset sender filter first + filmActionPanel.getViewSettingsPane().senderCheckList.getCheckModel().clearChecks(); + try { + SwingUtilities.invokeAndWait(() -> daten.getListeBlacklist().filterListe()); + } catch (InterruptedException | InvocationTargetException e) { + logger.error("Failed to filter list", e); + } + reloadTableDataTransition.playFromStart(); + }); + } private void setupActionListeners() { Platform.runLater(() -> { - reloadTableDataTransition.setOnFinished(e -> { - try { - SwingUtilities.invokeAndWait(this::loadTable); - } catch (InterruptedException | InvocationTargetException ex) { - ex.printStackTrace(); - logger.error("Table reload failed", ex); - } - }); + setupDataTransitions(); + final ChangeListener reloadTableListener = (ob, ov, nv) -> reloadTableDataTransition.playFromStart(); final ChangeListener reloadTableListener2 = (ob, ov, newValue) -> { if (!newValue) { reloadTableDataTransition.playFromStart(); } }; - filmActionPanel.showOnlyHd.addListener(reloadTableListener); - filmActionPanel.showSubtitlesOnly.addListener(reloadTableListener); - filmActionPanel.showNewOnly.addListener(reloadTableListener); - filmActionPanel.showBookMarkedOnly.addListener(reloadTableListener); - filmActionPanel.showUnseenOnly.addListener(reloadTableListener); - filmActionPanel.dontShowAbos.addListener(reloadTableListener); - filmActionPanel.dontShowTrailers.addListener(reloadTableListener); - filmActionPanel.dontShowSignLanguage.addListener(reloadTableListener); - filmActionPanel.dontShowAudioVersions.addListener(reloadTableListener); - filmActionPanel.showLivestreamsOnly.addListener(reloadTableListener); - filmActionPanel.filmLengthSlider.lowValueChangingProperty().addListener(reloadTableListener2); - filmActionPanel.filmLengthSlider.highValueChangingProperty().addListener(reloadTableListener2); - filmActionPanel.searchThroughDescription.addListener((os, o, n) -> { - if (!filmActionPanel.roSearchStringProperty.getReadOnlyProperty().isEmpty().get()) - reloadTableDataTransition.playFromStart(); - }); - - setupZeitraumListener(); - - filmActionPanel.themaBox.setOnAction(evt -> { - if (!filmActionPanel.themaBox.getItems().isEmpty()) { + filmActionPanel.showOnlyHighQualityProperty().addListener(reloadTableListener); + filmActionPanel.showSubtitlesOnlyProperty().addListener(reloadTableListener); + filmActionPanel.showNewOnlyProperty().addListener(reloadTableListener); + filmActionPanel.showBookMarkedOnlyProperty().addListener(reloadTableListener); + filmActionPanel.showUnseenOnlyProperty().addListener(reloadTableListener); + filmActionPanel.dontShowAbosProperty().addListener(reloadTableListener); + filmActionPanel.dontShowTrailersProperty().addListener(reloadTableListener); + filmActionPanel.dontShowSignLanguageProperty().addListener(reloadTableListener); + filmActionPanel.dontShowAudioVersionsProperty().addListener(reloadTableListener); + filmActionPanel.showLivestreamsOnlyProperty().addListener(reloadTableListener); + var filmLengthSlider = filmActionPanel.getFilmLengthSlider(); + filmLengthSlider.lowValueChangingProperty().addListener(reloadTableListener2); + filmLengthSlider.highValueChangingProperty().addListener(reloadTableListener2); + + filmActionPanel.zeitraumProperty().addListener((observable, oldValue, newValue) -> zeitraumTransition.playFromStart()); + + filmActionPanel.getViewSettingsPane().themaComboBox.setOnAction(evt -> { + if (!filmActionPanel.getViewSettingsPane().themaComboBox.getItems().isEmpty()) { reloadTableDataTransition.playFromStart(); } }); }); - - setupShowFilmDescriptionMenuItem(); } - /** - * Setup and show film description panel. Most of the setup is done in {@link GuiFilme} function. - * Here we just display the panel - */ - private void setupShowFilmDescriptionMenuItem() { - cbkShowDescription.setSelected( - ApplicationConfiguration.getConfiguration() - .getBoolean(ApplicationConfiguration.FILM_SHOW_DESCRIPTION, true)); - cbkShowDescription.addActionListener( - l -> fxDescriptionPanel.setVisible(cbkShowDescription.isSelected())); - cbkShowDescription.addItemListener(e -> ApplicationConfiguration.getConfiguration().setProperty( - ApplicationConfiguration.FILM_SHOW_DESCRIPTION, - cbkShowDescription.isSelected())); - fxDescriptionPanel.addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - cbkShowDescription.setSelected(true); - } - - @Override - public void componentHidden(ComponentEvent e) { - cbkShowDescription.setSelected(false); - } - }); - } + @Override + protected void setupShowFilmDescriptionMenuItem() { + var config = ApplicationConfiguration.getConfiguration(); - private void setupZeitraumListener() { - PauseTransition trans = new PauseTransition(Duration.millis(250)); - trans.setOnFinished(evt -> { - // reset sender filter first - filmActionPanel.getViewSettingsPane().senderCheckList.getCheckModel().clearChecks(); - try { - SwingUtilities.invokeAndWait(() -> daten.getListeBlacklist().filterListe()); - } catch (InterruptedException | InvocationTargetException e) { - e.printStackTrace(); - logger.error("Failed to filter list", e); - } - reloadTableDataTransition.playFromStart(); + cbkShowDescription.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F10, 0)); + cbkShowDescription.setSelected(config.getBoolean(ApplicationConfiguration.FILM_SHOW_DESCRIPTION, true)); + cbkShowDescription.addActionListener(l -> { + boolean visible = cbkShowDescription.isSelected(); + makeDescriptionTabVisible(visible); + config.setProperty(ApplicationConfiguration.FILM_SHOW_DESCRIPTION, visible); }); - filmActionPanel.zeitraumProperty.addListener((observable, oldValue, newValue) -> trans.playFromStart()); } - /** - * We perform model filtering in the background the keep UI thread alive. - */ - private ListenableFuture modelFuture; - private void loadTable() { if (modelFuture != null) { if (!modelFuture.isDone()) { @@ -798,7 +704,7 @@ private void loadTable() { } final var messageBus = MessageBus.getMessageBus(); - messageBus.publishAsync(new TableModelChangeEvent(true)); + messageBus.publish(new TableModelChangeEvent(true)); stopBeob = true; tabelle.getSpalten(); @@ -806,22 +712,28 @@ private void loadTable() { var decoratedPool = daten.getDecoratedPool(); modelFuture = decoratedPool.submit(() -> { - final GuiFilmeModelHelper helper = new GuiFilmeModelHelper(filmActionPanel, historyController); - //Thread.sleep(5000); - return helper.getFilteredTableModel(); - }); + GuiModelHelper helper; + var searchFieldData = new SearchFieldData(searchField.getText(), + searchField.getSearchMode()); + if (Daten.getInstance().getListeFilmeNachBlackList() instanceof IndexedFilmList) { + helper = new LuceneGuiFilmeModelHelper(filmActionPanel, historyController, searchFieldData); + } else { + helper = new GuiFilmeModelHelper(filmActionPanel, historyController, searchFieldData); + } + return helper.getFilteredTableModel(); + }); Futures.addCallback(modelFuture, new FutureCallback<>() { public void onSuccess(TableModel model) { SwingUtilities.invokeLater(() -> { tabelle.setModel(model); tabelle.setEnabled(true); - setInfoStatusbar(); + updateStartInfoProperty(); tabelle.setSpalten(); updateFilmData(); stopBeob = false; tabelle.scrollToSelection(); - messageBus.publishAsync(new TableModelChangeEvent(false)); + messageBus.publish(new TableModelChangeEvent(false)); }); } @@ -829,34 +741,396 @@ public void onFailure(@NotNull Throwable thrown) { logger.error("Model filtering failed!", thrown); SwingUtilities.invokeLater(() -> { tabelle.setEnabled(true); - setInfoStatusbar(); + updateStartInfoProperty(); tabelle.setSpalten(); updateFilmData(); stopBeob = false; - messageBus.publishAsync(new TableModelChangeEvent(false)); + messageBus.publish(new TableModelChangeEvent(false)); }); } }, decoratedPool); } - public class PlayFilmAction extends AbstractAction { + static class FilterVisibilityToggleButton extends JToggleButton { + public FilterVisibilityToggleButton(Action a) { + super(a); + setText(""); + final boolean visible = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.FilterDialog.VISIBLE, false); + setSelected(visible); + } + } + + public class ToggleFilterDialogVisibilityAction extends AbstractAction { + public ToggleFilterDialogVisibilityAction() { + putValue(Action.NAME, "Filterdialog anzeigen"); + putValue(Action.SHORT_DESCRIPTION, "Filter anzeigen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/filter.svg")); + putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0)); + } @Override - public synchronized void actionPerformed(ActionEvent e) { - DatenPset pset = Daten.listePset.getPsetAbspielen(); - if (pset != null) { - playerStarten(pset); + public void actionPerformed(ActionEvent e) { + var dlg = filmActionPanel.getFilterDialog(); + if (dlg != null) { + var visible = dlg.isVisible(); + visible = !visible; + + dlg.setVisible(visible); + } + } + } + + public abstract class SearchField extends JTextField { + private static final Dimension DEFAULT_DIMENSION = new Dimension(500, 100); + private static final String SEARCHMODE_PROPERTY_STRING = "searchMode"; + protected final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + protected SearchControlFieldMode searchMode; + + public SearchField() { + super("", 20); + setMaximumSize(DEFAULT_DIMENSION); + + //show clear icon when text is entered + putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true); + putClientProperty("JTextField.clearCallback", (Consumer) textField -> clearSearchField()); + + addKeyListener(new EscapeKeyAdapter()); + addActionListener(l -> performSearch()); + + createTrailingComponents(); + } + + protected abstract void createTrailingComponents(); + + protected abstract void performSearch(); + + protected void clearSearchField() { + setText(""); + fireActionPerformed(); + } + + public void addSearchModeChangeListener(PropertyChangeListener listener) { + this.pcs.addPropertyChangeListener(SEARCHMODE_PROPERTY_STRING, listener); + } + + public SearchControlFieldMode getSearchMode() { + return searchMode; + } + + public void setSearchMode(SearchControlFieldMode mode) { + var oldValue = searchMode; + searchMode = mode; + pcs.firePropertyChange(SEARCHMODE_PROPERTY_STRING, oldValue, mode); + } + + @Override + protected void fireActionPerformed() { + super.fireActionPerformed(); + } + + /** + * Clear searchfield on escape key press. + */ + class EscapeKeyAdapter extends KeyAdapter { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyChar() == KeyEvent.VK_ESCAPE) { + clearSearchField(); + } + } + } + + public class SearchHistoryButton extends JButton { + private static final Logger logger = LogManager.getLogger(); + private final List historyList = new ArrayList<>(); + private final JMenuItem miClearHistory = new JMenuItem("Historie löschen"); + private String SEARCH_HISTORY_CONFIG = "search.history.items"; + + public SearchHistoryButton(@Nullable SearchControlFieldMode mode) { + super(new FlatSearchWithHistoryIcon(true)); + setToolTipText("Vorherige Suchen"); + + if (mode != null) { + if (mode == SearchControlFieldMode.LUCENE) { + SEARCH_HISTORY_CONFIG += "_lucene"; + } + } + + miClearHistory.addActionListener(l -> { + historyList.clear(); + saveHistory(); + }); + addActionListener(l -> { + JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.add(miClearHistory); + if (!historyList.isEmpty()) { + popupMenu.addSeparator(); + for (var item : historyList) { + JMenuItem historyItem = new JMenuItem(item); + historyItem.addActionListener(li -> { + searchField.setText(item); + searchField.fireActionPerformed(); + }); + popupMenu.add(historyItem); + } + } + popupMenu.show(this, 0, this.getHeight()); + }); + + loadHistory(); + } + + public void addHistoryEntry(String text) { + if (!historyList.contains(text)) { + historyList.add(0, text); + saveHistory(); + } + + } + + private void loadHistory() { + try { + ObjectMapper mapper = new ObjectMapper(); + var json = ApplicationConfiguration.getConfiguration().getString(SEARCH_HISTORY_CONFIG, ""); + if (!json.isEmpty()) { + List entries = mapper.readValue(json, new TypeReference<>() { + }); + if (!entries.isEmpty()) { + historyList.addAll(entries); + } + } + } catch (JsonProcessingException ex) { + logger.error("Failed to load search history", ex); + } + } + + private void saveHistory() { + ObjectMapper mapper = new ObjectMapper(); + try { + var json = mapper.writeValueAsString(historyList); + ApplicationConfiguration.getConfiguration().setProperty(SEARCH_HISTORY_CONFIG, json); + } catch (JsonProcessingException e) { + logger.error("Failed to write search history", e); + } + } + } + } + + public class LuceneSearchField extends SearchField { + private static final Dimension LUCENE_DEFAULT_DIMENSION = new Dimension(700, 100); + private final SearchHistoryButton luceneSearchHistoryButton = new SearchHistoryButton(SearchControlFieldMode.LUCENE); + + public LuceneSearchField() { + setMaximumSize(LUCENE_DEFAULT_DIMENSION); + setSearchMode(SearchControlFieldMode.LUCENE); + + putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, "Lucene Search Query"); + putClientProperty(FlatClientProperties.TEXT_FIELD_LEADING_COMPONENT, luceneSearchHistoryButton); + } + + @Override + protected void createTrailingComponents() { + JToolBar searchToolbar = new JToolBar(); + searchToolbar.addSeparator(); + + var luceneBtn = new JButton(); + luceneBtn.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); + luceneBtn.setToolTipText("Lucene Query Syntax Hilfe"); + luceneBtn.addActionListener(l -> { + if (Desktop.isDesktopSupported()) { + var desktop = Desktop.getDesktop(); + if (desktop.isSupported(Desktop.Action.BROWSE)) { + try { + desktop.browse(Konstanten.LUCENE_CLIENT_HELP_URL.uri()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } else { + showError(); + } + } else { + showError(); + } + }); + searchToolbar.add(luceneBtn); + putClientProperty(FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT, searchToolbar); + } + + private void showError() { + JOptionPane.showMessageDialog(this, "Es konnte kein Browser geöffnet werden.", + Konstanten.PROGRAMMNAME, JOptionPane.ERROR_MESSAGE); + } + + @Override + protected void performSearch() { + String searchText = getText(); + if (!searchText.isEmpty()) { + luceneSearchHistoryButton.addHistoryEntry(searchText); + } + + loadTable(); + } + } + + public class RegularSearchField extends SearchField { + private final SearchHistoryButton regularSearchHistoryButton = new SearchHistoryButton(null); + + public RegularSearchField() { + addSearchModeChangeListener(evt -> setupHelperTexts()); + setupPlaceholderText(); + + putClientProperty(FlatClientProperties.TEXT_FIELD_LEADING_COMPONENT, regularSearchHistoryButton); + + installDocumentListener(); + } + + protected void setupPlaceholderText() { + //put placeholder text + boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + if (bSearchThroughDescription) + setSearchMode(SearchControlFieldMode.IRGENDWO); + else + setSearchMode(SearchControlFieldMode.THEMA_TITEL); + } + + @Override + protected void performSearch() { + String searchText = getText(); + if (!searchText.isEmpty()) { + regularSearchHistoryButton.addHistoryEntry(searchText); + } + + loadTable(); + } + + private void installDocumentListener() { + getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + doCheck(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + doCheck(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + doCheck(); + } + + private void doCheck() { + var searchText = getText(); + checkPatternValidity(searchText); + setForegroundTextColor(searchText); + } + }); + } + + private void setForegroundTextColor(String text) { + if (Filter.isPattern(text)) + setForeground(MVColor.getRegExPatternColor()); + else + setForeground(UIManager.getColor("TextField.foreground")); + } + + private boolean isPatternValid(String text) { + return Filter.makePatternNoCache(text) != null; + } + + private void checkPatternValidity(String text) { + if (Filter.isPattern(text)) + GuiFunktionen.showErrorIndication(this, !isPatternValid(text)); + else + GuiFunktionen.showErrorIndication(this, false); + } + + /** + * Sets tooltip and placeholder texts according to {@link RegularSearchField#searchMode}. + */ + private void setupHelperTexts() { + String text; + switch (searchMode) { + case IRGENDWO -> text = "Thema/Titel/Beschreibung"; + case THEMA_TITEL -> text = "Thema/Titel"; + case LUCENE -> text = "Lucene Query"; + default -> { + logger.error("Illegal search mode"); + text = ""; + } + + } + putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, text); + + if (searchMode == SearchControlFieldMode.IRGENDWO || searchMode == SearchControlFieldMode.THEMA_TITEL) { + setToolTipText(text + " durchsuchen"); } else { - MVMessageDialog.showMessageDialog(mediathekGui, - "Im Menü unter \"Datei->Einstellungen->Set bearbeiten\" ein Programm zum Abspielen festlegen.", - "kein Videoplayer!", - JOptionPane.INFORMATION_MESSAGE); + setToolTipText("Lucene Query Syntax für die Suche"); + } + } + + @Override + protected void createTrailingComponents() { + JToolBar searchToolbar = new JToolBar(); + searchToolbar.addSeparator(); + searchToolbar.add(new ToggleSearchFieldToggleButton()); + putClientProperty(FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT, searchToolbar); + } + + class ToggleSearchFieldToggleButton extends JToggleButton { + public ToggleSearchFieldToggleButton() { + FlatSVGIcon selectedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg"); + selectedIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> MVColor.getSelectedColor())); + FlatSVGIcon normalIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg"); + normalIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.GRAY)); + setIcon(normalIcon); + setSelectedIcon(selectedIcon); + + boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + setSelected(bSearchThroughDescription); + setupToolTip(bSearchThroughDescription); + + addActionListener(l -> { + switch (getSearchMode()) { + case IRGENDWO -> { + setSearchMode(SearchControlFieldMode.THEMA_TITEL); + setupToolTip(false); + } + case THEMA_TITEL -> { + setSearchMode(SearchControlFieldMode.IRGENDWO); + setupToolTip(true); + } + } + //update config + ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, getSearchMode() == SearchControlFieldMode.IRGENDWO); + + loadTable(); + }); + } + + private void setupToolTip(boolean active) { + if (active) + setToolTipText("Suche in Beschreibung aktiviert"); + else + setToolTipText("Suche in Beschreibung deaktiviert"); } } } public class SaveFilmAction extends AbstractAction { + public SaveFilmAction() { + putValue(Action.SHORT_DESCRIPTION, "Film downloaden"); + putValue(Action.NAME, "Film downloaden"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg")); + KeyStroke keyStroke; + if (SystemUtils.IS_OS_MAC_OSX) { + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F7, GuiFunktionen.getPlatformControlKey()); + } else + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, GuiFunktionen.getPlatformControlKey()); + putValue(Action.ACCELERATOR_KEY, keyStroke); + } @Override public void actionPerformed(ActionEvent e) { @@ -865,6 +1139,18 @@ public void actionPerformed(ActionEvent e) { } public class BookmarkFilmAction extends AbstractAction { + public BookmarkFilmAction() { + KeyStroke keyStroke; + if (SystemUtils.IS_OS_MAC_OSX) { + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F8, GuiFunktionen.getPlatformControlKey()); + } else { + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_B, GuiFunktionen.getPlatformControlKey()); + } + putValue(Action.ACCELERATOR_KEY, keyStroke); + putValue(Action.SHORT_DESCRIPTION, "Film merken"); + putValue(Action.NAME, "Film merken"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg")); + } @Override public void actionPerformed(ActionEvent e) { @@ -872,13 +1158,6 @@ public void actionPerformed(ActionEvent e) { } } - public class BookmarkManageListAction extends AbstractAction { - @Override - public void actionPerformed(ActionEvent ae) { - showBookmarkWindow(); - } - } - private class CopyUrlToClipboardAction extends AbstractAction { private final FilmResolution.Enum resolution; @@ -904,14 +1183,12 @@ class TableContextMenuHandler extends MouseAdapter { private final BeobBlacklist beobBlacklistSender = new BeobBlacklist(true, false); private final BeobBlacklist beobBlacklistSenderThema = new BeobBlacklist(true, true); private final BeobBlacklist beobBlacklistThema = new BeobBlacklist(false, true); - private final JMenuItem miPlay = createPlayItem(); private final JMenuItem miSave = createSaveFilmItem(); private final JMenuItem miBookmark = createBookmarkFilmItem(); - private final ShowFilmInformationAction showFilmInformationAction = - new ShowFilmInformationAction(false); private final ActionListener unseenActionListener = new BeobHistory(false); private final ActionListener seenActionListener = new BeobHistory(true); private final JDownloadHelper jDownloadHelper = new JDownloadHelper(); + private final DownloadSubtitleAction downloadSubtitleAction = new DownloadSubtitleAction(GuiFilme.this); private Point p; private JMenuItem miPrintTable; @@ -978,43 +1255,33 @@ private void buttonTable(int row, int column) { } } if (!stop) { - playAction.actionPerformed(null); + playFilmAction.actionPerformed(null); } }); } case DatenFilm.FILM_AUFZEICHNEN -> saveFilm(null); - case DatenFilm.FILM_MERKEN -> { - getCurrentlySelectedFilm().ifPresent(film -> { - if (!film.isLivestream()) - bookmarkFilm(); - }); - } + case DatenFilm.FILM_MERKEN -> getCurrentlySelectedFilm().ifPresent(film -> { + if (!film.isLivestream()) + bookmarkFilm(); + }); } } } - private JMenuItem createPlayItem() { - JMenuItem item = new JMenuItem("Film abspielen"); - item.setIcon(IconFontSwing.buildIcon(FontAwesome.PLAY, 16)); - item.addActionListener(playAction); - return item; - } - private JMenuItem createSaveFilmItem() { JMenuItem item = new JMenuItem("Film aufzeichnen"); - item.setIcon(IconFontSwing.buildIcon(FontAwesome.DOWNLOAD, 16)); + item.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg")); item.addActionListener(saveFilmAction); return item; } private JMenuItem createBookmarkFilmItem() { JMenuItem item = new JMenuItem("Film merken"); - item.setIcon(IconFontSwing.buildIcon(FontAwesome.BOOKMARK_O, 16)); + item.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg")); item.addActionListener(bookmarkFilmAction); return item; } - private void showMenu(MouseEvent evt) { p = evt.getPoint(); final int nr = tabelle.rowAtPoint(p); @@ -1024,7 +1291,7 @@ private void showMenu(MouseEvent evt) { JPopupMenu jPopupMenu = new JPopupMenu(); - jPopupMenu.add(miPlay); + jPopupMenu.add(playFilmAction); jPopupMenu.add(miSave); jPopupMenu.add(miBookmark); jPopupMenu.addSeparator(); @@ -1032,40 +1299,28 @@ private void showMenu(MouseEvent evt) { JMenu submenueAbo = new JMenu("Abo"); jPopupMenu.add(submenueAbo); // Abo anlegen - JMenuItem itemAboLoeschen = new JMenuItem("Abo Löschen"); JMenuItem itemAbo = new JMenuItem("Abo mit Sender und Thema anlegen"); JMenuItem itemAboMitTitel = new JMenuItem("Abo mit Sender und Thema und Titel anlegen"); - JMenuItem itemChangeAboFilter = new JMenuItem("Abo ändern"); Optional res = getFilm(nr); res.ifPresent(film -> { - if ((daten.getListeAbo().getAboFuerFilm_schnell(film, false /*die Länge nicht prüfen*/)) - != null) { - // gibts schon, dann löschen + if ((daten.getListeAbo().getAboFuerFilm_schnell(film, false)) != null) { + // gibts schon -> deaktivieren... itemAbo.setEnabled(false); itemAboMitTitel.setEnabled(false); - itemAboLoeschen.addActionListener(beobAbo); - - // dann können wir auch ändern - itemChangeAboFilter.addActionListener(new BeobChangeAbo()); } else { - itemAboLoeschen.setEnabled(false); - itemChangeAboFilter.setEnabled(false); - // neues Abo anlegen + // neues Abo anlegen möglich... itemAbo.addActionListener(beobAbo); itemAboMitTitel.addActionListener(beobAboMitTitel); } // update Bookmark state if (film.isLivestream()) { jPopupMenu.remove(miBookmark); - } - else { + } else { miBookmark.setText(film.isBookmarked() ? "Film aus Merkliste entfernen" : "Film merken"); } }); - submenueAbo.add(itemAboLoeschen); - submenueAbo.add(itemChangeAboFilter); submenueAbo.add(itemAbo); submenueAbo.add(itemAboMitTitel); @@ -1116,18 +1371,24 @@ private void showMenu(MouseEvent evt) { setupSearchEntries(jPopupMenu, film); }); + res.ifPresent(film -> { + if (film.hasSubtitle()) { + jPopupMenu.add(downloadSubtitleAction); + jPopupMenu.addSeparator(); + } + }); + // Drucken jPopupMenu.add(miPrintTable); - jPopupMenu.add(showFilmInformationAction); + jPopupMenu.add(mediathekGui.showFilmInformationAction); // History res.ifPresent(film -> setupHistoryContextActions(jPopupMenu, film)); // anzeigen jPopupMenu.show(evt.getComponent(), evt.getX(), evt.getY()); } - private void setupHistoryContextActions(@NotNull JPopupMenu popupMenu, @NotNull DatenFilm film) - { + private void setupHistoryContextActions(@NotNull JPopupMenu popupMenu, @NotNull DatenFilm film) { if (!film.isLivestream()) { JMenuItem miHistory; try (var history = new SeenHistoryController()) { @@ -1210,10 +1471,10 @@ private void setupFilmUrlCopyToClipboardEntries(@NotNull JMenu parentMenu, @NotN } } - if (!film.getUrlSubtitle().isEmpty()) { + if (!film.getSubtitleUrl().isEmpty()) { item = new JMenuItem("Untertitel-URL"); - item.addActionListener(e -> GuiFunktionen.copyToClipboard(film.getUrlSubtitle())); + item.addActionListener(e -> GuiFunktionen.copyToClipboard(film.getSubtitleUrl())); parentMenu.add(item); } } @@ -1295,37 +1556,6 @@ public void actionPerformed(ActionEvent e) { } } - private class BeobChangeAbo implements ActionListener { - - @Override - public void actionPerformed(ActionEvent e) { - if (Daten.listePset.getListeAbo().isEmpty()) { - new DialogAboNoSet(mediathekGui).setVisible(true); - } else { - final int nr = tabelle.rowAtPoint(p); - if (nr >= 0) { - stopBeob = true; - Optional res = getFilm(nr); - res.ifPresent(film -> { - DatenAbo datenAbo; - if ((datenAbo = - daten.getListeAbo().getAboFuerFilm_schnell(film, false /*ohne Länge*/)) - != null) { - // gibts schon, dann löschen - DialogEditAbo dialog = - new DialogEditAbo(mediathekGui, datenAbo, false /*onlyOne*/); - dialog.setVisible(true); - if (dialog.successful()) { - daten.getListeAbo().aenderungMelden(); - } - } - }); - stopBeob = false; - } - } - } - } - private class BeobAbo implements ActionListener { private final boolean mitTitel; diff --git a/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt b/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt index 313b6d51cc..d5adf29791 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt +++ b/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt @@ -1,13 +1,12 @@ package mediathek.gui.tabs.tab_film -import javafx.application.Platform import mediathek.config.Konstanten import mediathek.controller.history.SeenHistoryController import mediathek.daten.DatenFilm import mediathek.daten.FilmResolution import mediathek.mainwindow.MediathekGui +import mediathek.tool.SwingErrorDialog import mediathek.tool.http.MVHttpClient -import mediathek.tool.javafx.FXErrorDialog import okhttp3.FormBody import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl @@ -27,12 +26,12 @@ class JDownloadHelper { private fun downloadUrl(url: HttpUrl, film: DatenFilm) { val formBody: RequestBody = FormBody.Builder() - .add("urls", url.toString()) - .build() + .add("urls", url.toString()) + .build() val request = Request.Builder() - .url("http://127.0.0.1:9666/flash/add") - .post(formBody) - .build() + .url("http://127.0.0.1:9666/flash/add") + .post(formBody) + .build() try { val builder = MVHttpClient.getInstance().reducedTimeOutClient.newBuilder() builder.connectTimeout(125, TimeUnit.MILLISECONDS) @@ -41,17 +40,18 @@ class JDownloadHelper { if (it.isSuccessful) historyController.markSeen(film) } - } catch (e: ConnectException) { + } + catch (e: ConnectException) { showErrorMessage() - } catch (e: SocketTimeoutException) { + } + catch (e: SocketTimeoutException) { showErrorMessage() - } catch (e: Exception) { + } + catch (e: Exception) { logger.error("downloadUrl", e) - Platform.runLater { - FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, "Download nicht möglich", - "Die URL konnte nicht mit JDownloader geladen werden.\n" + - "Bitte wenden Sie sich bei Bedarf an das Forum.", e) - } + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), + "Die URL konnte nicht mit JDownloader geladen werden.
" + + "Bitte wenden Sie sich bei Bedarf an das Forum.", e) } } @@ -66,47 +66,40 @@ class JDownloadHelper { fun installContextMenu(film: DatenFilm, jPopupMenu: JPopupMenu) { jPopupMenu.addSeparator() - val miText = "Mit JDownloader herunterladen" - if (film.isPlayList) { - val miDownloadJD = JMenuItem(miText) - miDownloadJD.addActionListener { - val url = film.urlNormalQuality.toHttpUrl() - downloadUrl(url, film) - } - jPopupMenu.add(miDownloadJD) - } else { - val mJD = JMenu(miText) - val uNormal = film.urlNormalQuality.toHttpUrl() - val uHq = film.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY).toHttpUrl() - val uLow = film.getUrlFuerAufloesung(FilmResolution.Enum.LOW).toHttpUrl() - if (film.isHighQuality) { - val miHq = JMenuItem("in bester Qualität") - miHq.addActionListener { downloadUrl(uHq, film) } - mJD.add(miHq) - } - val miNormal = JMenuItem("in normaler Qualität") - miNormal.addActionListener { downloadUrl(uNormal, film) } - mJD.add(miNormal) - if (uLow !== uNormal) { - val miLow = JMenuItem("in niedriger Qualität") - miLow.addActionListener { downloadUrl(uLow, film) } - mJD.add(miLow) - } - jPopupMenu.add(mJD) + + val mJD = JMenu("Mit JDownloader herunterladen") + val uNormal = film.urlNormalQuality.toHttpUrl() + val uHq = film.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY).toHttpUrl() + val uLow = film.getUrlFuerAufloesung(FilmResolution.Enum.LOW).toHttpUrl() + + if (film.isHighQuality) { + val miHq = JMenuItem("in bester Qualität") + miHq.addActionListener { downloadUrl(uHq, film) } + mJD.add(miHq) + } + val miNormal = JMenuItem("in normaler Qualität") + miNormal.addActionListener { downloadUrl(uNormal, film) } + mJD.add(miNormal) + + if (uLow !== uNormal) { + val miLow = JMenuItem("in niedriger Qualität") + miLow.addActionListener { downloadUrl(uLow, film) } + mJD.add(miLow) } + jPopupMenu.add(mJD) val miWebsiteToJd = JMenuItem("Webseiten-URL an JDownloader übergeben") miWebsiteToJd.addActionListener { try { - val webSiteUrl = film.websiteLink.toHttpUrl() + val webSiteUrl = film.websiteUrl.toHttpUrl() downloadUrl(webSiteUrl, film) } catch (e: IllegalArgumentException) { - logger.error("Illegal Website URL found: {}", film.websiteLink) + logger.error("Illegal Website URL found: {}", film.websiteUrl) } } jPopupMenu.add(miWebsiteToJd) - if (film.websiteLink.isBlank()) { + if (film.websiteUrl.isBlank()) { miWebsiteToJd.isEnabled = false } } diff --git a/src/main/java/mediathek/gui/tabs/tab_film/PsetButtonsPanel.java b/src/main/java/mediathek/gui/tabs/tab_film/PsetButtonsPanel.java new file mode 100644 index 0000000000..97d07614ed --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_film/PsetButtonsPanel.java @@ -0,0 +1,75 @@ +package mediathek.gui.tabs.tab_film; + +import mediathek.config.Daten; +import mediathek.daten.DatenPset; +import mediathek.gui.messages.ProgramSetChangedEvent; +import mediathek.tool.MessageBus; +import net.engio.mbassy.listener.Handler; +import org.jdesktop.swingx.WrapLayout; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; + +public class PsetButtonsPanel extends JPanel { + private static final int DEFAULT_HEIGHT = 90; + private final JPanel btnPanel = new JPanel(); + + private final GuiFilme guiFilme; + + public PsetButtonsPanel(@NotNull GuiFilme guiFilme) { + this.guiFilme = guiFilme; + + setLayout(new BorderLayout()); + setPreferredSize(new Dimension(Integer.MAX_VALUE, DEFAULT_HEIGHT)); + setMinimumSize(new Dimension(100, DEFAULT_HEIGHT)); + + btnPanel.setLayout(new WrapLayout(FlowLayout.LEFT, 5, 5)); + + JScrollPane sp = new JScrollPane(); + add(sp, BorderLayout.CENTER); + + sp.setViewportView(btnPanel); + + setupButtonLayout(); + + MessageBus.getMessageBus().subscribe(this); + } + + public void install(@NotNull JTabbedPane tabbedPane) { + tabbedPane.add("Buttons", this); + } + + @Handler + private void handleProgramSetChangedEvent(ProgramSetChangedEvent e) { + System.out.println("Handle PSET CHANGE"); + SwingUtilities.invokeLater(this::setupButtonLayout); + } + + protected void setupButtonLayout() { + btnPanel.removeAll(); + + for (var pset : Daten.listePset.getListeButton()) { + final var psetName = pset.arr[DatenPset.PROGRAMMSET_NAME]; + final var psetColor = pset.getFarbe(); + if (!pset.isFreeLine()) { + if (pset.isLabel()) { + var l = new JLabel(psetName); + if (psetColor != null) + l.setForeground(psetColor); + btnPanel.add(l); + } else { + var b = new JButton(psetName); + if (psetColor != null) + b.setBackground(psetColor); + b.addActionListener(l -> guiFilme.playerStarten(pset)); + btnPanel.add(b); + } + } else { + btnPanel.add(new JLabel("")); + } + } + validate(); + repaint(); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadController.java b/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadController.java deleted file mode 100644 index bfbffb374e..0000000000 --- a/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadController.java +++ /dev/null @@ -1,100 +0,0 @@ -package mediathek.gui.tabs.tab_film; - -import javafx.embed.swing.SwingFXUtils; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Button; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.image.ImageView; -import mediathek.config.Daten; -import mediathek.daten.DatenFilm; -import mediathek.daten.DatenPset; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.tool.sender_icon_cache.MVSenderIconCache; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.net.URL; -import java.util.ResourceBundle; - -public class SaveDownloadController implements Initializable { - private final JDialog dialog; - @FXML - private Button saveBtn; - @FXML - private Button cancelBtn; - @FXML - private Label lblThema; - @FXML - private Label lblTitle; - @FXML - ImageView ivSender; - @FXML - ComboBox cBxPSet; - - private boolean success; - private final DatenFilm film; - private final DatenPset pSet; - - public SaveDownloadController(@NotNull JDialog dialog, @NotNull DatenFilm film, @Nullable DatenPset pSet) { - this.dialog = dialog; - this.film = film; - this.pSet = pSet; - } - - private void setupButtonBarActions() { - saveBtn.setOnAction(e -> { - success = true; - dialog.dispose(); - }); - - cancelBtn.setOnAction(e -> { - success = false; - dialog.dispose(); - }); - } - - private void setupSenderLogo() { - var icn = MVSenderIconCache.get(film.getSender()); - icn.ifPresentOrElse(icon -> { - var image = SwingFXUtils.toFXImage(JavaFxUtils.toBufferedImage(icon),null); - ivSender.setImage(image); - }, () -> ivSender.setImage(null)); - } - - private void setupProgramSetComboBox() { - var saveOnlyPSets = Daten.listePset.getListeSpeichern(); - for (var pSet : saveOnlyPSets) { - cBxPSet.getItems().add(pSet.arr[DatenPset.PROGRAMMSET_NAME]); - } - - if (pSet != null) { - cBxPSet.getSelectionModel().select(pSet.arr[DatenPset.PROGRAMMSET_NAME]); - } - else - cBxPSet.getSelectionModel().selectFirst(); - - if (cBxPSet.getItems().size() <= 1) { - cBxPSet.setDisable(true); - } - - //TODO change resolution on PSet change... - } - - @Override - public void initialize(URL location, ResourceBundle resources) { - setupButtonBarActions(); - setupSenderLogo(); - - lblThema.setText(film.getThema()); - lblTitle.setText(film.getTitle()); - - setupProgramSetComboBox(); - } - - public boolean success() { - return success; - } -} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadDialog.java b/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadDialog.java deleted file mode 100644 index f4f3243eeb..0000000000 --- a/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadDialog.java +++ /dev/null @@ -1,45 +0,0 @@ -package mediathek.gui.tabs.tab_film; - -import javafx.embed.swing.JFXPanel; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.layout.BorderPane; -import mediathek.daten.DatenFilm; -import mediathek.daten.DatenPset; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.tool.GuiFunktionen; - -import javax.swing.*; -import java.awt.*; -import java.io.IOException; -import java.net.URL; - -class SaveDownloadDialog extends JDialog { - public SaveDownloadController controller; - - public SaveDownloadDialog(DatenFilm datenFilm, DatenPset pSet) { - setTitle("FX Film Speichern"); - setModal(true); - JFXPanel fxPanel = new JFXPanel(); - - var contentPane = getContentPane(); - contentPane.setLayout(new BorderLayout()); - contentPane.add(fxPanel, BorderLayout.CENTER); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - try { - URL url = getClass().getResource("/mediathek/res/programm/fxml/save_download_dialog.fxml"); - FXMLLoader fxmlLoader = new FXMLLoader(url); - controller = new SaveDownloadController(this, datenFilm, pSet); - fxmlLoader.setController(controller); - BorderPane p = fxmlLoader.load(); - fxPanel.setScene(new Scene(p)); - } catch (IOException e) { - e.printStackTrace(); - } - SwingUtilities.invokeLater(() -> { - pack(); - GuiFunktionen.centerOnScreen(this, false); - }); - }); - } -} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/SearchFieldData.kt b/src/main/java/mediathek/gui/tabs/tab_film/SearchFieldData.kt new file mode 100644 index 0000000000..efe21a4c7b --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_film/SearchFieldData.kt @@ -0,0 +1,26 @@ +package mediathek.gui.tabs.tab_film + +import mediathek.javafx.filterpanel.SearchControlFieldMode +import mediathek.tool.Filter +import java.util.regex.Pattern + +@JvmRecord +data class SearchFieldData(val searchFieldText: String, val searchMode: SearchControlFieldMode) { + fun searchThroughDescriptions(): Boolean { + return searchMode == SearchControlFieldMode.IRGENDWO + } + + fun isEmpty(): Boolean { + return searchFieldText.isEmpty() + } + + fun evaluateThemaTitel(): Array { + return if (Filter.isPattern(searchFieldText)) { + arrayOf(searchFieldText) + } else { + Pattern.compile(",") + .splitAsStream(searchFieldText).map { s: String -> s.lowercase() } + .toArray { size -> arrayOfNulls(size) } + } + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java b/src/main/java/mediathek/gui/tabs/tab_film/helpers/GuiFilmeModelHelper.java similarity index 50% rename from src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java rename to src/main/java/mediathek/gui/tabs/tab_film/helpers/GuiFilmeModelHelper.java index d6d86e0463..1000c3447d 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/helpers/GuiFilmeModelHelper.java @@ -1,10 +1,10 @@ -package mediathek.gui.tabs.tab_film; +package mediathek.gui.tabs.tab_film.helpers; import javafx.collections.ObservableList; import mediathek.config.Daten; import mediathek.controller.history.SeenHistoryController; import mediathek.daten.DatenFilm; -import mediathek.daten.ListeFilme; +import mediathek.gui.tabs.tab_film.SearchFieldData; import mediathek.gui.tabs.tab_film.searchfilters.FinalStageFilterNoPattern; import mediathek.gui.tabs.tab_film.searchfilters.FinalStageFilterNoPatternWithDescription; import mediathek.gui.tabs.tab_film.searchfilters.FinalStagePatternFilter; @@ -18,41 +18,23 @@ import javax.swing.table.TableModel; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; -public class GuiFilmeModelHelper { - private final FilmActionPanel filmActionPanel; +public class GuiFilmeModelHelper extends GuiModelHelper { private TModelFilm filmModel; - private final ListeFilme listeFilme; - private final SeenHistoryController historyController; - private boolean searchThroughDescriptions; - private boolean showNewOnly; - private boolean showBookmarkedOnly; - private boolean showSubtitlesOnly; - private boolean showHqOnly; - private boolean dontShowSeen; - private boolean dontShowAbos; - private boolean showLivestreamsOnly; - private boolean dontShowTrailers; - private boolean dontShowGebaerdensprache; - private boolean dontShowAudioVersions; - private long maxLength; private String[] arrIrgendwo; - private long minLengthInSeconds; - private long maxLengthInSeconds; public GuiFilmeModelHelper(@NotNull FilmActionPanel filmActionPanel, - @NotNull SeenHistoryController historyController) { + @NotNull SeenHistoryController historyController, + @NotNull SearchFieldData searchFieldData) { this.filmActionPanel = filmActionPanel; this.historyController = historyController; - - listeFilme = Daten.getInstance().getListeFilmeNachBlackList(); + this.searchFieldData = searchFieldData; } private String getFilterThema() { - String filterThema = filmActionPanel.themaBox.getSelectionModel().getSelectedItem(); + String filterThema = filmActionPanel.getViewSettingsPane().themaComboBox.getSelectionModel().getSelectedItem(); if (filterThema == null) { filterThema = ""; } @@ -60,97 +42,63 @@ private String getFilterThema() { return filterThema; } - private String[] evaluateThemaTitel() { - String[] arrThemaTitel; - - final String filterThemaTitel = filmActionPanel.roSearchStringProperty.getValueSafe(); - if (Filter.isPattern(filterThemaTitel)) { - arrThemaTitel = new String[]{filterThemaTitel}; - } else { - arrThemaTitel = filterThemaTitel.split(","); - for (int i = 0; i < arrThemaTitel.length; ++i) { - arrThemaTitel[i] = arrThemaTitel[i].trim().toLowerCase(); - } - } - - return arrThemaTitel; - } + @Override + protected boolean noFiltersAreSet() { + var filmLengthSlider = filmActionPanel.getFilmLengthSlider(); - private boolean noFiltersAreSet() { return filmActionPanel.getViewSettingsPane().senderCheckList.getCheckModel().isEmpty() && getFilterThema().isEmpty() - && filmActionPanel.roSearchStringProperty.getValueSafe().isEmpty() - && ((int) filmActionPanel.filmLengthSlider.getLowValue() == 0) - && ((int) filmActionPanel.filmLengthSlider.getHighValue() == FilmLengthSlider.UNLIMITED_VALUE) - && !filmActionPanel.dontShowAbos.getValue() - && !filmActionPanel.showUnseenOnly.getValue() - && !filmActionPanel.showOnlyHd.getValue() - && !filmActionPanel.showSubtitlesOnly.getValue() - && !filmActionPanel.showLivestreamsOnly.getValue() - && !filmActionPanel.showNewOnly.getValue() - && !filmActionPanel.showBookMarkedOnly.getValue() - && !filmActionPanel.dontShowTrailers.getValue() - && !filmActionPanel.dontShowSignLanguage.getValue() - && !filmActionPanel.dontShowAudioVersions.getValue(); - } - - private void updateFilterVars() { - showNewOnly = filmActionPanel.showNewOnly.getValue(); - showBookmarkedOnly = filmActionPanel.showBookMarkedOnly.getValue(); - showSubtitlesOnly = filmActionPanel.showSubtitlesOnly.getValue(); - showHqOnly = filmActionPanel.showOnlyHd.getValue(); - dontShowSeen = filmActionPanel.showUnseenOnly.getValue(); - dontShowAbos = filmActionPanel.dontShowAbos.getValue(); - showLivestreamsOnly = filmActionPanel.showLivestreamsOnly.getValue(); - dontShowTrailers = filmActionPanel.dontShowTrailers.getValue(); - dontShowGebaerdensprache = filmActionPanel.dontShowSignLanguage.getValue(); - dontShowAudioVersions = filmActionPanel.dontShowAudioVersions.getValue(); - searchThroughDescriptions = filmActionPanel.searchThroughDescription.getValue(); - - arrIrgendwo = evaluateThemaTitel(); + && searchFieldData.isEmpty() + && ((int) filmLengthSlider.getLowValue() == 0) + && ((int) filmLengthSlider.getHighValue() == FilmLengthSlider.UNLIMITED_VALUE) + && !filmActionPanel.isDontShowAbos() + && !filmActionPanel.isShowUnseenOnly() + && !filmActionPanel.isShowOnlyHighQuality() + && !filmActionPanel.isShowSubtitlesOnly() + && !filmActionPanel.isShowLivestreamsOnly() + && !filmActionPanel.isShowNewOnly() + && !filmActionPanel.isShowBookMarkedOnly() + && !filmActionPanel.isDontShowTrailers() + && !filmActionPanel.isDontShowSignLanguage() + && !filmActionPanel.isDontShowAudioVersions(); } - private void calculateFilmLengthSliderValues() { - final long minLength = (long) filmActionPanel.filmLengthSlider.getLowValue(); - maxLength = (long) filmActionPanel.filmLengthSlider.getHighValue(); - minLengthInSeconds = TimeUnit.SECONDS.convert(minLength, TimeUnit.MINUTES); - maxLengthInSeconds = TimeUnit.SECONDS.convert(maxLength, TimeUnit.MINUTES); - } private void performTableFiltering() { - updateFilterVars(); + arrIrgendwo = searchFieldData.evaluateThemaTitel(); + calculateFilmLengthSliderValues(); final String filterThema = getFilterThema(); final ObservableList selectedSenders = filmActionPanel.getViewSettingsPane().senderCheckList.getCheckModel().getCheckedItems(); - if (dontShowSeen) + if (filmActionPanel.isShowUnseenOnly()) historyController.prepareMemoryCache(); - var stream = listeFilme.parallelStream(); + var stream = Daten.getInstance().getListeFilmeNachBlackList().parallelStream(); if (!selectedSenders.isEmpty()) { //ObservableList.contains() is insanely slow...this speeds up to factor 250! Set senderSet = new HashSet<>(selectedSenders.size()); senderSet.addAll(selectedSenders); stream = stream.filter(f -> senderSet.contains(f.getSender())); } - if (showNewOnly) + if (filmActionPanel.isShowNewOnly()) stream = stream.filter(DatenFilm::isNew); - if (showBookmarkedOnly) + if (filmActionPanel.isShowBookMarkedOnly()) stream = stream.filter(DatenFilm::isBookmarked); - if (showLivestreamsOnly) + if (filmActionPanel.isShowLivestreamsOnly()) stream = stream.filter(DatenFilm::isLivestream); - if (showHqOnly) + if (filmActionPanel.isShowOnlyHighQuality()) stream = stream.filter(DatenFilm::isHighQuality); - if (dontShowTrailers) + if (filmActionPanel.isDontShowTrailers()) stream = stream.filter(film -> !film.isTrailerTeaser()); - if (dontShowGebaerdensprache) + if (filmActionPanel.isDontShowSignLanguage()) stream = stream.filter(film -> !film.isSignLanguage()); - if (dontShowAudioVersions) + if (filmActionPanel.isDontShowAudioVersions()) stream = stream.filter(film -> !film.isAudioVersion()); - if (dontShowAbos) + if (filmActionPanel.isDontShowAbos()) stream = stream.filter(film -> film.getAbo() == null); - if (showSubtitlesOnly) { + if (filmActionPanel.isShowSubtitlesOnly()) { stream = stream.filter(this::subtitleCheck); } if (!filterThema.isEmpty()) { @@ -159,7 +107,7 @@ private void performTableFiltering() { if (maxLength < FilmLengthSlider.UNLIMITED_VALUE) { stream = stream.filter(this::maxLengthCheck); } - if (dontShowSeen) { + if (filmActionPanel.isShowUnseenOnly()) { stream = stream.filter(this::seenCheck); } //perform min length filtering after all others may have reduced the available entries... @@ -180,7 +128,7 @@ private void performTableFiltering() { list.clear(); - if (dontShowSeen) + if (filmActionPanel.isShowUnseenOnly()) historyController.emptyMemoryCache(); } @@ -189,7 +137,7 @@ private Predicate createFinalStageFilter() { //otherwise use more optimized search boolean isPattern = Filter.isPattern(arrIrgendwo[0]) || arrIrgendwo.length > 1; Predicate filter; - if (searchThroughDescriptions) { + if (searchFieldData.searchThroughDescriptions()) { if (isPattern) filter = new FinalStagePatternFilterWithDescription(arrIrgendwo); else @@ -207,28 +155,11 @@ private Predicate createFinalStageFilter() { private boolean subtitleCheck(DatenFilm film) { return film.hasSubtitle() || film.hasBurnedInSubtitles(); } - private boolean maxLengthCheck(DatenFilm film) { - return film.getFilmLength() < maxLengthInSeconds; - } - - private boolean seenCheck(DatenFilm film) { - return !historyController.hasBeenSeenFromCache(film); - } - - private boolean minLengthCheck(DatenFilm film) { - final long filmLength = film.getFilmLength(); - if (filmLength == 0) - return true; // always show entries with length 0, which are internally "no length" - else - return filmLength >= minLengthInSeconds; - } - /** - * Filter the filmlist. - * - * @return the filtered table model. - */ + @Override public TableModel getFilteredTableModel() { + final var listeFilme = Daten.getInstance().getListeFilmeNachBlackList(); + if (!listeFilme.isEmpty()) { if (noFiltersAreSet()) { //adjust initial capacity diff --git a/src/main/java/mediathek/gui/tabs/tab_film/helpers/GuiModelHelper.java b/src/main/java/mediathek/gui/tabs/tab_film/helpers/GuiModelHelper.java new file mode 100644 index 0000000000..037ead4fef --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_film/helpers/GuiModelHelper.java @@ -0,0 +1,50 @@ +package mediathek.gui.tabs.tab_film.helpers; + +import mediathek.controller.history.SeenHistoryController; +import mediathek.daten.DatenFilm; +import mediathek.gui.tabs.tab_film.SearchFieldData; +import mediathek.javafx.filterpanel.FilmActionPanel; + +import javax.swing.table.TableModel; +import java.util.concurrent.TimeUnit; + +public abstract class GuiModelHelper { + protected SliderRange sliderRange; + protected long maxLength; + protected FilmActionPanel filmActionPanel; + protected SeenHistoryController historyController; + protected SearchFieldData searchFieldData; + + /** + * Filter the filmlist. + * + * @return the filtered table model. + */ + public abstract TableModel getFilteredTableModel(); + + protected boolean maxLengthCheck(DatenFilm film) { + return film.getFilmLength() < sliderRange.maxLengthInSeconds(); + } + + protected boolean minLengthCheck(DatenFilm film) { + var filmLength = film.getFilmLength(); + if (filmLength == 0) + return true; // always show entries with length 0, which are internally "no length" + else + return filmLength >= sliderRange.minLengthInSeconds(); + } + + protected abstract boolean noFiltersAreSet(); + + protected boolean seenCheck(DatenFilm film) { + return !historyController.hasBeenSeenFromCache(film); + } + + protected void calculateFilmLengthSliderValues() { + final long minLength = (long) filmActionPanel.getFilmLengthSlider().getLowValue(); + maxLength = (long) filmActionPanel.getFilmLengthSlider().getHighValue(); + var minLengthInSeconds = TimeUnit.SECONDS.convert(minLength, TimeUnit.MINUTES); + var maxLengthInSeconds = TimeUnit.SECONDS.convert(maxLength, TimeUnit.MINUTES); + sliderRange = new SliderRange(minLengthInSeconds, maxLengthInSeconds); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/helpers/LuceneGuiFilmeModelHelper.java b/src/main/java/mediathek/gui/tabs/tab_film/helpers/LuceneGuiFilmeModelHelper.java new file mode 100644 index 0000000000..04b64b23c7 --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_film/helpers/LuceneGuiFilmeModelHelper.java @@ -0,0 +1,296 @@ +package mediathek.gui.tabs.tab_film.helpers; + +import com.google.common.base.Stopwatch; +import javafx.collections.ObservableList; +import mediathek.config.Daten; +import mediathek.controller.history.SeenHistoryController; +import mediathek.daten.DatenFilm; +import mediathek.daten.IndexedFilmList; +import mediathek.gui.tabs.tab_film.SearchFieldData; +import mediathek.gui.tasks.LuceneIndexKeys; +import mediathek.javafx.filterpanel.FilmActionPanel; +import mediathek.javafx.filterpanel.FilmLengthSlider; +import mediathek.javafx.filterpanel.ZeitraumSpinner; +import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SwingErrorDialog; +import mediathek.tool.models.TModelFilm; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.document.DateTools; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.queryparser.classic.QueryParser; +import org.apache.lucene.queryparser.flexible.standard.StandardQueryParser; +import org.apache.lucene.queryparser.flexible.standard.config.PointsConfig; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Query; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.table.TableModel; +import java.text.DecimalFormat; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class LuceneGuiFilmeModelHelper extends GuiModelHelper{ + private static final Logger logger = LogManager.getLogger(); + private static final Map PARSER_CONFIG_MAP = new HashMap<>(); + + static { + PARSER_CONFIG_MAP.put(LuceneIndexKeys.FILM_SIZE, new PointsConfig(new DecimalFormat(), Integer.class)); + PARSER_CONFIG_MAP.put(LuceneIndexKeys.FILM_LENGTH, new PointsConfig(new DecimalFormat(), Integer.class)); + } + + + public LuceneGuiFilmeModelHelper(@NotNull FilmActionPanel filmActionPanel, + @NotNull SeenHistoryController historyController, + @NotNull SearchFieldData searchFieldData) { + this.filmActionPanel = filmActionPanel; + this.historyController = historyController; + this.searchFieldData = searchFieldData; + } + + private String getFilterThema() { + String filterThema = filmActionPanel.getViewSettingsPane().themaComboBox.getSelectionModel().getSelectedItem(); + if (filterThema == null) { + filterThema = ""; + } + + return filterThema; + } + + @Override + protected boolean noFiltersAreSet() { + var filmLengthSlider = filmActionPanel.getFilmLengthSlider(); + + return filmActionPanel.getViewSettingsPane().senderCheckList.getCheckModel().isEmpty() + && getFilterThema().isEmpty() + && searchFieldData.isEmpty() + && ((int) filmLengthSlider.getLowValue() == 0) + && ((int) filmLengthSlider.getHighValue() == FilmLengthSlider.UNLIMITED_VALUE) + && !filmActionPanel.isDontShowAbos() + && !filmActionPanel.isShowUnseenOnly() + && !filmActionPanel.isShowOnlyHighQuality() + && !filmActionPanel.isShowSubtitlesOnly() + && !filmActionPanel.isShowLivestreamsOnly() + && !filmActionPanel.isShowNewOnly() + && !filmActionPanel.isShowBookMarkedOnly() + && !filmActionPanel.isDontShowTrailers() + && !filmActionPanel.isDontShowSignLanguage() + && !filmActionPanel.isDontShowAudioVersions() + && filmActionPanel.zeitraumProperty().get().equalsIgnoreCase(ZeitraumSpinner.UNLIMITED_VALUE); + } + + + private TModelFilm performTableFiltering() { + var listeFilme = (IndexedFilmList) Daten.getInstance().getListeFilmeNachBlackList(); + try { + calculateFilmLengthSliderValues(); + + if (filmActionPanel.isShowUnseenOnly()) + historyController.prepareMemoryCache(); + + String searchText = searchFieldData.searchFieldText(); + List resultList; + Stream stream; + + if (noFiltersAreSet()) { + resultList = new ArrayList<>(listeFilme); + stream = resultList.parallelStream(); + } else { + Stopwatch watch2 = Stopwatch.createStarted(); + if (searchText.isEmpty()) { + // search for everything... + searchText = "*:*"; + } + + var analyzer = listeFilme.getAnalyzer(); + var parser = new StandardQueryParser(analyzer); + parser.setPointsConfigMap(PARSER_CONFIG_MAP); + var initialQuery = parser.parse(searchText, LuceneIndexKeys.TITEL); + + BooleanQuery.Builder qb = new BooleanQuery.Builder(); + qb.add(initialQuery, BooleanClause.Occur.MUST); + + //Zeitraum filter on demand... + if (!filmActionPanel.zeitraumProperty().get().equals(ZeitraumSpinner.UNLIMITED_VALUE)) { + try { + qb.add(createZeitraumQuery(listeFilme), BooleanClause.Occur.FILTER); + } catch (Exception ex) { + logger.error("Unable to add zeitraum filter", ex); + } + } + if (filmActionPanel.isShowLivestreamsOnly()) { + addLivestreamQuery(qb, analyzer); + } + if (filmActionPanel.isShowOnlyHighQuality()) { + addHighQualityOnlyQuery(qb, analyzer); + } + if (filmActionPanel.isDontShowTrailers()) { + addNoTrailerTeaserQuery(qb, analyzer); + } + if (filmActionPanel.isDontShowAudioVersions()) { + addNoAudioVersionQuery(qb, analyzer); + } + if (filmActionPanel.isDontShowSignLanguage()) { + addNoSignLanguageQuery(qb, analyzer); + } + if (filmActionPanel.isShowSubtitlesOnly()) { + addSubtitleOnlyQuery(qb, analyzer); + } + if (filmActionPanel.isShowNewOnly()) { + addNewOnlyQuery(qb, analyzer); + } + final ObservableList selectedSenders = filmActionPanel.getViewSettingsPane().senderCheckList.getCheckModel().getCheckedItems(); + if (!selectedSenders.isEmpty()) { + addSenderFilterQuery(qb, analyzer, selectedSenders); + } + + //the complete lucene query... + Query finalQuery = qb.build(); + logger.info("Executing Lucene query: {}", finalQuery.toString()); + + //SEARCH + var searcher = listeFilme.getIndexSearcher(); + var docs = searcher.search(finalQuery, Integer.MAX_VALUE); + var hits = docs.scoreDocs; + + watch2.stop(); + logger.trace("Lucene index search took: {}", watch2); + + Set filmNrSet = new HashSet<>(hits.length); + for (var hit : hits) { + var d = searcher.doc(hit.doc); + filmNrSet.add(Integer.parseInt(d.get(LuceneIndexKeys.ID))); + } + logger.trace("Number of found Lucene index entries: {}", filmNrSet.size()); + stream = listeFilme.parallelStream() + .filter(film -> filmNrSet.contains(film.getFilmNr())); + } + + if (filmActionPanel.isShowBookMarkedOnly()) + stream = stream.filter(DatenFilm::isBookmarked); + if (filmActionPanel.isDontShowAbos()) + stream = stream.filter(film -> film.getAbo() == null); + + final String filterThema = getFilterThema(); + if (!filterThema.isEmpty()) { + stream = stream.filter(film -> film.getThema().equalsIgnoreCase(filterThema)); + } + if (maxLength < FilmLengthSlider.UNLIMITED_VALUE) { + stream = stream.filter(this::maxLengthCheck); + } + if (filmActionPanel.isShowUnseenOnly()) { + stream = stream.filter(this::seenCheck); + } + //perform min length filtering after all others may have reduced the available entries... + stream = stream.filter(this::minLengthCheck); + + resultList = stream.collect(Collectors.toList()); + //logger.trace("Resulting filmlist size after all filters applied: {}", resultList.size()); + stream.close(); + + //adjust initial capacity + var filmModel = new TModelFilm(resultList.size()); + filmModel.addAll(resultList); + + resultList.clear(); + + if (filmActionPanel.isShowUnseenOnly()) + historyController.emptyMemoryCache(); + + return filmModel; + } catch (Exception ex) { + logger.error("Lucene filtering failed!", ex); + SwingUtilities.invokeLater(() -> SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), + "Die Lucene Abfrage ist inkorrekt und führt zu keinen Ergebnissen.", ex)); + return new TModelFilm(); + } + } + + private void addSenderFilterQuery(@NotNull BooleanQuery.Builder qb, @NotNull StandardAnalyzer analyzer, @NotNull List selectedSenders) throws ParseException { + StringBuilder sb = new StringBuilder(); + sb.append("+("); + for (var sender : selectedSenders) { + sb.append("sender:"); + sb.append(sender); + sb.append(" "); + } + sb.append(")"); + var q = new QueryParser(LuceneIndexKeys.SENDER, analyzer).parse(sb.toString()); + qb.add(q, BooleanClause.Occur.FILTER); + } + private void addSubtitleOnlyQuery(@NotNull BooleanQuery.Builder qb, @NotNull StandardAnalyzer analyzer) throws ParseException { + var q = new QueryParser(LuceneIndexKeys.SUBTITLE, analyzer) + .parse("\"true\""); + qb.add(q, BooleanClause.Occur.FILTER); + } + + private void addNoSignLanguageQuery(@NotNull BooleanQuery.Builder qb, @NotNull StandardAnalyzer analyzer) throws ParseException { + var q = new QueryParser(LuceneIndexKeys.SIGN_LANGUAGE, analyzer).parse("\"true\""); + qb.add(q, BooleanClause.Occur.MUST_NOT); + } + + private void addNoAudioVersionQuery(@NotNull BooleanQuery.Builder qb, @NotNull StandardAnalyzer analyzer) throws ParseException { + var q = new QueryParser(LuceneIndexKeys.AUDIOVERSION, analyzer) + .parse("\"true\""); + qb.add(q, BooleanClause.Occur.MUST_NOT); + } + + private void addNoTrailerTeaserQuery(@NotNull BooleanQuery.Builder qb, @NotNull StandardAnalyzer analyzer) throws ParseException { + var q = new QueryParser(LuceneIndexKeys.TRAILER_TEASER, analyzer).parse("\"true\""); + qb.add(q, BooleanClause.Occur.MUST_NOT); + } + + private void addNewOnlyQuery(@NotNull BooleanQuery.Builder qb, @NotNull StandardAnalyzer analyzer) throws ParseException { + var q = new QueryParser(LuceneIndexKeys.NEW, analyzer).parse("\"true\""); + qb.add(q, BooleanClause.Occur.FILTER); + } + + private void addLivestreamQuery(@NotNull BooleanQuery.Builder qb, @NotNull StandardAnalyzer analyzer) throws ParseException { + var q = new QueryParser(LuceneIndexKeys.LIVESTREAM, analyzer).parse("\"true\""); + qb.add(q, BooleanClause.Occur.FILTER); + } + + private void addHighQualityOnlyQuery(@NotNull BooleanQuery.Builder qb, @NotNull StandardAnalyzer analyzer) throws ParseException { + var q = new QueryParser(LuceneIndexKeys.HIGH_QUALITY, analyzer).parse("\"true\""); + qb.add(q, BooleanClause.Occur.FILTER); + } + + private Query createZeitraumQuery(@NotNull IndexedFilmList listeFilme) throws ParseException { + var numDays = Integer.parseInt(filmActionPanel.zeitraumProperty().get()); + var to_Date = LocalDateTime.now(); + var from_Date = to_Date.minusDays(numDays); + var utcZone = ZoneId.of("UTC"); + //[20190101 TO 20190801] + var toStr = DateTools.timeToString(to_Date.atZone(utcZone).toInstant().toEpochMilli(), + DateTools.Resolution.DAY); + var fromStr = DateTools.timeToString(from_Date.atZone(utcZone).toInstant().toEpochMilli(), + DateTools.Resolution.DAY); + String zeitraum = String.format("[%s TO %s]", fromStr, toStr); + return new QueryParser(LuceneIndexKeys.SENDE_DATUM, listeFilme.getAnalyzer()).parse(zeitraum); + } + + @Override + public TableModel getFilteredTableModel() { + var listeFilme = (IndexedFilmList) Daten.getInstance().getListeFilmeNachBlackList(); + TModelFilm filmModel; + + if (!listeFilme.isEmpty()) { + if (noFiltersAreSet()) { + //adjust initial capacity + filmModel = new TModelFilm(listeFilme.size()); + filmModel.addAll(listeFilme); + } else { + filmModel = performTableFiltering(); + } + } else + return new TModelFilm(); + + return filmModel; + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/helpers/SliderRange.kt b/src/main/java/mediathek/gui/tabs/tab_film/helpers/SliderRange.kt new file mode 100644 index 0000000000..546cf389d6 --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_film/helpers/SliderRange.kt @@ -0,0 +1,4 @@ +package mediathek.gui.tabs.tab_film.helpers + +@JvmRecord +data class SliderRange(val minLengthInSeconds: Long, val maxLengthInSeconds: Long) diff --git a/src/main/java/mediathek/gui/tasks/BlacklistFilterWorker.java b/src/main/java/mediathek/gui/tasks/BlacklistFilterWorker.java new file mode 100644 index 0000000000..313fa84dfb --- /dev/null +++ b/src/main/java/mediathek/gui/tasks/BlacklistFilterWorker.java @@ -0,0 +1,23 @@ +package mediathek.gui.tasks; + +import mediathek.config.Daten; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class BlacklistFilterWorker extends SwingWorker { + + public BlacklistFilterWorker(@NotNull JLabel progLabel, @NotNull JProgressBar progressBar) { + SwingUtilities.invokeLater(() -> { + progLabel.setText("Blacklist anwenden"); + progressBar.setIndeterminate(true); + }); + } + + @Override + protected Void doInBackground() { + Daten.getInstance().getListeBlacklist().filterListe(); + + return null; + } +} diff --git a/src/main/java/mediathek/gui/tasks/FilmlistWriterWorker.kt b/src/main/java/mediathek/gui/tasks/FilmlistWriterWorker.kt new file mode 100644 index 0000000000..e343a5d27b --- /dev/null +++ b/src/main/java/mediathek/gui/tasks/FilmlistWriterWorker.kt @@ -0,0 +1,41 @@ +package mediathek.gui.tasks + +import mediathek.config.Daten +import mediathek.config.StandardLocations.getFilmlistFilePathString +import mediathek.filmlisten.writer.FilmListWriter +import java.beans.PropertyChangeEvent +import java.beans.PropertyChangeListener +import javax.swing.JLabel +import javax.swing.JProgressBar +import javax.swing.SwingUtilities +import javax.swing.SwingWorker +import kotlin.math.roundToInt + +class FilmlistWriterWorker(progLabel: JLabel, private val progressBar: JProgressBar) : SwingWorker(), + PropertyChangeListener { + init { + addPropertyChangeListener(this) + SwingUtilities.invokeLater { + progLabel.text = "Schreibe Filmliste" + progressBar.isIndeterminate = false + progressBar.minimum = 0 + progressBar.maximum = 100 + progressBar.value = 0 + } + } + + override fun doInBackground(): Void? { + val writer = FilmListWriter(false) + writer.writeFilmList(getFilmlistFilePathString(), Daten.getInstance().listeFilme) + { prog: Double -> + progress = (100.0 * prog).roundToInt() + } + return null + } + + override fun propertyChange(evt: PropertyChangeEvent) { + if (evt.propertyName.equals("progress", ignoreCase = true)) { + SwingUtilities.invokeLater { progressBar.value = evt.newValue as Int } + } + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/tasks/LuceneIndexKeys.java b/src/main/java/mediathek/gui/tasks/LuceneIndexKeys.java new file mode 100644 index 0000000000..d8ce074efa --- /dev/null +++ b/src/main/java/mediathek/gui/tasks/LuceneIndexKeys.java @@ -0,0 +1,65 @@ +package mediathek.gui.tasks; + +public class LuceneIndexKeys { + /** + * In Abfragen nicht zu verwenden! + */ + public static final String ID = "id"; + /** + * String-Value + */ + public static final String SENDER = "sender"; + /** + * String-Value + */ + public static final String TITEL = "titel"; + /** + * String-Value + */ + public static final String THEMA = "thema"; + /** + * String-Value + */ + public static final String BESCHREIBUNG = "beschreibung"; + /** + * Boolean Key ist nur vorhanden, wenn Bedingung erfüllt. Dann "true" + */ + public static final String LIVESTREAM = "livestream"; + /** + * Boolean Key ist nur vorhanden, wenn Bedingung erfüllt. Dann "true" + */ + public static final String HIGH_QUALITY = "highquality"; + /** + * Boolean Key ist nur vorhanden, wenn Bedingung erfüllt. Dann "true" + */ + public static final String SUBTITLE = "untertitel"; + /** + * Boolean Key ist nur vorhanden, wenn Bedingung erfüllt. Dann "true" + */ + public static final String TRAILER_TEASER = "trailerteaser"; + /** + * Boolean Key ist nur vorhanden, wenn Bedingung erfüllt. Dann "true" + */ + public static final String AUDIOVERSION = "audioversion"; + /** + * Boolean Key ist nur vorhanden, wenn Bedingung erfüllt. Dann "true" + */ + public static final String SIGN_LANGUAGE = "signlanguage"; + /** + * Datum im Format "YYYYMMDD". String. + * Nicht existente Werte sind "19000101". + */ + public static final String SENDE_DATUM = "sendedatum"; + /** + * Boolean Key ist nur vorhanden, wenn Bedingung erfüllt. Dann "true" + */ + public static final String NEW = "neu"; + /** + * Filmlänge in Sekunde. Integer-Value. 0 wenn nicht vorhanden. + */ + public static final String FILM_LENGTH = "länge"; + /** + * Filmgröße in Megabytes. Integer-Value. 0 wenn nicht vorhanden. + */ + public static final String FILM_SIZE = "größe"; +} diff --git a/src/main/java/mediathek/gui/tasks/LuceneIndexWorker.java b/src/main/java/mediathek/gui/tasks/LuceneIndexWorker.java new file mode 100644 index 0000000000..9845a55691 --- /dev/null +++ b/src/main/java/mediathek/gui/tasks/LuceneIndexWorker.java @@ -0,0 +1,121 @@ +package mediathek.gui.tasks; + +import com.google.common.base.Stopwatch; +import mediathek.config.Daten; +import mediathek.daten.DatenFilm; +import mediathek.daten.IndexedFilmList; +import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SwingErrorDialog; +import mediathek.tool.datum.DateUtil; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.lucene.document.*; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.search.IndexSearcher; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.io.IOException; + +public class LuceneIndexWorker extends SwingWorker { + private static final Logger logger = LogManager.getLogger(); + private final JProgressBar progressBar; + private final JLabel progLabel; + private int oldProgress = 0; + + public LuceneIndexWorker(@NotNull JLabel progLabel, @NotNull JProgressBar progressBar) { + this.progressBar = progressBar; + this.progLabel = progLabel; + + SwingUtilities.invokeLater(() -> { + progLabel.setText("Blacklist anwenden"); + progressBar.setIndeterminate(true); + }); + } + + private void indexFilm(@NotNull IndexWriter writer, @NotNull DatenFilm film) throws IOException { + var doc = new Document(); + // store fields for debugging, otherwise they should stay disabled + doc.add(new StringField(LuceneIndexKeys.ID, Integer.toString(film.getFilmNr()), Field.Store.YES)); + doc.add(new StringField(LuceneIndexKeys.NEW, Boolean.toString(film.isNew()), Field.Store.NO)); + doc.add(new TextField(LuceneIndexKeys.SENDER, film.getSender(), Field.Store.NO)); + doc.add(new TextField(LuceneIndexKeys.TITEL, film.getTitle(), Field.Store.NO)); + doc.add(new TextField(LuceneIndexKeys.THEMA, film.getThema(), Field.Store.NO)); + doc.add(new IntPoint(LuceneIndexKeys.FILM_LENGTH, film.getFilmLength())); + doc.add(new IntPoint(LuceneIndexKeys.FILM_SIZE, film.getFileSize().toInteger())); + + doc.add(new TextField(LuceneIndexKeys.BESCHREIBUNG, film.getDescription(), Field.Store.NO)); + doc.add(new StringField(LuceneIndexKeys.LIVESTREAM, Boolean.toString(film.isLivestream()), Field.Store.NO)); + doc.add(new StringField(LuceneIndexKeys.HIGH_QUALITY, Boolean.toString(film.isHighQuality()), Field.Store.NO)); + doc.add(new StringField(LuceneIndexKeys.SUBTITLE, Boolean.toString(film.hasSubtitle() || film.hasBurnedInSubtitles()), Field.Store.NO)); + doc.add(new StringField(LuceneIndexKeys.TRAILER_TEASER, Boolean.toString(film.isTrailerTeaser()), Field.Store.NO)); + doc.add(new StringField(LuceneIndexKeys.AUDIOVERSION, Boolean.toString(film.isAudioVersion()), Field.Store.NO)); + doc.add(new StringField(LuceneIndexKeys.SIGN_LANGUAGE, Boolean.toString(film.isSignLanguage()), Field.Store.NO)); + + addSendeDatum(doc, film); + + writer.addDocument(doc); + } + + private void addSendeDatum(@NotNull Document doc, @NotNull DatenFilm film) { + String sendeDatumStr = DateTools.timeToString(DateUtil.convertFilmDateToLuceneDate(film), + DateTools.Resolution.DAY); + doc.add(new StringField(LuceneIndexKeys.SENDE_DATUM, sendeDatumStr, Field.Store.NO)); + } + + @Override + protected Void doInBackground() { + var filmListe = (IndexedFilmList) Daten.getInstance().getListeFilmeNachBlackList(); + SwingUtilities.invokeLater(() -> { + progLabel.setText("Indiziere Filme"); + progressBar.setMinimum(0); + progressBar.setMaximum(100); + progressBar.setValue(0); + progressBar.setIndeterminate(false); + }); + + //index filmlist after blacklist only + var writer = filmListe.getWriter(); + var totalSize = (float) filmListe.size(); + + try { + int counter = 0; + Stopwatch watch = Stopwatch.createStarted(); + //for safety delete all entries + writer.deleteAll(); + + for (var film : filmListe) { + counter++; + indexFilm(writer, film); + + final var progress = (int) (100.0f * (counter / totalSize)); + if (progress != oldProgress) { + oldProgress = progress; + SwingUtilities.invokeLater(() -> progressBar.setValue(progress)); + } + } + writer.commit(); + watch.stop(); + logger.trace("Lucene index creation took {}", watch); + + var reader = filmListe.getReader(); + if (reader != null) { + reader.close(); + } + reader = DirectoryReader.open(filmListe.getLuceneDirectory()); + filmListe.setReader(reader); + + filmListe.setIndexSearcher(new IndexSearcher(reader)); + } catch (Exception ex) { + SwingUtilities.invokeLater(() -> { + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), + "Fehler bei der Erstellung des Filmindex.\nDas Programm wird beendet da es nicht lauffähig ist.", ex); + MediathekGui.ui().quitApplication(); + }); + } + + return null; + } + +} diff --git a/src/main/java/mediathek/gui/tasks/RefreshAboWorker.java b/src/main/java/mediathek/gui/tasks/RefreshAboWorker.java new file mode 100644 index 0000000000..5b5288d93b --- /dev/null +++ b/src/main/java/mediathek/gui/tasks/RefreshAboWorker.java @@ -0,0 +1,24 @@ +package mediathek.gui.tasks; + +import mediathek.config.Daten; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class RefreshAboWorker extends SwingWorker { + + public RefreshAboWorker(@NotNull JLabel progLabel, @NotNull JProgressBar progressBar) { + SwingUtilities.invokeLater(() -> { + progLabel.setText("Abos eintragen"); + progressBar.setIndeterminate(true); + }); + } + + @Override + protected Void doInBackground() { + var daten = Daten.getInstance(); + daten.getListeAbo().setAboFuerFilm(daten.getListeFilme(), false); + + return null; + } +} diff --git a/src/main/java/mediathek/gui/toolbar/FXDownloadToolBar.java b/src/main/java/mediathek/gui/toolbar/FXDownloadToolBar.java deleted file mode 100644 index 58d0cacb6f..0000000000 --- a/src/main/java/mediathek/gui/toolbar/FXDownloadToolBar.java +++ /dev/null @@ -1,45 +0,0 @@ -package mediathek.gui.toolbar; - -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.scene.control.Button; -import javafx.scene.control.ToolBar; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.IOException; -import java.net.URL; - -public class FXDownloadToolBar extends ToolBar { - private static final Logger logger = LogManager.getLogger(FXDownloadToolBar.class); - @FXML - public Button btnUpdateDownloads; - @FXML - public Button btnFilmInfo; - @FXML - public Button btnStartAllDownloads; - @FXML - public Button btnPlayFilm; - @FXML - public Button btnZurueckstellen; - @FXML - public Button btnRemoveDownload; - @FXML - public Button btnCleanup; - @FXML - public Button btnFilter; - - public FXDownloadToolBar() { - super(); - - try { - URL url = getClass().getResource("/mediathek/res/programm/fxml/download_toolbar.fxml"); - FXMLLoader fxmlLoader = new FXMLLoader(url); - fxmlLoader.setRoot(this); - fxmlLoader.setController(this); - fxmlLoader.load(); - } catch (IOException e) { - logger.error("Failed to load FXML!", e); - } - } -} diff --git a/src/main/java/mediathek/javafx/AppTerminationIndefiniteProgress.java b/src/main/java/mediathek/javafx/AppTerminationIndefiniteProgress.java deleted file mode 100644 index b86858b5a8..0000000000 --- a/src/main/java/mediathek/javafx/AppTerminationIndefiniteProgress.java +++ /dev/null @@ -1,50 +0,0 @@ -package mediathek.javafx; - -import javafx.application.Platform; -import javafx.embed.swing.JFXPanel; -import javafx.scene.Scene; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressIndicator; -import net.miginfocom.layout.AC; -import net.miginfocom.layout.CC; -import net.miginfocom.layout.LC; -import org.tbee.javafx.scene.layout.MigPane; - -/** - * This will display a JFXPanel with a indefinite progress indicator and some status - * messages used as a glass pane overlay during app termination. - */ -public class AppTerminationIndefiniteProgress extends JFXPanel { - private final boolean willBeShutDown; - private Label lblMessage; - - public AppTerminationIndefiniteProgress(boolean willbeShutDown) { - super(); - this.willBeShutDown = willbeShutDown; - - Platform.runLater(this::initFX); - } - - public void setMessage(String text) { - Platform.runLater(() -> lblMessage.setText(text)); - } - - private void initFX() { - setScene(createScene()); - } - - private Scene createScene() { - var migPane = new MigPane(new LC().hideMode(3), - new AC().fill().fill(),new AC()); - migPane.add(new ProgressIndicator(),new CC().cell(0,0).span(1,3)); - lblMessage = new Label("Warte auf Abschluss der Downloads..."); - migPane.add(lblMessage, new CC().cell(1,0)); - if (willBeShutDown) { - Label lblShutdown = new Label("Der Rechner wird danach heruntergefahren."); - migPane.add(lblShutdown, new CC().cell(1,1)); - } - migPane.add(new Label("Sie können den Vorgang mit Escape abbrechen."), new CC().cell(1,2)); - - return new Scene(migPane); - } -} diff --git a/src/main/java/mediathek/javafx/AustrianVlcCheck.kt b/src/main/java/mediathek/javafx/AustrianVlcCheck.kt index fe3137badb..35a08e311e 100644 --- a/src/main/java/mediathek/javafx/AustrianVlcCheck.kt +++ b/src/main/java/mediathek/javafx/AustrianVlcCheck.kt @@ -7,7 +7,7 @@ import javafx.scene.control.Hyperlink import javafx.scene.control.Label import javafx.scene.layout.FlowPane import mediathek.config.Konstanten -import mediathek.daten.GeoblockingField +import mediathek.daten.Country import mediathek.gui.actions.UrlHyperlinkAction import mediathek.mainwindow.MediathekGui import mediathek.tool.ApplicationConfiguration @@ -43,8 +43,7 @@ class AustrianVlcCheck { logger.trace("ORF setup tutorial display check started") if (config.getBoolean(ApplicationConfiguration.APPLICATION_SHOW_ORF_CONFIG_HELP, true)) { //we haven´t shown the config help dialog before - val location = config.getString(ApplicationConfiguration.GEO_LOCATION, "") - if (location == GeoblockingField.GEO_AT) { + if (ApplicationConfiguration.getInstance().geographicLocation == Country.AT) { Platform.runLater { val alert = Alert(Alert.AlertType.INFORMATION) alert.title = Konstanten.PROGRAMMNAME diff --git a/src/main/java/mediathek/javafx/CenteredBorderPane.java b/src/main/java/mediathek/javafx/CenteredBorderPane.java deleted file mode 100644 index 98954f5865..0000000000 --- a/src/main/java/mediathek/javafx/CenteredBorderPane.java +++ /dev/null @@ -1,15 +0,0 @@ -package mediathek.javafx; - -import javafx.scene.Node; -import javafx.scene.layout.BorderPane; - -/** - * A BorderPane subclass which automatically centers nodes - */ -public class CenteredBorderPane extends BorderPane { - public CenteredBorderPane(Node node) { - super(); - setCenter(node); - setCenterShape(true); - } -} diff --git a/src/main/java/mediathek/javafx/FilmListFilterTask.java b/src/main/java/mediathek/javafx/FilmListFilterTask.java deleted file mode 100644 index cac30c79a5..0000000000 --- a/src/main/java/mediathek/javafx/FilmListFilterTask.java +++ /dev/null @@ -1,43 +0,0 @@ -package mediathek.javafx; - -import javafx.concurrent.Task; -import mediathek.config.Daten; -import mediathek.filmeSuchen.ListenerFilmeLadenEvent; -import mediathek.gui.messages.FilmListReadStopEvent; -import mediathek.tool.MessageBus; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.swing.*; - -public class FilmListFilterTask extends Task { - private final Daten daten = Daten.getInstance(); - private final boolean submitEvent; - private static final Logger logger = LogManager.getLogger(FilmListFilterTask.class); - - public FilmListFilterTask(boolean submitEvent) { - this.submitEvent = submitEvent; - } - - @Override - protected Void call() { - logger.trace("FilmListFilterTask started"); - - if (submitEvent) - MessageBus.getMessageBus().publishAsync(new FilmListReadStopEvent()); - - updateMessage("Abos eintragen"); - updateProgress(-1, 4); - daten.getListeAbo().setAboFuerFilm(daten.getListeFilme(), false); - - updateMessage("Alle Filter anwenden"); - updateProgress(-1, 4); - daten.getListeBlacklist().filterListe(); - - SwingUtilities.invokeLater(() -> daten.getFilmeLaden().notifyFertig(new ListenerFilmeLadenEvent("", "", 100, 100, false))); - - logger.trace("FilmListFilterTask finished"); - - return null; - } -} diff --git a/src/main/java/mediathek/javafx/FilmListNetworkReaderTask.java b/src/main/java/mediathek/javafx/FilmListNetworkReaderTask.java deleted file mode 100644 index e7c34136a0..0000000000 --- a/src/main/java/mediathek/javafx/FilmListNetworkReaderTask.java +++ /dev/null @@ -1,24 +0,0 @@ -package mediathek.javafx; - -import javafx.concurrent.Task; -import mediathek.config.Daten; -import mediathek.tool.FilmListUpdateType; -import mediathek.tool.GuiFunktionen; - -public class FilmListNetworkReaderTask extends Task { - - @Override - protected Void call() { - final Daten daten = Daten.getInstance(); - - updateProgress(-1, 4); - updateMessage("Prüfe Alter der Filmliste"); - - if (GuiFunktionen.getFilmListUpdateType() == FilmListUpdateType.AUTOMATIC && daten.getListeFilme().needsUpdate()) { - updateMessage("Lade Filmliste Netzwerk"); - daten.getFilmeLaden().loadFilmlist("", true); - } - - return null; - } -} diff --git a/src/main/java/mediathek/javafx/FilmListReaderTask.java b/src/main/java/mediathek/javafx/FilmListReaderTask.java deleted file mode 100644 index c7ca84470b..0000000000 --- a/src/main/java/mediathek/javafx/FilmListReaderTask.java +++ /dev/null @@ -1,32 +0,0 @@ -package mediathek.javafx; - -import javafx.concurrent.Task; -import mediathek.config.Daten; -import mediathek.config.StandardLocations; -import mediathek.filmlisten.reader.FilmListReader; -import mediathek.gui.messages.FilmListReadStartEvent; -import mediathek.tool.ApplicationConfiguration; -import mediathek.tool.MessageBus; - -public class FilmListReaderTask extends Task { - private final Daten daten; - - public FilmListReaderTask() { - super(); - daten = Daten.getInstance(); - } - - @Override - protected Void call() { - MessageBus.getMessageBus().publishAsync(new FilmListReadStartEvent()); - - updateProgress(-1, 4); - updateMessage("Lese lokale Filmliste"); - try (FilmListReader reader = new FilmListReader()) { - final int num_days = ApplicationConfiguration.getConfiguration().getInt(ApplicationConfiguration.FilmList.LOAD_NUM_DAYS,0); - reader.readFilmListe(StandardLocations.getFilmlistFilePath(), daten.getListeFilme(), num_days); - } - - return null; - } -} diff --git a/src/main/java/mediathek/javafx/GarbageCollectionButton.java b/src/main/java/mediathek/javafx/GarbageCollectionButton.java deleted file mode 100644 index 92f4016540..0000000000 --- a/src/main/java/mediathek/javafx/GarbageCollectionButton.java +++ /dev/null @@ -1,21 +0,0 @@ -package mediathek.javafx; - -import javafx.scene.control.Button; -import javafx.scene.control.Tooltip; -import org.controlsfx.glyphfont.FontAwesome; -import org.controlsfx.glyphfont.GlyphFont; -import org.controlsfx.glyphfont.GlyphFontRegistry; - -/** - * a JavaFX button which will simply perform the garbage collection when clicked - */ -public class GarbageCollectionButton extends Button { - private static final GlyphFont fontAwesome = GlyphFontRegistry.font("FontAwesome"); - - public GarbageCollectionButton() { - super("", fontAwesome.create(FontAwesome.Glyph.RECYCLE)); - setText(""); - setTooltip(new Tooltip("Garbage Collection durchführen")); - setOnAction(e -> System.gc()); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/AboLabel.java b/src/main/java/mediathek/javafx/InfoLabel/AboLabel.java deleted file mode 100644 index f481f033d2..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/AboLabel.java +++ /dev/null @@ -1,16 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; - -public class AboLabel extends Label { - public AboLabel() { - setTooltip(new Tooltip("Anzahl der Abos in der Downloadliste")); - } - - public void updateLabel(DownloadStartInfo info) { - String abo = (info.num_abos == 1) ? "1 Abo" : info.num_abos + " Abos"; - setText(abo); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/ActiveDownloadsLabel.java b/src/main/java/mediathek/javafx/InfoLabel/ActiveDownloadsLabel.java deleted file mode 100644 index 2e3dcf04ae..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/ActiveDownloadsLabel.java +++ /dev/null @@ -1,26 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.config.Daten; -import mediathek.daten.DownloadStartInfo; - -public class ActiveDownloadsLabel extends Label { - public ActiveDownloadsLabel() { - setTooltip(new Tooltip("Anzahl der aktiven Downloads")); - } - - public void updateLabel(Daten daten, DownloadStartInfo info) { - String numDownloads; - - if (info.hasValues()) { - numDownloads = (info.running == 1) ? "1 läuft" : info.running + " laufen"; - - if (info.running > 0) { - numDownloads += " (" + daten.getDownloadInfos().getBandwidthStr() + ')'; - } - setText(numDownloads); - } else - setText(""); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/ErrorLabel.java b/src/main/java/mediathek/javafx/InfoLabel/ErrorLabel.java deleted file mode 100644 index 5c04f45940..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/ErrorLabel.java +++ /dev/null @@ -1,21 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; - -public class ErrorLabel extends Label { - public ErrorLabel() { - setTooltip(new Tooltip("Anzahl der fehlerhaften Downloads")); - } - - public void updateLabel(DownloadStartInfo info) { - if (info.hasValues()) { - String textLinks = ""; - - if (info.error > 0) - textLinks += info.error + " fehlerhaft"; - setText(textLinks); - } - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/FinishedLabel.java b/src/main/java/mediathek/javafx/InfoLabel/FinishedLabel.java deleted file mode 100644 index 4db71d1736..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/FinishedLabel.java +++ /dev/null @@ -1,19 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; - -public class FinishedLabel extends Label { - public FinishedLabel() { - setTooltip(new Tooltip("Anzahl der abgeschlossenen Downloads")); - } - - public void updateLabel(DownloadStartInfo info) { - if (info.hasValues()) { - String fin = (info.finished == 1) ? "1 fertig" : info.finished + " fertig"; - setText(fin); - } else - setText(""); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/GesamtdownloadsLabel.java b/src/main/java/mediathek/javafx/InfoLabel/GesamtdownloadsLabel.java deleted file mode 100644 index ffba5dfae9..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/GesamtdownloadsLabel.java +++ /dev/null @@ -1,22 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; -import mediathek.daten.ListeDownloads; - -public class GesamtdownloadsLabel extends Label { - public GesamtdownloadsLabel() { - setTooltip(new Tooltip("Gesamtzahl aller Downloads")); - } - - public void updateLabel(ListeDownloads listeDownloads, DownloadStartInfo info) { - final int anz = listeDownloads.size(); - final int diff = anz - info.total_starts; - String download = "Gesamtdownloads: " + anz; - if (diff >= 1) { - download += " (" + diff + " zurückgestellt)"; - } - setText(download); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/NumDownloadsLabel.java b/src/main/java/mediathek/javafx/InfoLabel/NumDownloadsLabel.java deleted file mode 100644 index 61ce1a78ca..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/NumDownloadsLabel.java +++ /dev/null @@ -1,16 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; - -public class NumDownloadsLabel extends Label { - public NumDownloadsLabel() { - setTooltip(new Tooltip("Anzahl der manuellen Downloads in der Downloadliste")); - } - - public void updateLabel(DownloadStartInfo info) { - String numDownloads = (info.num_downloads == 1) ? "1 Download" : info.num_downloads + " Downloads"; - setText(numDownloads); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/WaitingLabel.java b/src/main/java/mediathek/javafx/InfoLabel/WaitingLabel.java deleted file mode 100644 index 63428cc7fb..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/WaitingLabel.java +++ /dev/null @@ -1,19 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; - -public class WaitingLabel extends Label { - public WaitingLabel() { - setTooltip(new Tooltip("Anzahl der wartenden Downloads")); - } - - public void updateLabel(DownloadStartInfo info) { - if (info.hasValues()) { - String waiting = (info.initialized == 1) ? "1 wartet" : info.initialized + " warten"; - setText(waiting); - } else - setText(""); - } -} diff --git a/src/main/java/mediathek/javafx/MemoryMonitor.java b/src/main/java/mediathek/javafx/MemoryMonitor.java deleted file mode 100644 index 1ce21da80c..0000000000 --- a/src/main/java/mediathek/javafx/MemoryMonitor.java +++ /dev/null @@ -1,145 +0,0 @@ -package mediathek.javafx; - -import javafx.animation.*; -import javafx.beans.binding.NumberBinding; -import javafx.beans.property.LongProperty; -import javafx.beans.property.SimpleLongProperty; -import javafx.geometry.Insets; -import javafx.scene.Scene; -import javafx.scene.chart.LineChart; -import javafx.scene.chart.NumberAxis; -import javafx.scene.chart.XYChart; -import javafx.scene.control.Label; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.*; -import javafx.scene.paint.Color; -import javafx.stage.Stage; -import javafx.stage.StageStyle; -import javafx.util.Duration; -import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.tool.FileUtils; -import org.apache.commons.lang3.SystemUtils; - -import java.util.concurrent.atomic.AtomicInteger; - -public class MemoryMonitor extends Stage { - private static final int TIMELINE_SIZE = 60; - private final AtomicInteger time = new AtomicInteger(); - private final XYChart.Series series = new XYChart.Series<>(); - private Timeline updateMemoryTimer; - private LongProperty totalMemory; - private LongProperty freeMemory; - private LongProperty maxMemory; - private NumberBinding usedMemory; - private LineChart chart; - - public MemoryMonitor() { - super(); - initComponents(); - } - - private void initComponents() { - setTitle("Speicherverbrauch"); - initOwner(JFXHiddenApplication.getPrimaryStage()); - getIcons().add(JFXHiddenApplication.getApplicationImage()); - setAlwaysOnTop(true); - if (SystemUtils.IS_OS_MAC_OSX) - initStyle(StageStyle.UTILITY); - - createPropertiesAndBindings(); - - Scene scene = new Scene(createMemoryMonitor()); - setScene(scene); - - setOnHiding(e -> updateMemoryTimer.stop()); - setOnShowing(e -> updateMemoryTimer.play()); - - addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { - final Animation animation = new Transition() { - { - setCycleDuration(Duration.millis(1000)); - setInterpolator(Interpolator.EASE_OUT); - } - - @Override - protected void interpolate(double frac) { - Color vColor = new Color(1, 0, 0, 1 - frac); - chart.setBackground(new Background(new BackgroundFill(vColor, CornerRadii.EMPTY, Insets.EMPTY))); - } - }; - animation.play(); - System.gc(); - }); - } - - private long toMegabytes(long bytes) { - return bytes / FileUtils.ONE_MB; - } - - private void createPropertiesAndBindings() { - totalMemory = new SimpleLongProperty(toMegabytes(Runtime.getRuntime().totalMemory())); - freeMemory = new SimpleLongProperty(toMegabytes(Runtime.getRuntime().freeMemory())); - maxMemory = new SimpleLongProperty(toMegabytes(Runtime.getRuntime().maxMemory())); - - usedMemory = totalMemory.subtract(freeMemory); - } - - private void createUpdateTimer() { - updateMemoryTimer = new Timeline(new KeyFrame(Duration.seconds(1), event -> { - totalMemory.set(toMegabytes(Runtime.getRuntime().totalMemory())); - freeMemory.set(toMegabytes(Runtime.getRuntime().freeMemory())); - maxMemory.set(toMegabytes(Runtime.getRuntime().maxMemory())); - - series.getData().add(new XYChart.Data<>(time.incrementAndGet(), usedMemory.getValue())); - if (series.getData().size() > TIMELINE_SIZE) { - series.getData().subList(0, series.getData().size() - TIMELINE_SIZE).clear(); - } - })); - updateMemoryTimer.setCycleCount(Animation.INDEFINITE); - updateMemoryTimer.play(); - } - - private Pane createMemoryMonitor() { - series.setName("Speicherverbrauch (MByte)"); - - createUpdateTimer(); - chart = createChart(); - - return new BorderPane(chart, createLabels(), null, null, null); - } - - private Pane createLabels() { - Label lblUsed = new Label(); - lblUsed.textProperty().bind(usedMemory.asString("Used: %,d")); - - Label lblFree = new Label(); - lblFree.textProperty().bind(freeMemory.asString("Free: %,d")); - - Label lblTotal = new Label(); - lblTotal.textProperty().bind(totalMemory.asString("Total: %,d")); - - Label lblMax = new Label(); - lblMax.textProperty().bind(maxMemory.asString("Max: %,d")); - - HBox labels = new HBox(lblUsed, lblFree, lblTotal, lblMax); - labels.setSpacing(10d); - - return labels; - } - - private LineChart createChart() { - NumberAxis xAxis = new NumberAxis(); - xAxis.setLabel("Laufzeit"); - xAxis.setForceZeroInRange(false); - - NumberAxis yAxis = new NumberAxis(); - yAxis.setLabel("Speicher"); - - LineChart chart = new LineChart<>(xAxis, yAxis); - chart.setAnimated(false); - chart.getData().add(series); - chart.createSymbolsProperty().setValue(false); - - return chart; - } -} diff --git a/src/main/java/mediathek/javafx/SelectedItemsLabel.java b/src/main/java/mediathek/javafx/SelectedItemsLabel.java deleted file mode 100644 index 60c2aba45a..0000000000 --- a/src/main/java/mediathek/javafx/SelectedItemsLabel.java +++ /dev/null @@ -1,24 +0,0 @@ -package mediathek.javafx; - -import javafx.beans.property.IntegerProperty; -import javafx.geometry.Insets; -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.StackPane; - -/** - * Displays the number of currently selected entries - */ -public class SelectedItemsLabel extends StackPane { - - public SelectedItemsLabel(IntegerProperty selectedItemsProperty) { - super(); - - Label textLabel = new Label(); - textLabel.setTooltip(new Tooltip("Ausgewählte Einträge der aktiven Tabelle")); - textLabel.textProperty().bind(selectedItemsProperty.asString()); - - setMargin(textLabel,new Insets(0,4,0,4)); - getChildren().add(textLabel); - } -} diff --git a/src/main/java/mediathek/javafx/ShutdownDialog.java b/src/main/java/mediathek/javafx/ShutdownDialog.java deleted file mode 100644 index 4adc03084b..0000000000 --- a/src/main/java/mediathek/javafx/ShutdownDialog.java +++ /dev/null @@ -1,101 +0,0 @@ -package mediathek.javafx; - -import javafx.application.Platform; -import javafx.event.Event; -import javafx.scene.Scene; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressBar; -import javafx.scene.control.ProgressIndicator; -import javafx.stage.Stage; -import javafx.stage.StageStyle; -import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.mainwindow.MediathekGui; -import mediathek.tool.ShutdownState; -import org.tbee.javafx.scene.layout.MigPane; - -import java.util.EnumSet; - -/** - * Display a wait dialog with some status message to inform user what is happening currently. - */ -public class ShutdownDialog { - private static final double MAXIMUM_STEPS = EnumSet.allOf(ShutdownState.class).size(); - private final MediathekGui gui; - private Label lblStatusText; - private Stage stage; - private ProgressBar progress; - private boolean hidden; - private double curSteps; - - public ShutdownDialog(MediathekGui gui) { - this.gui = gui; - - Platform.runLater(() -> { - stage = new Stage(); - stage.setOnHidden(e -> hidden = true); - stage.setAlwaysOnTop(true); - stage.initOwner(JFXHiddenApplication.getPrimaryStage()); - stage.setResizable(false); - stage.setOnCloseRequest(Event::consume); - stage.initStyle(StageStyle.UNDECORATED); - stage.setTitle("Programm beenden"); - stage.setScene(createScene()); - }); - - } - - public void show() { - Platform.runLater(() -> { - stage.show(); - stage.centerOnScreen(); - }); - gui.setEnabled(false); - } - - public void hide() { - Platform.runLater(() -> { - if (!hidden) - stage.hide(); - }); - gui.setEnabled(true); - } - - public void setStatusText(ShutdownState state) { - Platform.runLater(() -> { - curSteps++; - final double p = (curSteps / MAXIMUM_STEPS); - progress.setProgress(p); - lblStatusText.setText(state.toString()); - }); -/* - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } -*/ - } - - private Scene createScene() { - MigPane migPane = new MigPane( - "hidemode 3", - "[fill]" + - "[fill]", - "[]" + - "[]" + - "[]"); - - progress = new ProgressBar(); - progress.setProgress(0d); - progress.setPrefWidth(450d); - progress.setMinWidth(350d); - - migPane.add(new ProgressIndicator(), "cell 0 0 1 3"); - lblStatusText = new Label("Offene Operationen müssen noch beendet werden."); - migPane.add(lblStatusText, "cell 1 0"); - migPane.add(progress, "cell 1 1"); - migPane.add(new Label(""), "cell 1 2"); - - return new Scene(migPane); - } -} diff --git a/src/main/java/mediathek/javafx/StatusBarController.java b/src/main/java/mediathek/javafx/StatusBarController.java deleted file mode 100644 index c8287074c0..0000000000 --- a/src/main/java/mediathek/javafx/StatusBarController.java +++ /dev/null @@ -1,133 +0,0 @@ -package mediathek.javafx; - -import javafx.application.Platform; -import javafx.collections.ObservableList; -import javafx.scene.Node; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressBar; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Region; -import mediathek.config.Config; -import mediathek.config.Daten; -import mediathek.filmeSuchen.ListenerFilmeLaden; -import mediathek.filmeSuchen.ListenerFilmeLadenEvent; -import mediathek.javafx.filmlist.FilmListInfoPane; -import mediathek.tool.MessageBus; -import org.controlsfx.control.StatusBar; - -public class StatusBarController { - private final Label progressLabel = new Label(""); - private final ProgressBar progressBar = new ProgressBar(); - /** - * The new javafx based status bar - */ - private final StatusBar statusBar = new StatusBar(); - private final FilmListInfoPane filmListInfoPane; - private final GarbageCollectionButton btnGc = new GarbageCollectionButton(); - private Pane progressPane; - - public StatusBarController(Daten daten) { - filmListInfoPane = new FilmListInfoPane(daten); - - MessageBus.getMessageBus().subscribe(this); - - createProgressPane(); - - daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { - @Override - public void start(ListenerFilmeLadenEvent event) { - addProgressItems(); - - if (Config.isDebugModeEnabled()) - Platform.runLater(() -> statusBar.setText(event.senderUrl)); - } - - @Override - public void progress(ListenerFilmeLadenEvent event) { - updateProgressBar(event); - } - - @Override - public void fertig(ListenerFilmeLadenEvent event) { - Platform.runLater(() -> progressBar.setProgress(0d)); - removeProgressItems(); - if (Config.isDebugModeEnabled()) - Platform.runLater(() -> statusBar.setText("")); - } - }); - } - - public StatusBar getStatusBar() { - return statusBar; - } - - private void createProgressPane() { - HBox hb = new HBox(); - hb.setSpacing(5d); - hb.setMinWidth(Region.USE_PREF_SIZE); - hb.getChildren().addAll(new VerticalSeparator(), - new CenteredBorderPane(progressLabel), - new CenteredBorderPane(progressBar) - ); - - progressPane = hb; - } - - private void updateProgressBar(ListenerFilmeLadenEvent event) { - Platform.runLater(() -> { - if (!progressBar.isVisible()) - progressBar.setVisible(true); - - if (event.max == 0 || event.progress == event.max) { - progressBar.setProgress(-1d); - } else { - final double max = event.max; - final double progress = event.progress; - - progressBar.setProgress(progress / max); - } - progressLabel.setText(event.text); - }); - } - - private void addProgressItems() { - Platform.runLater(() -> { - ObservableList rightItems = statusBar.getRightItems(); - //fix strange exception that duplicate was added... - if (!rightItems.contains(progressPane)) - rightItems.add(progressPane); - - }); - } - - private void removeProgressItems() { - Platform.runLater(() -> { - ObservableList rightItems = statusBar.getRightItems(); - rightItems.remove(progressPane); - }); - } - - private void setupLeftPane() { - ObservableList leftItems = statusBar.getLeftItems(); - - if (Config.isDebugModeEnabled()) { - leftItems.add(btnGc); - leftItems.add(new VerticalSeparator()); - } - } - - private void setupRightPane() { - statusBar.getRightItems().add(filmListInfoPane); - } - - public StatusBar createStatusBar() { - //reset text - statusBar.setText(""); - - setupLeftPane(); - setupRightPane(); - - return statusBar; - } -} diff --git a/src/main/java/mediathek/javafx/VerticalSeparator.java b/src/main/java/mediathek/javafx/VerticalSeparator.java deleted file mode 100644 index e8f36c9210..0000000000 --- a/src/main/java/mediathek/javafx/VerticalSeparator.java +++ /dev/null @@ -1,14 +0,0 @@ -package mediathek.javafx; - -import javafx.geometry.Orientation; -import javafx.scene.control.Separator; - -/** - * A JavaFX in default vertical orientation - */ -public class VerticalSeparator extends Separator { - public VerticalSeparator() { - super(); - setOrientation(Orientation.VERTICAL); - } -} diff --git a/src/main/java/mediathek/javafx/bookmark/BookmarkData.java b/src/main/java/mediathek/javafx/bookmark/BookmarkData.java index 14ba211755..4f7004c5b6 100644 --- a/src/main/java/mediathek/javafx/bookmark/BookmarkData.java +++ b/src/main/java/mediathek/javafx/bookmark/BookmarkData.java @@ -37,8 +37,8 @@ public BookmarkData(DatenFilm filmdata) { this.sender = filmdata.getSender(); this.titel = filmdata.getTitle(); this.senddate = filmdata.getSendeDatum(); - this.highQualityUrl = filmdata.getUrlHighQuality(); - this.urlKlein = filmdata.getUrlLowQuality(); + this.highQualityUrl = filmdata.getHighQualityUrl(); + this.urlKlein = filmdata.getLowQualityUrl(); this.filmdata = filmdata; this.willExpire = false; } @@ -56,7 +56,7 @@ public void setThema(String url){} public String getTitel(){ return this.titel; } public void setTitel(String url){ this.titel = url;} - public String getDauer(){ return ((filmdata != null) ? filmdata.getDauer(): ""); } + public String getDauer(){ return ((filmdata != null) ? filmdata.getFilmLengthAsString(): ""); } public void setDauer(String dauer){} public String getDescription(){ return ((filmdata != null) ? filmdata.getDescription(): ""); } @@ -96,7 +96,7 @@ public boolean hasURL() { @JsonIgnore public boolean hasWebURL() { - return (this.filmdata != null && !this.filmdata.getWebsiteLink().isEmpty()); + return (this.filmdata != null && !this.filmdata.getWebsiteUrl().isEmpty()); } /** @@ -132,7 +132,7 @@ public DatenFilm getDatenFilm() { @JsonIgnore public String getWebUrl() { - return (this.filmdata != null) ? this.filmdata.getWebsiteLink() : null; + return (this.filmdata != null) ? this.filmdata.getWebsiteUrl() : null; } @JsonIgnore @@ -161,11 +161,11 @@ public DatenFilm getDataAsDatenFilm() { Film = new DatenFilm(); Film.setThema(getThema()); Film.setTitle(getTitel()); - Film.setUrlNormalQuality(getUrl()); - Film.setUrlHighQuality(getHighQualityUrl()); - Film.setUrlLowQuality(getUrlKlein()); + Film.setNormalQualityUrl(getUrl()); + Film.setHighQualityUrl(getHighQualityUrl()); + Film.setLowQualityUrl(getUrlKlein()); Film.setSender(getSender()); - Film.setDauer(getDauer()); + Film.setFilmLength(getDauer()); } return Film; } diff --git a/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java b/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java index 0c292e982f..2720926a9e 100644 --- a/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java +++ b/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java @@ -31,7 +31,9 @@ import jiconfont.icons.font_awesome.FontAwesome; import jiconfont.javafx.IconNode; import mediathek.config.Daten; +import mediathek.config.MVColor; import mediathek.config.MVConfig; +import mediathek.config.StandardLocations; import mediathek.controller.history.SeenHistoryController; import mediathek.daten.DatenDownload; import mediathek.daten.DatenFilm; @@ -66,7 +68,8 @@ import java.util.concurrent.TimeUnit; import static javafx.scene.input.MouseButton.PRIMARY; -import static mediathek.config.MVColor.*; +import static mediathek.config.MVColor.DOWNLOAD_FEHLER; +import static mediathek.config.MVColor.FILM_HISTORY; /** @@ -81,7 +84,6 @@ public class BookmarkWindowController implements Initializable { private final BookmarkDataList listeBookmarkList; private FilteredList filteredBookmarkList; private Color ColorExpired; - private Color ColorLive; private Background BackgroundSeen; private Background BackgroundSelected; private final SeenHistoryController history = new SeenHistoryController(); @@ -320,7 +322,7 @@ protected void updateItem(BookmarkData data, boolean empty) { } else { setBackground(isSelected() ? BackgroundSelected : data.getSeen() ? BackgroundSeen : Background.EMPTY); // set foreground color: - Color fillcolor = isSelected() ? Color.WHITE : data.isNotInFilmList() ? ColorExpired : data.isLiveStream() ? ColorLive : null; + Color fillcolor = isSelected() ? Color.WHITE : data.isNotInFilmList() ? ColorExpired : null; if (fillcolor != null) { this.getChildren().forEach((n) -> ((Labeled) n).setTextFill(fillcolor)); } @@ -654,7 +656,7 @@ private void updateDisplay() { */ private void saveBookMarkList() { if (listUpdated) { - listeBookmarkList.saveToFile(Daten.getBookmarkFilePath()); + listeBookmarkList.saveToFile(StandardLocations.getBookmarkFilePath()); btnSaveList.setDisable(true); JavaFxUtils.invokeInFxThreadAndWait(() -> lblMessage.setText("Merkliste ist gesichert")); } @@ -840,10 +842,9 @@ private static Color convertMVCAWTColor(MVC mvc) { } private void initSettings() { - Color colorSeen = convertMVCAWTColor(FILM_HISTORY); - Color colorNew = convertMVCAWTColor(FILM_NEU); + var colorSeen = convertMVCAWTColor(FILM_HISTORY); + var colorNew = JavaFxUtils.toFXColor(MVColor.getNewColor()); ColorExpired = convertMVCAWTColor(DOWNLOAD_FEHLER); - ColorLive = convertMVCAWTColor(FILM_LIVESTREAM); BackgroundSeen = new Background(new BackgroundFill(colorSeen, CornerRadii.EMPTY, Insets.EMPTY)); BackgroundSelected = new Background(new BackgroundFill(colorNew, CornerRadii.EMPTY, Insets.EMPTY)); } diff --git a/src/main/java/mediathek/javafx/buttonsPanel/ButtonsPanelController.java b/src/main/java/mediathek/javafx/buttonsPanel/ButtonsPanelController.java deleted file mode 100644 index ba3a8fcb7c..0000000000 --- a/src/main/java/mediathek/javafx/buttonsPanel/ButtonsPanelController.java +++ /dev/null @@ -1,105 +0,0 @@ -package mediathek.javafx.buttonsPanel; - -import javafx.application.Platform; -import javafx.embed.swing.JFXPanel; -import javafx.event.Event; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.TilePane; -import mediathek.config.Daten; -import mediathek.daten.DatenPset; -import mediathek.gui.messages.ProgramSetChangedEvent; -import mediathek.gui.tabs.tab_film.GuiFilme; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.swing.*; -import java.io.IOException; -import java.net.URL; -import java.util.ResourceBundle; - -public class ButtonsPanelController implements Initializable { - private static final Logger logger = LogManager.getLogger(); - @FXML - private Tab buttonsTab; - @FXML - private TilePane tilePane; - - public void setGuiFilme(GuiFilme guiFilme) { - this.guiFilme = guiFilme; - } - - private GuiFilme guiFilme; - - public static ButtonsPanelController install(JFXPanel parent, GuiFilme guiFilme) throws IOException { - logger.trace("install"); - - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(ButtonsPanelController.class.getResource("/mediathek/res/programm/fxml/pset_buttons.fxml")); - - TabPane buttonsPane = loader.load(); - final ButtonsPanelController buttonsController = loader.getController(); - buttonsController.setGuiFilme(guiFilme); - parent.setScene(new Scene(buttonsPane)); - - return buttonsController; - } - - public void setOnCloseRequest(EventHandler e) { - buttonsTab.setOnCloseRequest(e); - } - - public void setupButtonLayout() { - logger.trace("setupButtonLayout called"); - - tilePane.getChildren().clear(); - var listeButton = Daten.listePset.getListeButton(); - final var children = tilePane.getChildren(); - for (var pset : listeButton) { - final var psetName = pset.arr[DatenPset.PROGRAMMSET_NAME]; - final var psetColor = pset.getFarbe(); - if (!pset.isFreeLine()) { - if (pset.isLabel()) { - var l = new Label(psetName); - if (psetColor != null) - l.setTextFill(JavaFxUtils.toFXColor(psetColor)); - children.add(l); - } else { - var b = new Button(psetName); - if (psetColor != null) - b.setBackground(new Background(new BackgroundFill(JavaFxUtils.toFXColor(psetColor),null,null))); - b.setOnAction(e -> { - System.out.println("EXECUTING PSET BUTTON"); - SwingUtilities.invokeLater(() -> guiFilme.playerStarten(pset)); - }); - children.add(b); - } - } - else { - children.add(new Label("")); - } - } - } - - @Handler - private void handleProgramSetChangedEvent(ProgramSetChangedEvent e) { - Platform.runLater(this::setupButtonLayout); - } - - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - MessageBus.getMessageBus().subscribe(this); - } -} diff --git a/src/main/java/mediathek/javafx/descriptionPanel/DescriptionPanelController.java b/src/main/java/mediathek/javafx/descriptionPanel/DescriptionPanelController.java deleted file mode 100644 index 3635e479fe..0000000000 --- a/src/main/java/mediathek/javafx/descriptionPanel/DescriptionPanelController.java +++ /dev/null @@ -1,177 +0,0 @@ -package mediathek.javafx.descriptionPanel; - -import javafx.embed.swing.JFXPanel; -import javafx.event.Event; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.control.*; -import javafx.scene.text.*; -import mediathek.config.Konstanten; -import mediathek.daten.DatenFilm; -import mediathek.gui.actions.UrlHyperlinkAction; -import mediathek.gui.dialog.DialogFilmBeschreibung; -import mediathek.mainwindow.MediathekGui; -import mediathek.tool.GuiFunktionen; -import org.apache.commons.lang3.SystemUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.Optional; - -/** - * The controller for the film description panel - */ -public class DescriptionPanelController { - private static final Logger logger = LogManager.getLogger(DescriptionPanelController.class); - private static final String SPACER = " - "; - @FXML - private Hyperlink websiteLink; - @FXML - private TextFlow textField; - @FXML - private Tab descriptionTab; - @FXML - private ScrollPane scrollPane; - private DatenFilm currentFilm; - private ContextMenu contextMenu; - - public static DescriptionPanelController install(JFXPanel fxDescriptionPanel) throws IOException { - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(Konstanten.FXML_FILM_DESCRIPTION_PANEL_URL); - - TabPane descriptionPane = loader.load(); - final DescriptionPanelController descriptionPanelController = loader.getController(); - descriptionPanelController.setOnCloseRequest(e -> { - SwingUtilities.invokeLater(() -> fxDescriptionPanel.setVisible(false)); - e.consume(); - }); - - fxDescriptionPanel.setScene(new Scene(descriptionPane)); - return descriptionPanelController; - } - - public static String getStringFromTextFlow(TextFlow tf) { - StringBuilder sb = new StringBuilder(); - tf.getChildren().stream() - .filter(t -> Text.class.equals(t.getClass())) - .forEach(t -> sb.append(((Text) t).getText())); - return sb.toString(); - } - - private void setupWebsiteLink() { - websiteLink.setOnAction(e -> { - final var link = currentFilm.getWebsiteLink(); - SwingUtilities.invokeLater(() -> { - try { - UrlHyperlinkAction.openURL(MediathekGui.ui(), link); - } catch (URISyntaxException ex) { - logger.error("Failed to launch web browser for URL: {}", link); - } - }); - }); - - // create context menu - var contextMenu = new ContextMenu(); - var mi = new MenuItem("URL kopieren"); - mi.setOnAction(e -> SwingUtilities.invokeLater(() -> GuiFunktionen.copyToClipboard(currentFilm.getWebsiteLink()))); - contextMenu.getItems().add(mi); - websiteLink.setContextMenu(contextMenu); - } - - private Font getFont() { - /* - Thank you Oracle for not fixing the bold font handling for years in JavaFX... - As of version 16 bold font handling ist STILL BROKEN on macOS. - So we need a little workaround which will not die before 2027 based on bug fix rate - of non-paying customers... - */ - Font defaultFont; - if (SystemUtils.IS_OS_MAC_OSX) { - defaultFont = Font.font("Arial", 14d); - } - else - defaultFont = Font.getDefault(); - - return defaultFont; - } - - public void showFilmDescription(@NotNull Optional optFilm) { - textField.getChildren().clear(); - - optFilm.ifPresentOrElse(film -> { - currentFilm = film; - websiteLink.setVisible(true); - websiteLink.setVisited(false); - websiteLink.setTooltip(new Tooltip(film.getWebsiteLink())); - - Font defaultFont = getFont(); - Text headLine = new Text((film.getSender().isEmpty() ? "" : film.getSender() + SPACER) + film.getThema() + SPACER + film.getTitle()); - headLine.setFont(Font.font(defaultFont.getName(), FontWeight.BOLD, FontPosture.REGULAR, defaultFont.getSize())); - - Text description = new Text(film.getDescription()); - description.setFont(Font.font(defaultFont.getName(), FontWeight.NORMAL, FontPosture.REGULAR, defaultFont.getSize())); - - textField.getChildren().addAll(headLine, - new Text("\n"), - new Text("\n"), - description); - }, () -> { - websiteLink.setTooltip(null); - websiteLink.setVisible(false); - currentFilm = null; - }); - } - - private ContextMenu createContextMenu() { - ContextMenu contextMenu = new ContextMenu(); - - MenuItem edit = new MenuItem("Beschreibung ändern"); - edit.setOnAction(e -> SwingUtilities.invokeLater(() -> { - DialogFilmBeschreibung dialog = new DialogFilmBeschreibung(MediathekGui.ui(), currentFilm); - dialog.setVisible(true); - })); - - MenuItem copyToClipboard = new MenuItem("In Zwischenablage kopieren"); - copyToClipboard.setOnAction(e -> { - var text = getStringFromTextFlow(textField); - GuiFunktionen.copyToClipboard(text); - }); - - var items = contextMenu.getItems(); - items.add(edit); - items.add(new SeparatorMenuItem()); - items.add(copyToClipboard); - - contextMenu.setOnShowing(e -> { - //if there is no film do not offer edit or copy capability - if (currentFilm == null) { - edit.setDisable(true); - copyToClipboard.setDisable(true); - } - else { - edit.setDisable(false); - copyToClipboard.setDisable(false); - } - }); - - return contextMenu; - } - - public void setOnCloseRequest(EventHandler e) { - descriptionTab.setOnCloseRequest(e); - } - - public void initialize() { - websiteLink.setVisible(false); - setupWebsiteLink(); - - contextMenu = createContextMenu(); - scrollPane.setOnContextMenuRequested(event -> contextMenu.show(textField, event.getScreenX(), event.getScreenY())); - } -} diff --git a/src/main/java/mediathek/javafx/downloadtab/DownloadTabInformationLabel.java b/src/main/java/mediathek/javafx/downloadtab/DownloadTabInformationLabel.java deleted file mode 100644 index e2f25c2949..0000000000 --- a/src/main/java/mediathek/javafx/downloadtab/DownloadTabInformationLabel.java +++ /dev/null @@ -1,118 +0,0 @@ -package mediathek.javafx.downloadtab; - -import javafx.application.Platform; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.scene.layout.HBox; -import mediathek.config.Daten; -import mediathek.gui.messages.TimerEvent; -import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; -import mediathek.javafx.CenteredBorderPane; -import mediathek.javafx.InfoLabel.*; -import mediathek.javafx.VerticalSeparator; -import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; - - -public class DownloadTabInformationLabel extends HBox { - private final Daten daten; - private final GesamtdownloadsLabel overallDownloadLabel = new GesamtdownloadsLabel(); - private final AboLabel aboLabel = new AboLabel(); - private final NumDownloadsLabel numDownloadsLabel = new NumDownloadsLabel(); - private final ActiveDownloadsLabel activeDownloadLabel = new ActiveDownloadsLabel(); - private final WaitingLabel waitingLabel = new WaitingLabel(); - private final FinishedLabel finishedLabel = new FinishedLabel(); - private final ErrorLabel errorLabel = new ErrorLabel(); - private final HBox finishedBox = new HBox(); - private final HBox waitingBox = new HBox(); - private final HBox activeBox = new HBox(); - public DownloadTabInformationLabel(Daten daten) { - super(); - this.daten = daten; - - setupListeners(); - initLayout(); - } - - private void setupListeners() { - if (isVisible()) - MessageBus.getMessageBus().subscribe(this); - - visibleProperty().addListener(new ChangeListener<>() { - @Override - public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { - if (newValue) { - MessageBus.getMessageBus().subscribe(this); - } else { - MessageBus.getMessageBus().unsubscribe(this); - } - } - }); - } - - private void initLayout() { - finishedBox.getChildren().addAll(new CenteredBorderPane(finishedLabel), new VerticalSeparator()); - waitingBox.getChildren().addAll(new CenteredBorderPane(waitingLabel), new VerticalSeparator()); - activeBox.getChildren().addAll(new CenteredBorderPane(activeDownloadLabel), new VerticalSeparator()); - - getChildren().addAll(new CenteredBorderPane(overallDownloadLabel), - new VerticalSeparator(), - new CenteredBorderPane(aboLabel), - new VerticalSeparator(), - new CenteredBorderPane(numDownloadsLabel), - new VerticalSeparator(), - activeBox, - waitingBox, - finishedBox, - new CenteredBorderPane(errorLabel)); - } - - @Handler - private void handleLeftDisplayUpdate(UpdateStatusBarLeftDisplayEvent e) { - Platform.runLater(this::getInfoTextDownloads); - } - - @Handler - private void handleTimerEvent(TimerEvent e) { - Platform.runLater(this::getInfoTextDownloads); - } - - private void getInfoTextDownloads() { - final var listeDownloads = daten.getListeDownloads(); - final var info = listeDownloads.getStarts(); - final var children = getChildren(); - - overallDownloadLabel.updateLabel(listeDownloads, info); - aboLabel.updateLabel(info); - numDownloadsLabel.updateLabel(info); - - if (info.running > 0) { - if (!children.contains(activeBox)) - children.add(activeBox); - activeDownloadLabel.updateLabel(daten, info); - } else - children.remove(activeBox); - - if (info.initialized > 0) { - if (!children.contains(waitingBox)) - children.add(waitingBox); - waitingLabel.updateLabel(info); - } else - children.remove(waitingBox); - - if (info.finished > 0) { - if (!children.contains(finishedBox)) - children.add(finishedBox); - finishedLabel.updateLabel(info); - } else - children.remove(finishedBox); - - if (info.error > 0) { - - if (!children.contains(errorLabel)) - children.add(new CenteredBorderPane(errorLabel)); - errorLabel.updateLabel(info); - } else - children.remove(errorLabel); - } -} diff --git a/src/main/java/mediathek/javafx/filmlist/FilmListAgeLabel.kt b/src/main/java/mediathek/javafx/filmlist/FilmListAgeLabel.kt deleted file mode 100644 index f35b536b68..0000000000 --- a/src/main/java/mediathek/javafx/filmlist/FilmListAgeLabel.kt +++ /dev/null @@ -1,53 +0,0 @@ -package mediathek.javafx.filmlist - -import javafx.animation.Animation -import javafx.animation.KeyFrame -import javafx.animation.Timeline -import javafx.scene.control.Tooltip -import javafx.util.Duration -import mediathek.config.Daten -import mediathek.javafx.tool.ComputedLabel - -/** - * Label which will compute the age of the filmlist when updated. - * Update cycle one second. - */ -class FilmListAgeLabel internal constructor() : ComputedLabel() { - private val timeline = Timeline(KeyFrame(Duration.millis(1_000.0), { setAgeToLabel() })) - private var oldAge = FilmListAge(0, 0) - - private fun setAgeToLabel() { - val listAge = calculateAge() - if (listAge != oldAge) { - setComputedText(computeAgeString(listAge)) - oldAge = listAge - } - } - - private data class FilmListAge(val hours: Long, val minutes: Long) - - private fun calculateAge(): FilmListAge { - val duration = java.time.Duration.ofSeconds(Daten.getInstance().listeFilme.metaData().ageInSeconds) - var minutes = duration.toMinutes() - val hours = minutes / 60 - minutes -= hours * 60 - return FilmListAge(hours, minutes) - } - - private fun computeAgeString(age: FilmListAge): String { - return try { - if (age.hours == 0L) - String.format("Alter: %dm", age.minutes) - else - String.format("Alter: %dh %dm", age.hours, age.minutes) - } catch (ex: IllegalArgumentException) { - "Ungültiges Alter" - } - } - - init { - timeline.cycleCount = Animation.INDEFINITE - timeline.play() - tooltip = Tooltip("Alter der Filmliste") - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/javafx/filmlist/FilmListCreationDateLabel.java b/src/main/java/mediathek/javafx/filmlist/FilmListCreationDateLabel.java deleted file mode 100644 index 5b186aa70d..0000000000 --- a/src/main/java/mediathek/javafx/filmlist/FilmListCreationDateLabel.java +++ /dev/null @@ -1,24 +0,0 @@ -package mediathek.javafx.filmlist; - -import mediathek.config.Daten; -import mediathek.javafx.tool.ComputedLabel; - -/** - * Computed label which will display the creation date of the current film list. - */ -class FilmListCreationDateLabel extends ComputedLabel { - private final Daten daten; - - FilmListCreationDateLabel(Daten daten) { - super(); - this.daten = daten; - } - - /** - * Computes and displays the age of the list. - */ - public void computeCreationDate() { - //update text - setComputedText(String.format("Filmliste erstellt: %s Uhr", daten.getListeFilme().metaData().getGenerationDateTimeAsString())); - } -} diff --git a/src/main/java/mediathek/javafx/filmlist/FilmListInfoPane.java b/src/main/java/mediathek/javafx/filmlist/FilmListInfoPane.java deleted file mode 100644 index df16a80ff8..0000000000 --- a/src/main/java/mediathek/javafx/filmlist/FilmListInfoPane.java +++ /dev/null @@ -1,48 +0,0 @@ -package mediathek.javafx.filmlist; - -import javafx.application.Platform; -import javafx.scene.layout.HBox; -import mediathek.config.Daten; -import mediathek.filmeSuchen.ListenerFilmeLaden; -import mediathek.filmeSuchen.ListenerFilmeLadenEvent; -import mediathek.javafx.CenteredBorderPane; -import mediathek.javafx.VerticalSeparator; -import mediathek.tool.MessageBus; - -import javax.swing.*; - -/** - * Pane which will display common information about the current filmlist. - * Also handles updating the subcomponents based on a TimerEvent. - */ -public class FilmListInfoPane extends HBox { - private final FilmListCreationDateLabel filmListCreationDateLabel; - - public FilmListInfoPane(Daten daten) { - super(); - setSpacing(4d); - - SwingUtilities.invokeLater(() -> daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { - @Override - public void start(ListenerFilmeLadenEvent event) { - MessageBus.getMessageBus().unsubscribe(this); - Platform.runLater(() -> setVisible(false)); - } - - @Override - public void fertig(ListenerFilmeLadenEvent event) { - MessageBus.getMessageBus().subscribe(this); - Platform.runLater(() -> { - filmListCreationDateLabel.computeCreationDate(); - setVisible(true); - }); - } - })); - - filmListCreationDateLabel = new FilmListCreationDateLabel(daten); - - getChildren().addAll(new CenteredBorderPane(filmListCreationDateLabel), - new VerticalSeparator(), - new CenteredBorderPane(new FilmListAgeLabel())); - } -} diff --git a/src/main/java/mediathek/javafx/filmtab/FilmInfoLabel.java b/src/main/java/mediathek/javafx/filmtab/FilmInfoLabel.java deleted file mode 100644 index 3885594ec7..0000000000 --- a/src/main/java/mediathek/javafx/filmtab/FilmInfoLabel.java +++ /dev/null @@ -1,51 +0,0 @@ -package mediathek.javafx.filmtab; - -import javafx.scene.control.Label; -import mediathek.config.Daten; -import mediathek.gui.tabs.tab_film.GuiFilme; - -public class FilmInfoLabel extends Label { - private final Daten daten; - private final GuiFilme tabFilme; - - public FilmInfoLabel(Daten daten, GuiFilme tabFilme) { - super(); - this.daten = daten; - this.tabFilme = tabFilme; - } - - private String createFilmLabel(final int rowCount) { - String textLinks; - if (rowCount == 1) - textLinks = "1 Film"; - else - textLinks = rowCount + " Filme"; - - return textLinks; - } - - private int oldGesamt = 0; - private int oldRowCount = 0; - - public void updateValues() { - String textLinks; - final int gesamt = daten.getListeFilme().size(); - final int rowCount = tabFilme.getTableRowCount(); - - if (gesamt == oldGesamt && rowCount == oldRowCount) - return; - - // Anzahl der Filme - if (gesamt == rowCount) { - textLinks = createFilmLabel(rowCount); - } else { - textLinks = createFilmLabel(rowCount); - textLinks += " (Insgesamt: " + gesamt + ")"; - } - - setText(textLinks); - - oldGesamt = gesamt; - oldRowCount = rowCount; - } -} diff --git a/src/main/java/mediathek/javafx/filmtab/FilmTabDownloadInformationLabel.java b/src/main/java/mediathek/javafx/filmtab/FilmTabDownloadInformationLabel.java deleted file mode 100644 index b7f5b19746..0000000000 --- a/src/main/java/mediathek/javafx/filmtab/FilmTabDownloadInformationLabel.java +++ /dev/null @@ -1,45 +0,0 @@ -package mediathek.javafx.filmtab; - -import javafx.scene.control.Label; -import mediathek.config.Daten; - -public class FilmTabDownloadInformationLabel extends Label { - private final Daten daten; - - public FilmTabDownloadInformationLabel(Daten daten) { - super(); - this.daten = daten; - } - - public void setInfoFilme() { - setText(getInfoTextDownloads()); - } - - private String getInfoTextDownloads() { - String textLinks; - final var listeDownloads = daten.getListeDownloads(); - final var info = listeDownloads.getStarts(); - final int anz = listeDownloads.size(); - - textLinks = (anz == 1) ? "1 Download" : anz + " Downloads"; - - if (info.hasValues()) { - textLinks += ": "; - - textLinks += (info.running == 1) ? "1 läuft" : info.running + " laufen"; - - if (info.running > 0) - textLinks += " (" + daten.getDownloadInfos().getBandwidthStr() + ')'; - - textLinks += (info.initialized == 1) ? ", 1 wartet" : ", " + info.initialized + " warten"; - - if (info.finished > 0) - textLinks += (info.finished == 1) ? ", 1 fertig" : ", " + info.finished + " fertig"; - - if (info.error > 0) - textLinks += (info.error == 1) ? ", 1 fehlerhaft" : ", " + info.error + " fehlerhaft"; - } - - return textLinks; - } -} diff --git a/src/main/java/mediathek/javafx/filmtab/FilmTabInfoPane.java b/src/main/java/mediathek/javafx/filmtab/FilmTabInfoPane.java deleted file mode 100644 index e285a860fa..0000000000 --- a/src/main/java/mediathek/javafx/filmtab/FilmTabInfoPane.java +++ /dev/null @@ -1,64 +0,0 @@ -package mediathek.javafx.filmtab; - -import javafx.application.Platform; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.scene.layout.HBox; -import mediathek.config.Daten; -import mediathek.gui.messages.DownloadInfoUpdateAvailableEvent; -import mediathek.gui.messages.TimerEvent; -import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; -import mediathek.gui.tabs.tab_film.GuiFilme; -import mediathek.javafx.CenteredBorderPane; -import mediathek.javafx.VerticalSeparator; -import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; - -public class FilmTabInfoPane extends HBox { - private final FilmTabDownloadInformationLabel downloadInformationLabel; - private final FilmInfoLabel filmInfoLabel; - - public FilmTabInfoPane(Daten daten, GuiFilme tabFilme) { - super(); - downloadInformationLabel = new FilmTabDownloadInformationLabel(daten); - filmInfoLabel = new FilmInfoLabel(daten,tabFilme); - - getChildren().addAll(new CenteredBorderPane(filmInfoLabel), - new VerticalSeparator(), - new CenteredBorderPane(downloadInformationLabel), - new VerticalSeparator()); - - if (isVisible()) - MessageBus.getMessageBus().subscribe(this); - - visibleProperty().addListener(new ChangeListener<>() { - @Override - public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { - if (newValue) { - MessageBus.getMessageBus().subscribe(this); - } else { - MessageBus.getMessageBus().unsubscribe(this); - } - } - }); - } - - private void updateLayout() { - filmInfoLabel.updateValues(); - } - - @Handler - private void handleDownloadInfoUpdate(DownloadInfoUpdateAvailableEvent e) { - Platform.runLater(downloadInformationLabel::setInfoFilme); - } - @Handler - private void handleLeftDisplayUpdate(UpdateStatusBarLeftDisplayEvent e) { - Platform.runLater(this::updateLayout); - } - - - @Handler - private void handleTimerEvent(TimerEvent e) { - Platform.runLater(this::updateLayout); - } -} diff --git a/src/main/java/mediathek/javafx/filterpanel/BlacklistButton.java b/src/main/java/mediathek/javafx/filterpanel/BlacklistButton.java deleted file mode 100644 index 732f268550..0000000000 --- a/src/main/java/mediathek/javafx/filterpanel/BlacklistButton.java +++ /dev/null @@ -1,75 +0,0 @@ -package mediathek.javafx.filterpanel; - -import javafx.application.Platform; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.scene.control.Button; -import javafx.scene.control.Tooltip; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import mediathek.config.Daten; -import mediathek.config.MVConfig; -import mediathek.gui.messages.BlacklistChangedEvent; -import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; - -import javax.swing.*; - -public class BlacklistButton extends Button { - private final Image offImage = new Image(getClass().getResourceAsStream("/mediathek/res/programm/button-blacklist-aus.png"),0,18,true,true); - private final ImageView offImageView = new ImageView(offImage); - private final Image onImage = new Image(getClass().getResourceAsStream("/mediathek/res/programm/button-blacklist-ein.png"),0,18,true,true); - private final ImageView onImageView = new ImageView(onImage); - private final BooleanProperty activeProperty = new SimpleBooleanProperty(false); - private final Tooltip tooltipOn = new Tooltip("Blacklist ausschalten"); - private final Tooltip tooltipOff = new Tooltip("Blacklist einschalten"); - - public BlacklistButton() { - super(); - - final boolean isOn = Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_ON)); - if (isOn) - setupOn(); - else - setupOff(); - - //set initial state - activeProperty.addListener((observable, oldValue, newValue) -> { - if (newValue) { - setupOn(); - } else { - setupOff(); - } - }); - - final var daten = Daten.getInstance(); - final var messageBus = MessageBus.getMessageBus(); - messageBus.subscribe(this); - - activeProperty.setValue(isOn); - activeProperty.addListener((observable, oldValue, newValue) -> SwingUtilities.invokeLater(() -> { - MVConfig.add(MVConfig.Configs.SYSTEM_BLACKLIST_ON, Boolean.toString(newValue)); - daten.getListeBlacklist().filterListe(); - messageBus.publishAsync(new BlacklistChangedEvent()); - })); - - setOnAction(value -> activeProperty.setValue(!activeProperty.getValue())); - } - - @Handler - private void handleBlacklistChangedEvent(BlacklistChangedEvent e) { - //config was changed outside - final boolean on = Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_ON)); - Platform.runLater(() -> activeProperty.setValue(on)); - } - - private void setupOn() { - setGraphic(onImageView); - setTooltip(tooltipOn); - } - - private void setupOff() { - setGraphic(offImageView); - setTooltip(tooltipOff); - } -} diff --git a/src/main/java/mediathek/javafx/filterpanel/CommonViewSettingsPane.java b/src/main/java/mediathek/javafx/filterpanel/CommonViewSettingsPane.java index 6c0ad14dfe..262cda0189 100644 --- a/src/main/java/mediathek/javafx/filterpanel/CommonViewSettingsPane.java +++ b/src/main/java/mediathek/javafx/filterpanel/CommonViewSettingsPane.java @@ -24,112 +24,127 @@ import java.util.ResourceBundle; public class CommonViewSettingsPane extends VBox implements Initializable { - private static final Logger logger = LogManager.getLogger(CommonViewSettingsPane.class); - @FXML public Button btnDeleteFilterSettings; - @FXML public CheckBox cbShowOnlyHd; - @FXML public CheckBox cbShowSubtitlesOnly; - @FXML public CheckBox cbShowNewOnly; - @FXML public CheckBox cbShowBookMarkedOnly; - @FXML public CheckBox cbShowOnlyLivestreams; - @FXML public CheckBox cbShowUnseenOnly; - @FXML public CheckBox cbDontShowAbos; - @FXML public CheckBox cbDontShowGebaerdensprache; - @FXML public CheckBox cbDontShowTrailers; - @FXML public CheckBox cbDontShowAudioVersions; - @FXML public SenderBoxNode senderCheckList; - @FXML public ThemaComboBox themaComboBox; - @FXML public FilmLenghtSliderNode filmLengthSliderNode; - @FXML public ZeitraumSpinner zeitraumSpinner; - @FXML public Button btnDeleteCurrentFilter; - @FXML private Label themaLabel; - @FXML private ComboBox filterSelect; - @FXML private Button btnAddNewFilter; - private boolean deleteCurrentFilterButtonDisabled; + private static final Logger logger = LogManager.getLogger(CommonViewSettingsPane.class); + @FXML + public Button btnDeleteFilterSettings; + @FXML + public CheckBox cbShowOnlyHd; + @FXML + public CheckBox cbShowSubtitlesOnly; + @FXML + public CheckBox cbShowNewOnly; + @FXML + public CheckBox cbShowBookMarkedOnly; + @FXML + public CheckBox cbShowOnlyLivestreams; + @FXML + public CheckBox cbShowUnseenOnly; + @FXML + public CheckBox cbDontShowAbos; + @FXML + public CheckBox cbDontShowGebaerdensprache; + @FXML + public CheckBox cbDontShowTrailers; + @FXML + public CheckBox cbDontShowAudioVersions; + @FXML + public SenderBoxNode senderCheckList; + @FXML + public ThemaComboBox themaComboBox; + @FXML + public FilmLenghtSliderNode filmLengthSliderNode; + @FXML + public ZeitraumSpinner zeitraumSpinner; + @FXML + public Button btnDeleteCurrentFilter; + @FXML + private Label themaLabel; + @FXML + private ComboBox filterSelect; + @FXML + private Button btnAddNewFilter; + private boolean deleteCurrentFilterButtonDisabled; - public CommonViewSettingsPane() { - super(); + public CommonViewSettingsPane() { + super(); - try { - URL url = getClass().getResource("/mediathek/res/programm/fxml/filter_settings_pane.fxml"); - FXMLLoader fxmlLoader = new FXMLLoader(url); - fxmlLoader.setRoot(this); - fxmlLoader.setController(this); - fxmlLoader.load(); - } catch (IOException e) { - logger.error("Failed to load FXML!", e); + try { + URL url = getClass().getResource("/mediathek/res/programm/fxml/filter_settings_pane.fxml"); + FXMLLoader fxmlLoader = new FXMLLoader(url); + fxmlLoader.setRoot(this); + fxmlLoader.setController(this); + fxmlLoader.load(); + } catch (IOException e) { + logger.error("Failed to load FXML!", e); + } } - } - /** - * Prevent user from changing filter settings while the swing table model gets updated. - * - * @param evt the model event - */ - @Handler - private void handleTableModelChangeEvent(TableModelChangeEvent evt) { - Platform.runLater( - () -> { - final boolean disable = evt.active; - btnDeleteFilterSettings.setDisable(disable); - cbShowOnlyHd.setDisable(disable); - cbShowSubtitlesOnly.setDisable(disable); - cbShowNewOnly.setDisable(disable); - cbShowBookMarkedOnly.setDisable(disable); - cbShowOnlyLivestreams.setDisable(disable); - cbShowUnseenOnly.setDisable(disable); - cbDontShowAbos.setDisable(disable); - cbDontShowGebaerdensprache.setDisable(disable); - cbDontShowTrailers.setDisable(disable); - cbDontShowAudioVersions.setDisable(disable); - senderCheckList.setDisable(disable); - themaComboBox.setDisable(disable); - filmLengthSliderNode.setDisable(disable); - zeitraumSpinner.setDisable(disable); - filterSelect.setDisable(disable); - btnDeleteCurrentFilter.setDisable(disable || deleteCurrentFilterButtonDisabled); - btnAddNewFilter.setDisable(disable); - }); - } - - public void disableDeleteCurrentFilterButton(boolean disable) { - deleteCurrentFilterButtonDisabled = disable; - btnDeleteCurrentFilter.setDisable(disable); - } + /** + * Prevent user from changing filter settings while the swing table model gets updated. + * + * @param evt the model event + */ + @Handler + private void handleTableModelChangeEvent(TableModelChangeEvent evt) { + Platform.runLater( + () -> { + final boolean disable = evt.active; + btnDeleteFilterSettings.setDisable(disable); + cbShowOnlyHd.setDisable(disable); + cbShowSubtitlesOnly.setDisable(disable); + cbShowNewOnly.setDisable(disable); + cbShowBookMarkedOnly.setDisable(disable); + cbShowOnlyLivestreams.setDisable(disable); + cbShowUnseenOnly.setDisable(disable); + cbDontShowAbos.setDisable(disable); + cbDontShowGebaerdensprache.setDisable(disable); + cbDontShowTrailers.setDisable(disable); + cbDontShowAudioVersions.setDisable(disable); + senderCheckList.setDisable(disable); + themaComboBox.setDisable(disable); + filmLengthSliderNode.setDisable(disable); + zeitraumSpinner.setDisable(disable); + filterSelect.setDisable(disable); + btnDeleteCurrentFilter.setDisable(disable || deleteCurrentFilterButtonDisabled); + btnAddNewFilter.setDisable(disable); + }); + } - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - themaLabel.setMinWidth(USE_PREF_SIZE); - // font size is greater on tested ubuntu linux :( - if (SystemUtils.IS_OS_LINUX) themaLabel.setPrefWidth(50d); - else themaLabel.setPrefWidth(45d); + public void disableDeleteCurrentFilterButton(boolean disable) { + deleteCurrentFilterButtonDisabled = disable; + btnDeleteCurrentFilter.setDisable(disable); + } - MessageBus.getMessageBus().subscribe(this); - } + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + themaLabel.setMinWidth(USE_PREF_SIZE); + // font size is greater on tested ubuntu linux :( + if (SystemUtils.IS_OS_LINUX) themaLabel.setPrefWidth(50d); + else themaLabel.setPrefWidth(45d); - public void setFilterSelectionChangeListener(ChangeListener changeListener) { - filterSelect.getSelectionModel().selectedItemProperty().addListener(changeListener); - } + MessageBus.getMessageBus().subscribe(this); + } - public FilterDTO getSelectedFilter() { - return filterSelect.getValue(); - } + public void setFilterSelectionChangeListener(ChangeListener changeListener) { + filterSelect.getSelectionModel().selectedItemProperty().addListener(changeListener); + } - public void setAvailableFilters(ObservableList filters) { - filterSelect.setItems(filters); - } + public void setAvailableFilters(ObservableList filters) { + filterSelect.setItems(filters); + } - public void selectFilter(FilterDTO filter) { - SingleSelectionModel selectionModel = filterSelect.getSelectionModel(); - if (!filter.equals(selectionModel.getSelectedItem())) { - selectionModel.select(filter); + public void selectFilter(FilterDTO filter) { + SingleSelectionModel selectionModel = filterSelect.getSelectionModel(); + if (!filter.equals(selectionModel.getSelectedItem())) { + selectionModel.select(filter); + } } - } - public void setAddNewFilterButtonEventHandler(EventHandler eventHandler) { - btnAddNewFilter.setOnAction(eventHandler); - } + public void setAddNewFilterButtonEventHandler(EventHandler eventHandler) { + btnAddNewFilter.setOnAction(eventHandler); + } - public void setFilterSelectionStringConverter(StringConverter filterStringConverter) { - filterSelect.setConverter(filterStringConverter); - } + public void setFilterSelectionStringConverter(StringConverter filterStringConverter) { + filterSelect.setConverter(filterStringConverter); + } } diff --git a/src/main/java/mediathek/javafx/filterpanel/EditBlacklistButton.java b/src/main/java/mediathek/javafx/filterpanel/EditBlacklistButton.java deleted file mode 100644 index a5f46a11e0..0000000000 --- a/src/main/java/mediathek/javafx/filterpanel/EditBlacklistButton.java +++ /dev/null @@ -1,26 +0,0 @@ -package mediathek.javafx.filterpanel; - -import javafx.scene.control.Button; -import javafx.scene.control.Tooltip; -import mediathek.config.Daten; -import mediathek.gui.dialog.DialogLeer; -import mediathek.gui.dialogEinstellungen.PanelBlacklist; -import org.controlsfx.glyphfont.FontAwesome; -import org.controlsfx.glyphfont.GlyphFont; -import org.controlsfx.glyphfont.GlyphFontRegistry; - -import javax.swing.*; - -public class EditBlacklistButton extends Button { - public EditBlacklistButton() { - super(); - GlyphFont fontAwesome = GlyphFontRegistry.font("FontAwesome"); - setGraphic(fontAwesome.create(FontAwesome.Glyph.SKYATLAS).size(16d)); - setTooltip(new Tooltip("Blacklist bearbeiten")); - setOnAction(e -> SwingUtilities.invokeLater(() -> { - DialogLeer dialog = new DialogLeer(null, true); - dialog.init("Blacklist", new PanelBlacklist(Daten.getInstance(), null, PanelBlacklist.class.getName() + "_3")); - dialog.setVisible(true); - })); - } -} diff --git a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java deleted file mode 100644 index 0371f1677f..0000000000 --- a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java +++ /dev/null @@ -1,111 +0,0 @@ -package mediathek.javafx.filterpanel; - -import javafx.application.Platform; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.scene.control.*; -import mediathek.gui.messages.TableModelChangeEvent; -import mediathek.tool.FilterConfiguration; -import mediathek.tool.FilterDTO; -import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.IOException; -import java.net.URL; - -public class FXFilmToolBar extends ToolBar { - @FXML public Button btnDownloadFilmList; - - @FXML public Button btnFilmInfo; - - @FXML Button btnPlay; - - @FXML Button btnRecord; - - @FXML Button btnBookmark; - - @FXML Button btnManageAbos; - - @FXML ToggleButton btnShowBookmarkedMovies; - - @FXML Button btnManageBookMarks; - - @FXML Button btnShowFilter; - - @FXML - FXSearchControl jfxSearchField; - - @FXML ToggleButton btnSearchThroughDescription; - - @FXML ComboBox filterSelect; - - public FXFilmToolBar() { - try { - URL url = getClass().getResource("/mediathek/res/programm/fxml/film_toolbar.fxml"); - FXMLLoader fxmlLoader = new FXMLLoader(url); - fxmlLoader.setRoot(this); - fxmlLoader.setController(this); - fxmlLoader.load(); - setUpFilterSelect(); - - MessageBus.getMessageBus().subscribe(this); - } catch (IOException e) { - Logger logger = LogManager.getLogger(FXFilmToolBar.class); - logger.error("Failed to load FXML!"); - } - } - - /** - * maintain search field data values - */ - private SearchFieldData searchFieldData; - - @Handler - private void handleTableModelChangeEvent(TableModelChangeEvent e) { - if (e.active) { - Platform.runLater(() -> { - searchFieldData = new SearchFieldData(jfxSearchField.isFocused(), jfxSearchField.getCaretPosition()); - setDisable(true); - }); - } - else { - Platform.runLater(() -> { - setDisable(false); - if (searchFieldData.focused()) { - jfxSearchField.requestFocus(); - if (!jfxSearchField.getText().isEmpty()) { - jfxSearchField.positionCaret(searchFieldData.caretPosition()); - } - } - }); - } - } - - private void setUpFilterSelect() { - FilterConfiguration filterConfig = new FilterConfiguration(); - ObservableList availableFilters = - FXCollections.observableArrayList(filterConfig.getAvailableFilters()); - FilterConfiguration.addAvailableFiltersObserver( - () -> { - availableFilters.clear(); - availableFilters.addAll(filterConfig.getAvailableFilters()); - }); - - SingleSelectionModel selectionModel = filterSelect.getSelectionModel(); - FilterConfiguration.addCurrentFiltersObserver(selectionModel::select); - filterSelect.setItems(availableFilters); - selectionModel.select(filterConfig.getCurrentFilter()); - selectionModel - .selectedItemProperty() - .addListener( - (observableValue, oldValue, newValue) -> { - if (newValue != null && !newValue.equals(filterConfig.getCurrentFilter())) { - filterConfig.setCurrentFilter(newValue); - } - }); - } -} diff --git a/src/main/java/mediathek/javafx/filterpanel/FXSearchControl.kt b/src/main/java/mediathek/javafx/filterpanel/FXSearchControl.kt deleted file mode 100644 index b42235b1c1..0000000000 --- a/src/main/java/mediathek/javafx/filterpanel/FXSearchControl.kt +++ /dev/null @@ -1,80 +0,0 @@ -package mediathek.javafx.filterpanel - -import javafx.beans.value.ObservableValue -import javafx.event.EventHandler -import javafx.scene.Cursor -import javafx.scene.control.Tooltip -import javafx.scene.input.KeyCode -import javafx.scene.input.KeyEvent -import mediathek.tool.Filter -import org.controlsfx.control.textfield.CustomTextField -import org.controlsfx.glyphfont.FontAwesome -import org.controlsfx.glyphfont.GlyphFontRegistry - -class FXSearchControl : CustomTextField() { - - companion object { - private const val PROMPT_THEMA_TITEL = "Thema/Titel" - private const val PROMPT_IRGENDWO = "Thema/Titel/Beschreibung" - } - - private val themaTitelTooltip = Tooltip("Thema/Titel durchsuchen") - private val irgendwoTooltip = Tooltip("Thema/Titel/Beschreibung durchsuchen") - - fun setMode(mode: FXSearchControlFieldMode) { - when (mode) { - FXSearchControlFieldMode.THEMA_TITEL -> { - tooltip = themaTitelTooltip - promptText = PROMPT_THEMA_TITEL - } - FXSearchControlFieldMode.IRGENDWO -> { - tooltip = irgendwoTooltip - promptText = PROMPT_IRGENDWO - } - } - } - - init { - val fontAwesome = GlyphFontRegistry.font("FontAwesome") - left = fontAwesome.create(FontAwesome.Glyph.SEARCH) - right = fontAwesome.create(FontAwesome.Glyph.REMOVE) - onKeyPressed = EventHandler { event: KeyEvent -> - if (event.code == KeyCode.ESCAPE) { - if (text.isNotEmpty()) - text = "" - event.consume() - } - } - - val rightNode = right - rightNode.onMouseClicked = EventHandler { text = "" } - rightNode.cursor = Cursor.DEFAULT - rightNode.isVisible = false - - val textProperty = textProperty() - textProperty.addListener { _: ObservableValue?, _: String?, newValue: String -> - rightNode.isVisible = newValue.isNotEmpty() - } - - prefWidth = 350.0 - minWidth = 350.0 - maxWidth = 350.0 - - textProperty.addListener { _, _, newValue -> checkPatternValidity(newValue) } - } - - private fun checkPatternValidity(text: String) { - style = if (Filter.isPattern(text)) { - if (Filter.makePatternNoCache(text) == null) { - // invalid pattern - "-fx-text-fill: red" - } else { - //valid pattern - "-fx-text-fill: blue" - } - } else { - // regular search text, reset to default style - null - } - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/javafx/filterpanel/FXSearchControlFieldMode.kt b/src/main/java/mediathek/javafx/filterpanel/FXSearchControlFieldMode.kt deleted file mode 100644 index 938c619491..0000000000 --- a/src/main/java/mediathek/javafx/filterpanel/FXSearchControlFieldMode.kt +++ /dev/null @@ -1,5 +0,0 @@ -package mediathek.javafx.filterpanel - -enum class FXSearchControlFieldMode { - THEMA_TITEL, IRGENDWO -} \ No newline at end of file diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 795651ac43..777c6ed67c 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -1,32 +1,26 @@ package mediathek.javafx.filterpanel; +import ca.odell.glazedlists.BasicEventList; +import ca.odell.glazedlists.EventList; +import ca.odell.glazedlists.TransactionList; +import ca.odell.glazedlists.javafx.EventObservableList; import impl.org.controlsfx.autocompletion.SuggestionProvider; -import javafx.animation.PauseTransition; -import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.beans.property.ReadOnlyStringWrapper; -import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; -import javafx.scene.Scene; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Tooltip; -import javafx.util.Duration; import javafx.util.StringConverter; import mediathek.config.Daten; -import mediathek.filmeSuchen.ListenerFilmeLaden; -import mediathek.filmeSuchen.ListenerFilmeLadenEvent; -import mediathek.gui.actions.ManageAboAction; -import mediathek.gui.messages.FilmListWriteStartEvent; -import mediathek.gui.messages.FilmListWriteStopEvent; import mediathek.mainwindow.MediathekGui; -import mediathek.tool.*; -import net.engio.mbassy.listener.Handler; +import mediathek.tool.FilterConfiguration; +import mediathek.tool.FilterDTO; +import mediathek.tool.GermanStringSorter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.controlsfx.control.RangeSlider; import org.controlsfx.control.textfield.TextFields; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.util.ArrayList; @@ -34,445 +28,386 @@ import java.util.UUID; /** - * This class sets up the GuiFilme tool panel and search bar. search is exposed via a readonly + * This class sets up the GuiFilme filter dialog. * property for filtering in GuiFilme. */ public class FilmActionPanel { - private static final Logger logger = LogManager.getLogger(); - private final PauseTransition finalActionTrans = new PauseTransition(Duration.seconds(1)); - private final Tooltip tooltipSearchIrgendwo = new Tooltip("Suche in Beschreibung aktiviert"); - private final Tooltip tooltipSearchRegular = new Tooltip("Suche in Beschreibung deaktiviert"); - private final Tooltip bookmarklistSelected = new Tooltip("Alle Filme anzeigen"); - private final Tooltip bookmarklistDeselected = new Tooltip("Gemerkte Filme anzeigen"); - private final FilterConfiguration filterConfig; - private final ObservableList availableFilters; - public ReadOnlyStringWrapper roSearchStringProperty = new ReadOnlyStringWrapper(); - public BooleanProperty showOnlyHd; - public BooleanProperty showSubtitlesOnly; - public BooleanProperty showNewOnly; - public BooleanProperty showBookMarkedOnly; - public BooleanProperty showUnseenOnly; - public BooleanProperty showLivestreamsOnly; - public BooleanProperty dontShowAbos; - public BooleanProperty dontShowTrailers; - public BooleanProperty dontShowSignLanguage; - public BooleanProperty dontShowAudioVersions; - public BooleanProperty searchThroughDescription; - public ReadOnlyObjectProperty zeitraumProperty; - public ComboBox themaBox; - public RangeSlider filmLengthSlider; - public JDialog filterDialog; - public ManageAboAction manageAboAction; - /** Stores the list of thema strings used for autocompletion. */ - private SuggestionProvider themaSuggestionProvider; - - private FXFilmToolBar toolBar; - private CommonViewSettingsPane viewSettingsPane; - private final PauseTransition reloadTableDataTransition; - - public FilmActionPanel(PauseTransition reloadTableDataTransition) { - this.filterConfig = new FilterConfiguration(); - this.reloadTableDataTransition = reloadTableDataTransition; - - setupViewSettingsPane(); - setupDeleteFilterButton(); - - SwingUtilities.invokeLater( - () -> filterDialog = new SwingFilterDialog(MediathekGui.ui(), viewSettingsPane)); - - restoreConfigSettings(); - - setupConfigListeners(); - availableFilters = FXCollections.observableArrayList(filterConfig.getAvailableFilters()); - setupFilterSelection(); - setupDeleteCurrentFilterButton(); - setupAddNewFilterButton(); - - MessageBus.getMessageBus().subscribe(this); - } - - private void setupAddNewFilterButton() { - viewSettingsPane.setAddNewFilterButtonEventHandler( - event -> { - FilterDTO newFilter = - new FilterDTO( - UUID.randomUUID(), String.format("Filter %d", availableFilters.size() + 1)); - filterConfig.addNewFilter(newFilter); - viewSettingsPane.disableDeleteCurrentFilterButton(false); - viewSettingsPane.selectFilter(newFilter); - }); - } + private static final Logger logger = LogManager.getLogger(); + private final FilterConfiguration filterConfig; + private final ObservableList availableFilters; + /** + * The "base" thema list + */ + private final EventList sourceThemaList = new BasicEventList<>(); + /** + * The JavaFX list based on {@link #sourceThemaList}. + */ + private final EventObservableList themaListItems = new EventObservableList<>(sourceThemaList); + private OldSwingJavaFxFilterDialog filterDialog; + private RangeSlider filmLengthSlider; + private ReadOnlyObjectProperty zeitraumProperty; + private BooleanProperty dontShowAudioVersions; + private BooleanProperty dontShowSignLanguage; + private BooleanProperty dontShowTrailers; + private BooleanProperty dontShowAbos; + private BooleanProperty showLivestreamsOnly; + private BooleanProperty showUnseenOnly; + private BooleanProperty showBookMarkedOnly; + private BooleanProperty showSubtitlesOnly; + private BooleanProperty showOnlyHighQuality; + private BooleanProperty showNewOnly; + /** + * Stores the list of thema strings used for autocompletion. + */ + private SuggestionProvider themaSuggestionProvider; + private CommonViewSettingsPane viewSettingsPane; + + public FilmActionPanel(@NotNull JToggleButton filterToggleBtn) { + this.filterConfig = new FilterConfiguration(); + + setupViewSettingsPane(); + setupDeleteFilterButton(); + + SwingUtilities.invokeLater( + () -> filterDialog = new OldSwingJavaFxFilterDialog(MediathekGui.ui(), viewSettingsPane, filterToggleBtn)); + + restoreConfigSettings(); + + setupConfigListeners(); + availableFilters = FXCollections.observableArrayList(filterConfig.getAvailableFilters()); + setupFilterSelection(); + setupDeleteCurrentFilterButton(); + setupAddNewFilterButton(); + } - private void setupDeleteCurrentFilterButton() { - if (availableFilters.size() <= 1) { - viewSettingsPane.disableDeleteCurrentFilterButton(true); + public OldSwingJavaFxFilterDialog getFilterDialog() { + return filterDialog; } - viewSettingsPane.btnDeleteCurrentFilter.setOnAction( - event -> { - FilterDTO filterToDelete = filterConfig.getCurrentFilter(); - filterConfig.deleteFilter(filterToDelete); + public RangeSlider getFilmLengthSlider() { + return filmLengthSlider; + } - if (availableFilters.size() <= 1) { - viewSettingsPane.disableDeleteCurrentFilterButton(true); - } - }); - } - - private void setupFilterSelection() { - viewSettingsPane.setAvailableFilters(availableFilters); - FilterConfiguration.addAvailableFiltersObserver( - () -> { - availableFilters.clear(); - availableFilters.addAll(filterConfig.getAvailableFilters()); - }); - FilterConfiguration.addCurrentFiltersObserver( - filter -> { - viewSettingsPane.selectFilter(filter); - restoreConfigSettings(); - }); + public ReadOnlyObjectProperty zeitraumProperty() { + return zeitraumProperty; + } - viewSettingsPane.setFilterSelectionChangeListener( - (observableValue, oldValue, newValue) -> { - if (newValue != null && !newValue.equals(oldValue)) { - filterConfig.setCurrentFilter(newValue); - } - }); + public boolean isDontShowAudioVersions() { + return dontShowAudioVersions.get(); + } - viewSettingsPane.setFilterSelectionStringConverter( - new StringConverter<>() { + public BooleanProperty dontShowAudioVersionsProperty() { + return dontShowAudioVersions; + } - @Override - public String toString(FilterDTO filter) { - if (filter == null) { - return null; - } - return filter.name(); - } + public boolean isDontShowSignLanguage() { + return dontShowSignLanguage.get(); + } - @Override - public FilterDTO fromString(String name) { - return filterConfig.findFilterForName(name).orElseGet(() -> renameCurrentFilter(name)); - } - }); - } - - private FilterDTO renameCurrentFilter(String newValue) { - FilterDTO currentFilter = filterConfig.getCurrentFilter(); - if (logger.isDebugEnabled()) { - logger.debug( - "Can't find a filter with name \"{}\". Renaming the current filter \"{}\" to it.", - newValue, - currentFilter.name()); - } - filterConfig.renameCurrentFilter(newValue); - return filterConfig.getCurrentFilter(); - } - - private void setupDeleteFilterButton() { - viewSettingsPane.btnDeleteFilterSettings.setOnAction( - e -> { - filterConfig.clearCurrentFilter(); - restoreConfigSettings(); - }); - } - - private void setupViewSettingsPane() { - viewSettingsPane = new CommonViewSettingsPane(); - - showOnlyHd = viewSettingsPane.cbShowOnlyHd.selectedProperty(); - showSubtitlesOnly = viewSettingsPane.cbShowSubtitlesOnly.selectedProperty(); - showNewOnly = viewSettingsPane.cbShowNewOnly.selectedProperty(); - showBookMarkedOnly = viewSettingsPane.cbShowBookMarkedOnly.selectedProperty(); - showLivestreamsOnly = viewSettingsPane.cbShowOnlyLivestreams.selectedProperty(); - - showUnseenOnly = viewSettingsPane.cbShowUnseenOnly.selectedProperty(); - dontShowAbos = viewSettingsPane.cbDontShowAbos.selectedProperty(); - dontShowSignLanguage = viewSettingsPane.cbDontShowGebaerdensprache.selectedProperty(); - dontShowTrailers = viewSettingsPane.cbDontShowTrailers.selectedProperty(); - dontShowAudioVersions = viewSettingsPane.cbDontShowAudioVersions.selectedProperty(); - - viewSettingsPane.senderCheckList.pauseTransition.setOnFinished(e -> updateThemaBox()); - - themaBox = viewSettingsPane.themaComboBox; - themaSuggestionProvider = SuggestionProvider.create(themaBox.getItems()); - TextFields.bindAutoCompletion(themaBox.getEditor(), themaSuggestionProvider); - - filmLengthSlider = viewSettingsPane.filmLengthSliderNode._filmLengthSlider; - - zeitraumProperty = viewSettingsPane.zeitraumSpinner.valueProperty(); - } - - public CommonViewSettingsPane getViewSettingsPane() { - return viewSettingsPane; - } - - private void restoreConfigSettings() { - viewSettingsPane.selectFilter(filterConfig.getCurrentFilter()); - showOnlyHd.set(filterConfig.isShowHdOnly()); - showSubtitlesOnly.set(filterConfig.isShowSubtitlesOnly()); - showNewOnly.set(filterConfig.isShowNewOnly()); - showBookMarkedOnly.set(filterConfig.isShowBookMarkedOnly()); - showUnseenOnly.set(filterConfig.isShowUnseenOnly()); - showLivestreamsOnly.set(filterConfig.isShowLivestreamsOnly()); - - dontShowAbos.set(filterConfig.isDontShowAbos()); - dontShowTrailers.set(filterConfig.isDontShowTrailers()); - dontShowSignLanguage.set(filterConfig.isDontShowSignLanguage()); - dontShowAudioVersions.set(filterConfig.isDontShowAudioVersions()); - - try { - double loadedMin = filterConfig.getFilmLengthMin(); - if(loadedMin > filmLengthSlider.getHighValue()) { - filmLengthSlider.setHighValueChanging(true); - filmLengthSlider.setHighValue(filterConfig.getFilmLengthMax()); - filmLengthSlider.setHighValueChanging(false); - - filmLengthSlider.setLowValueChanging(true); - filmLengthSlider.setLowValue(loadedMin); - filmLengthSlider.setLowValueChanging(false); - } else { - filmLengthSlider.setLowValueChanging(true); - filmLengthSlider.setLowValue(loadedMin); - filmLengthSlider.setLowValueChanging(false); + public BooleanProperty dontShowSignLanguageProperty() { + return dontShowSignLanguage; + } - filmLengthSlider.setHighValueChanging(true); - filmLengthSlider.setHighValue(filterConfig.getFilmLengthMax()); - filmLengthSlider.setHighValueChanging(false); - } + public boolean isDontShowTrailers() { + return dontShowTrailers.get(); + } - } catch (Exception exception) { - logger.debug( - "Beim wiederherstellen der Filter Einstellungen für die Filmlänge ist ein Fehler aufgetreten!", - exception); - } - - try { - viewSettingsPane.zeitraumSpinner.getValueFactory().setValue(filterConfig.getZeitraum()); - } catch (Exception exception) { - logger.debug( - "Beim wiederherstellen der Filter Einstellungen für den Zeitraum ist ein Fehler aufgetreten!", - exception); - } - } - - private void setupConfigListeners() { - showOnlyHd.addListener( - (observable, oldValue, newValue) -> filterConfig.setShowHdOnly(newValue)); - showSubtitlesOnly.addListener( - ((observable, oldValue, newValue) -> filterConfig.setShowSubtitlesOnly(newValue))); - showBookMarkedOnly.addListener( - ((observable, oldValue, newValue) -> filterConfig.setShowBookMarkedOnly(newValue))); - showNewOnly.addListener( - ((observable, oldValue, newValue) -> filterConfig.setShowNewOnly(newValue))); - showUnseenOnly.addListener( - ((observable, oldValue, newValue) -> filterConfig.setShowUnseenOnly(newValue))); - showLivestreamsOnly.addListener( - ((observable, oldValue, newValue) -> filterConfig.setShowLivestreamsOnly(newValue))); - - dontShowAbos.addListener( - ((observable, oldValue, newValue) -> filterConfig.setDontShowAbos(newValue))); - dontShowTrailers.addListener( - ((observable, oldValue, newValue) -> filterConfig.setDontShowTrailers(newValue))); - dontShowSignLanguage.addListener( - ((observable, oldValue, newValue) -> filterConfig.setDontShowSignLanguage(newValue))); - dontShowAudioVersions.addListener( - ((observable, oldValue, newValue) -> filterConfig.setDontShowAudioVersions(newValue))); - - filmLengthSlider - .lowValueProperty() - .addListener( - ((observable, oldValue, newValue) -> - filterConfig.setFilmLengthMin(newValue.doubleValue()))); - filmLengthSlider - .highValueProperty() - .addListener( - ((observable, oldValue, newValue) -> - filterConfig.setFilmLengthMax(newValue.doubleValue()))); - - viewSettingsPane - .zeitraumSpinner - .valueProperty() - .addListener(((observable, oldValue, newValue) -> filterConfig.setZeitraum(newValue))); - } - - @Handler - private void handleFilmlistWriteStartEvent(FilmListWriteStartEvent e) { - Platform.runLater(() -> toolBar.btnDownloadFilmList.setDisable(true)); - } - - @Handler - private void handleFilmlistWriteStopEvent(FilmListWriteStopEvent e) { - Platform.runLater(() -> toolBar.btnDownloadFilmList.setDisable(false)); - } - - private void searchFieldFinalAction() { - final var text = toolBar.jfxSearchField.getText(); - boolean invokeTableFiltering = false; - - if (Filter.isPattern(text)) { - //only invoke filtering if we have regexp pattern and the pattern is valid - if (Filter.makePatternNoCache(text) != null) { - invokeTableFiltering = true; - } - } else { - invokeTableFiltering = true; - } + public BooleanProperty dontShowTrailersProperty() { + return dontShowTrailers; + } - if (invokeTableFiltering) - reloadTableDataTransition.playFromStart(); + public boolean isDontShowAbos() { + return dontShowAbos.get(); } - private void setupSearchField() { - toolBar.jfxSearchField.setMode(FXSearchControlFieldMode.THEMA_TITEL); + public BooleanProperty dontShowAbosProperty() { + return dontShowAbos; + } - final StringProperty textProperty = toolBar.jfxSearchField.textProperty(); - roSearchStringProperty.bind(textProperty); + public boolean isShowLivestreamsOnly() { + return showLivestreamsOnly.get(); + } - finalActionTrans.setOnFinished(e -> searchFieldFinalAction()); - textProperty.addListener((observable, oldValue, newValue) -> finalActionTrans.playFromStart()); + public BooleanProperty showLivestreamsOnlyProperty() { + return showLivestreamsOnly; } - private void setupSearchThroughDescriptionButton() { - final boolean enabled = - ApplicationConfiguration.getConfiguration() - .getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); - toolBar.btnSearchThroughDescription.setSelected(enabled); + public boolean isShowUnseenOnly() { + return showUnseenOnly.get(); + } + + public BooleanProperty showUnseenOnlyProperty() { + return showUnseenOnly; + } - if (enabled) setupForIrgendwoSearch(); - else setupForRegularSearch(); + public boolean isShowBookMarkedOnly() { + return showBookMarkedOnly.get(); + } - toolBar.btnSearchThroughDescription.setOnAction( - e -> { - final boolean bSearchThroughDescription = - toolBar.btnSearchThroughDescription.isSelected(); - ApplicationConfiguration.getConfiguration() - .setProperty( - ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, bSearchThroughDescription); + public BooleanProperty showBookMarkedOnlyProperty() { + return showBookMarkedOnly; + } - if (bSearchThroughDescription) setupForIrgendwoSearch(); - else setupForRegularSearch(); - }); + public boolean isShowSubtitlesOnly() { + return showSubtitlesOnly.get(); + } + + public BooleanProperty showSubtitlesOnlyProperty() { + return showSubtitlesOnly; + } + + public boolean isShowOnlyHighQuality() { + return showOnlyHighQuality.get(); + } + + public BooleanProperty showOnlyHighQualityProperty() { + return showOnlyHighQuality; + } + + public boolean isShowNewOnly() { + return showNewOnly.get(); + } + + public BooleanProperty showNewOnlyProperty() { + return showNewOnly; + } + + private void setupAddNewFilterButton() { + viewSettingsPane.setAddNewFilterButtonEventHandler( + event -> { + FilterDTO newFilter = + new FilterDTO( + UUID.randomUUID(), String.format("Filter %d", availableFilters.size() + 1)); + filterConfig.addNewFilter(newFilter); + viewSettingsPane.disableDeleteCurrentFilterButton(false); + viewSettingsPane.selectFilter(newFilter); + }); + } + + private void setupDeleteCurrentFilterButton() { + if (availableFilters.size() <= 1) { + viewSettingsPane.disableDeleteCurrentFilterButton(true); + } + + viewSettingsPane.btnDeleteCurrentFilter.setOnAction( + event -> { + FilterDTO filterToDelete = filterConfig.getCurrentFilter(); + filterConfig.deleteFilter(filterToDelete); - searchThroughDescription = toolBar.btnSearchThroughDescription.selectedProperty(); - } - - private void setupForRegularSearch() { - toolBar.jfxSearchField.setMode(FXSearchControlFieldMode.THEMA_TITEL); - - toolBar.btnSearchThroughDescription.setTooltip(tooltipSearchRegular); - } - - private void setupForIrgendwoSearch() { - toolBar.jfxSearchField.setMode(FXSearchControlFieldMode.IRGENDWO); - - toolBar.btnSearchThroughDescription.setTooltip(tooltipSearchIrgendwo); - } - - private void setupShowBookmarkedMoviesButton() { - toolBar.btnShowBookmarkedMovies.setSelected(false); - toolBar.btnShowBookmarkedMovies.setTooltip(bookmarklistDeselected); - toolBar.btnShowBookmarkedMovies.setOnAction( - event -> - viewSettingsPane - .cbShowBookMarkedOnly - .selectedProperty() - .set(toolBar.btnShowBookmarkedMovies.isSelected())); - showBookMarkedOnly.addListener( - (observable, oldValue, benabled) -> { - toolBar.btnShowBookmarkedMovies.setTooltip( - benabled ? bookmarklistSelected : bookmarklistDeselected); - toolBar.btnShowBookmarkedMovies.setSelected(benabled); - if (benabled) { - toolBar.jfxSearchField.clear(); - } + if (availableFilters.size() <= 1) { + viewSettingsPane.disableDeleteCurrentFilterButton(true); + } + }); + } + + private void setupFilterSelection() { + viewSettingsPane.setAvailableFilters(availableFilters); + FilterConfiguration.addAvailableFiltersObserver( + () -> { + availableFilters.clear(); + availableFilters.addAll(filterConfig.getAvailableFilters()); + }); + FilterConfiguration.addCurrentFiltersObserver( + filter -> { + viewSettingsPane.selectFilter(filter); + restoreConfigSettings(); + }); + + viewSettingsPane.setFilterSelectionChangeListener( + (observableValue, oldValue, newValue) -> { + if (newValue != null && !newValue.equals(oldValue)) { + filterConfig.setCurrentFilter(newValue); + } + }); + + viewSettingsPane.setFilterSelectionStringConverter( + new StringConverter<>() { + + @Override + public String toString(FilterDTO filter) { + if (filter == null) { + return null; + } + return filter.name(); + } + + @Override + public FilterDTO fromString(String name) { + return filterConfig.findFilterForName(name).orElseGet(() -> renameCurrentFilter(name)); + } + }); + } + + private FilterDTO renameCurrentFilter(String newValue) { + FilterDTO currentFilter = filterConfig.getCurrentFilter(); + logger.debug("Can't find a filter with name \"{}\". Renaming the current filter \"{}\" to it.", + newValue, currentFilter.name()); + filterConfig.renameCurrentFilter(newValue); + return filterConfig.getCurrentFilter(); + } + + private void setupDeleteFilterButton() { + viewSettingsPane.btnDeleteFilterSettings.setOnAction(e -> { + filterConfig.clearCurrentFilter(); + restoreConfigSettings(); }); - } + } + + private void setupViewSettingsPane() { + viewSettingsPane = new CommonViewSettingsPane(); + + showOnlyHighQuality = viewSettingsPane.cbShowOnlyHd.selectedProperty(); + showSubtitlesOnly = viewSettingsPane.cbShowSubtitlesOnly.selectedProperty(); + showNewOnly = viewSettingsPane.cbShowNewOnly.selectedProperty(); + showBookMarkedOnly = viewSettingsPane.cbShowBookMarkedOnly.selectedProperty(); + showLivestreamsOnly = viewSettingsPane.cbShowOnlyLivestreams.selectedProperty(); + + showUnseenOnly = viewSettingsPane.cbShowUnseenOnly.selectedProperty(); + dontShowAbos = viewSettingsPane.cbDontShowAbos.selectedProperty(); + dontShowSignLanguage = viewSettingsPane.cbDontShowGebaerdensprache.selectedProperty(); + dontShowTrailers = viewSettingsPane.cbDontShowTrailers.selectedProperty(); + dontShowAudioVersions = viewSettingsPane.cbDontShowAudioVersions.selectedProperty(); - public void updateThemaBox() { - final var items = themaBox.getItems(); - items.clear(); - items.add(""); + setupThemaComboBox(); + viewSettingsPane.senderCheckList.getCheckModel().getCheckedItems(). + addListener((ListChangeListener) c -> updateThemaComboBox()); + filmLengthSlider = viewSettingsPane.filmLengthSliderNode._filmLengthSlider; + + zeitraumProperty = viewSettingsPane.zeitraumSpinner.valueProperty(); + } + + private void setupThemaComboBox() { + viewSettingsPane.themaComboBox.setItems(themaListItems); + themaSuggestionProvider = SuggestionProvider.create(themaListItems); + TextFields.bindAutoCompletion(viewSettingsPane.themaComboBox.getEditor(), themaSuggestionProvider); + } + + public CommonViewSettingsPane getViewSettingsPane() { + return viewSettingsPane; + } + + private void restoreConfigSettings() { + viewSettingsPane.selectFilter(filterConfig.getCurrentFilter()); + showOnlyHighQuality.set(filterConfig.isShowHdOnly()); + showSubtitlesOnly.set(filterConfig.isShowSubtitlesOnly()); + showNewOnly.set(filterConfig.isShowNewOnly()); + showBookMarkedOnly.set(filterConfig.isShowBookMarkedOnly()); + showUnseenOnly.set(filterConfig.isShowUnseenOnly()); + showLivestreamsOnly.set(filterConfig.isShowLivestreamsOnly()); + + dontShowAbos.set(filterConfig.isDontShowAbos()); + dontShowTrailers.set(filterConfig.isDontShowTrailers()); + dontShowSignLanguage.set(filterConfig.isDontShowSignLanguage()); + dontShowAudioVersions.set(filterConfig.isDontShowAudioVersions()); + + try { + double loadedMin = filterConfig.getFilmLengthMin(); + if (loadedMin > filmLengthSlider.getHighValue()) { + filmLengthSlider.setHighValueChanging(true); + filmLengthSlider.setHighValue(filterConfig.getFilmLengthMax()); + filmLengthSlider.setHighValueChanging(false); + + filmLengthSlider.setLowValueChanging(true); + filmLengthSlider.setLowValue(loadedMin); + filmLengthSlider.setLowValueChanging(false); + } else { + filmLengthSlider.setLowValueChanging(true); + filmLengthSlider.setLowValue(loadedMin); + filmLengthSlider.setLowValueChanging(false); + + filmLengthSlider.setHighValueChanging(true); + filmLengthSlider.setHighValue(filterConfig.getFilmLengthMax()); + filmLengthSlider.setHighValueChanging(false); + } + + } catch (Exception exception) { + logger.debug("Beim wiederherstellen der Filter Einstellungen für die Filmlänge ist ein Fehler aufgetreten!", + exception); + } + + try { + viewSettingsPane.zeitraumSpinner.getValueFactory().setValue(filterConfig.getZeitraum()); + } catch (Exception exception) { + logger.debug("Beim wiederherstellen der Filter Einstellungen für den Zeitraum ist ein Fehler aufgetreten!", + exception); + } + } + + private void setupConfigListeners() { + showOnlyHighQuality.addListener( + (observable, oldValue, newValue) -> filterConfig.setShowHdOnly(newValue)); + showSubtitlesOnly.addListener( + ((observable, oldValue, newValue) -> filterConfig.setShowSubtitlesOnly(newValue))); + showBookMarkedOnly.addListener( + ((observable, oldValue, newValue) -> filterConfig.setShowBookMarkedOnly(newValue))); + showNewOnly.addListener( + ((observable, oldValue, newValue) -> filterConfig.setShowNewOnly(newValue))); + showUnseenOnly.addListener( + ((observable, oldValue, newValue) -> filterConfig.setShowUnseenOnly(newValue))); + showLivestreamsOnly.addListener( + ((observable, oldValue, newValue) -> filterConfig.setShowLivestreamsOnly(newValue))); + + dontShowAbos.addListener( + ((observable, oldValue, newValue) -> filterConfig.setDontShowAbos(newValue))); + dontShowTrailers.addListener( + ((observable, oldValue, newValue) -> filterConfig.setDontShowTrailers(newValue))); + dontShowSignLanguage.addListener( + ((observable, oldValue, newValue) -> filterConfig.setDontShowSignLanguage(newValue))); + dontShowAudioVersions.addListener( + ((observable, oldValue, newValue) -> filterConfig.setDontShowAudioVersions(newValue))); + + filmLengthSlider.lowValueProperty().addListener( + ((observable, oldValue, newValue) -> filterConfig.setFilmLengthMin(newValue.doubleValue()))); + filmLengthSlider.highValueProperty().addListener( + ((observable, oldValue, newValue) -> filterConfig.setFilmLengthMax(newValue.doubleValue()))); + + zeitraumProperty.addListener(((os, oV, newValue) -> filterConfig.setZeitraum(newValue))); + } + + /** + * Retrieve the list of all thema based on sender select checkbox list. + * + * @param selectedSenders the list of selected senders + * @return list of all applicable themas. + */ + private List getThemaList(@NotNull List selectedSenders) { List finalList = new ArrayList<>(); - List selectedSenders = viewSettingsPane.senderCheckList.getCheckModel().getCheckedItems(); final var blackList = Daten.getInstance().getListeFilmeNachBlackList(); if (selectedSenders.isEmpty()) { - final List lst = blackList.getThemen(""); - finalList.addAll(lst); - lst.clear(); + finalList.addAll(blackList.getThemen("")); } else { for (String sender : selectedSenders) { - final List lst = blackList.getThemen(sender); - finalList.addAll(lst); - lst.clear(); + finalList.addAll(blackList.getThemen(sender)); } } - items.addAll(finalList.stream() - .distinct() - .sorted(GermanStringSorter.getInstance()).toList()); - finalList.clear(); + return finalList; + } + /** + * Update the Thema list and the autocompletion provider after a sender checkbox list change. + */ + public void updateThemaComboBox() { + //update the thema list -> updates the combobox automagically + //use transaction list to minimize updates... + var transactionThemaList = new TransactionList<>(sourceThemaList); + transactionThemaList.beginEvent(true); + transactionThemaList.clear(); + transactionThemaList.add(""); + + var selectedSenders = viewSettingsPane.senderCheckList.getCheckModel().getCheckedItems(); + var tempThemaList = getThemaList(selectedSenders).stream() + .sorted(GermanStringSorter.getInstance()) + .toList(); + transactionThemaList.addAll(tempThemaList); + transactionThemaList.commitEvent(); + + //update autocpmpletion provider here only as the other listeners fire too much themaSuggestionProvider.clearSuggestions(); - themaSuggestionProvider.addPossibleSuggestions(items); - themaBox.getSelectionModel().select(0); - } - - private void setupToolBar() { - toolBar = new FXFilmToolBar(); - toolBar.btnDownloadFilmList.setOnAction(e -> SwingUtilities.invokeLater( - () -> MediathekGui.ui().performFilmListLoadOperation(false))); - toolBar.btnFilmInfo.setOnAction( - e -> SwingUtilities.invokeLater(MediathekGui.ui().getFilmInfoDialog()::showInfo)); - toolBar.btnPlay.setOnAction(evt -> - SwingUtilities.invokeLater( - () -> MediathekGui.ui().tabFilme.playAction.actionPerformed(null))); - toolBar.btnRecord.setOnAction(e -> - SwingUtilities.invokeLater( - () -> MediathekGui.ui().tabFilme.saveFilmAction.actionPerformed(null))); - toolBar.btnBookmark.setOnAction(e -> - SwingUtilities.invokeLater( - () -> MediathekGui.ui().tabFilme.bookmarkFilmAction.actionPerformed(null))); - toolBar.btnManageBookMarks.setOnAction(e -> - SwingUtilities.invokeLater( - () -> MediathekGui.ui().tabFilme.bookmarkManageListAction.actionPerformed(null))); - toolBar.btnManageAbos.setOnAction(e -> - SwingUtilities.invokeLater(() -> { - if (manageAboAction.isEnabled()) manageAboAction.actionPerformed(null); - })); - toolBar.btnShowFilter.setOnAction(e -> - SwingUtilities.invokeLater(() -> { - if (filterDialog != null) { - filterDialog.setVisible( - !filterDialog.isVisible()); // Toggle Dialog display on button press - } - })); - } - - public Scene getFilmActionPanelScene() { - setupToolBar(); - - setupSearchField(); - - setupSearchThroughDescriptionButton(); - - setupShowBookmarkedMoviesButton(); - - Daten.getInstance().getFilmeLaden().addAdListener( - new ListenerFilmeLaden() { - @Override - public void start(ListenerFilmeLadenEvent event) { - Platform.runLater(() -> toolBar.setDisable(true)); - } - - @Override - public void fertig(ListenerFilmeLadenEvent event) { - Platform.runLater(() -> toolBar.setDisable(false)); - } - }); - - return new Scene(toolBar); + //themaListItems wird durch die sourcelist/transactionlist aktualisiert im Vorfeld + themaSuggestionProvider.addPossibleSuggestions(themaListItems); + viewSettingsPane.themaComboBox.getSelectionModel().select(0); } } diff --git a/src/main/java/mediathek/javafx/filterpanel/SwingFilterDialog.java b/src/main/java/mediathek/javafx/filterpanel/OldSwingJavaFxFilterDialog.java similarity index 54% rename from src/main/java/mediathek/javafx/filterpanel/SwingFilterDialog.java rename to src/main/java/mediathek/javafx/filterpanel/OldSwingJavaFxFilterDialog.java index e1dc77e8a0..bf9ae49325 100644 --- a/src/main/java/mediathek/javafx/filterpanel/SwingFilterDialog.java +++ b/src/main/java/mediathek/javafx/filterpanel/OldSwingJavaFxFilterDialog.java @@ -11,18 +11,26 @@ import mediathek.tool.MessageBus; import net.engio.mbassy.listener.Handler; import org.apache.commons.configuration2.sync.LockMode; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; import java.util.NoSuchElementException; -public class SwingFilterDialog extends JDialog { +public class OldSwingJavaFxFilterDialog extends JDialog { private final JFXPanel fxPanel = new JFXPanel(); + private final JToggleButton filterToggleButton; - public SwingFilterDialog(Frame owner, CommonViewSettingsPane content) { + public OldSwingJavaFxFilterDialog(Frame owner, CommonViewSettingsPane content, @NotNull JToggleButton filterToggleButton) { super(owner); + this.filterToggleButton = filterToggleButton; + + ToggleVisibilityKeyHandler handler = new ToggleVisibilityKeyHandler(this); + handler.installHandler(filterToggleButton.getAction()); + setDefaultCloseOperation(HIDE_ON_CLOSE); setTitle("Filter"); setType(Type.UTILITY); @@ -33,7 +41,7 @@ public SwingFilterDialog(Frame owner, CommonViewSettingsPane content) { pack(); restoreWindowSizeFromConfig(); restoreDialogVisibility(); - registerWindowSizeListener(); + addComponentListener(new FilterDialogComponentListener()); }); }); @@ -90,46 +98,60 @@ private void restoreWindowSizeFromConfig() { } - private void registerWindowSizeListener() { - addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - storeWindowPosition(e); - } + static class ToggleVisibilityKeyHandler { + private static final String TOGGLE_FILTER_VISIBILITY = "toggle_dialog_visibility"; + private final JRootPane rootPane; + public ToggleVisibilityKeyHandler(JDialog dlg) { + this.rootPane = dlg.getRootPane(); + } - @Override - public void componentMoved(ComponentEvent e) { - storeWindowPosition(e); - } + public void installHandler(Action action) { + final var inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0), TOGGLE_FILTER_VISIBILITY); + rootPane.getActionMap().put(TOGGLE_FILTER_VISIBILITY, action); + } + } - @Override - public void componentShown(ComponentEvent e) { - storeDialogVisibility(); - } + public class FilterDialogComponentListener extends ComponentAdapter { + @Override + public void componentResized(ComponentEvent e) { + storeWindowPosition(e); + } - @Override - public void componentHidden(ComponentEvent e) { - storeWindowPosition(e); - storeDialogVisibility(); - } + @Override + public void componentMoved(ComponentEvent e) { + storeWindowPosition(e); + } - private void storeWindowPosition(ComponentEvent e) { - var config = ApplicationConfiguration.getConfiguration(); - var component = e.getComponent(); - - var dims = component.getSize(); - var loc = component.getLocation(); - try { - config.lock(LockMode.WRITE); - config.setProperty(ApplicationConfiguration.FilterDialog.WIDTH, dims.width); - config.setProperty(ApplicationConfiguration.FilterDialog.HEIGHT, dims.height); - config.setProperty(ApplicationConfiguration.FilterDialog.X, loc.x); - config.setProperty(ApplicationConfiguration.FilterDialog.Y, loc.y); - } finally { - config.unlock(LockMode.WRITE); - } - } - }); + @Override + public void componentShown(ComponentEvent e) { + storeDialogVisibility(); + filterToggleButton.setSelected(true); + } + @Override + public void componentHidden(ComponentEvent e) { + storeWindowPosition(e); + storeDialogVisibility(); + + filterToggleButton.setSelected(false); + } + + private void storeWindowPosition(ComponentEvent e) { + var config = ApplicationConfiguration.getConfiguration(); + var component = e.getComponent(); + + var dims = component.getSize(); + var loc = component.getLocation(); + try { + config.lock(LockMode.WRITE); + config.setProperty(ApplicationConfiguration.FilterDialog.WIDTH, dims.width); + config.setProperty(ApplicationConfiguration.FilterDialog.HEIGHT, dims.height); + config.setProperty(ApplicationConfiguration.FilterDialog.X, loc.x); + config.setProperty(ApplicationConfiguration.FilterDialog.Y, loc.y); + } finally { + config.unlock(LockMode.WRITE); + } + } } } diff --git a/src/main/java/mediathek/javafx/filterpanel/SearchControlFieldMode.kt b/src/main/java/mediathek/javafx/filterpanel/SearchControlFieldMode.kt new file mode 100644 index 0000000000..568388b986 --- /dev/null +++ b/src/main/java/mediathek/javafx/filterpanel/SearchControlFieldMode.kt @@ -0,0 +1,5 @@ +package mediathek.javafx.filterpanel + +enum class SearchControlFieldMode { + THEMA_TITEL, IRGENDWO, LUCENE +} \ No newline at end of file diff --git a/src/main/java/mediathek/javafx/filterpanel/SearchFieldData.java b/src/main/java/mediathek/javafx/filterpanel/SearchFieldData.java deleted file mode 100644 index 426f700297..0000000000 --- a/src/main/java/mediathek/javafx/filterpanel/SearchFieldData.java +++ /dev/null @@ -1,7 +0,0 @@ -package mediathek.javafx.filterpanel; - -/** - * Stores necessary data related to search fields between table filter updates. - */ -record SearchFieldData(boolean focused, int caretPosition) { -} diff --git a/src/main/java/mediathek/javafx/filterpanel/SenderBoxNode.java b/src/main/java/mediathek/javafx/filterpanel/SenderBoxNode.java index 5ee8a21282..88ff7ec55b 100644 --- a/src/main/java/mediathek/javafx/filterpanel/SenderBoxNode.java +++ b/src/main/java/mediathek/javafx/filterpanel/SenderBoxNode.java @@ -3,17 +3,12 @@ import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.javafx.EventObservableList; -import javafx.animation.PauseTransition; -import javafx.collections.ListChangeListener; import javafx.scene.control.ContextMenu; import javafx.scene.control.MenuItem; -import javafx.util.Duration; import mediathek.controller.SenderFilmlistLoadApprover; import org.controlsfx.control.CheckListView; public class SenderBoxNode extends CheckListView { - public final PauseTransition pauseTransition = new PauseTransition(Duration.millis(500d)); - public SenderBoxNode() { //do not display unchecked(unloaded) senders from config... EventList senderList = new BasicEventList<>(); @@ -22,9 +17,6 @@ public SenderBoxNode() { setItems(new EventObservableList<>(senderList)); - getCheckModel().getCheckedItems(). - addListener((ListChangeListener) c -> pauseTransition.playFromStart()); - var contextMenu = new ContextMenu(); var miClearChecks = new MenuItem("Alle Senderfilter zurücksetzen"); miClearChecks.setOnAction(e -> getCheckModel().clearChecks()); diff --git a/src/main/java/mediathek/javafx/tool/ComputedLabel.java b/src/main/java/mediathek/javafx/tool/ComputedLabel.java deleted file mode 100644 index c7c1e7734d..0000000000 --- a/src/main/java/mediathek/javafx/tool/ComputedLabel.java +++ /dev/null @@ -1,23 +0,0 @@ -package mediathek.javafx.tool; - -import javafx.scene.control.Label; - -/** - * A JavaFX Label which will adapt its minimum width. - */ -public abstract class ComputedLabel extends Label { - private double width = 0d; - - /** - * This sets the text of the label and adjusts the width of the label to prevent "jumping" text. - */ - public void setComputedText(String text) { - setText(text); - double curWidth = getWidth(); - if (curWidth >= width) { - width = curWidth; - setMinWidth(width); - } - - } -} diff --git a/src/main/java/mediathek/javafx/tool/FXProgressPane.java b/src/main/java/mediathek/javafx/tool/FXProgressPane.java deleted file mode 100644 index d19e5f4f76..0000000000 --- a/src/main/java/mediathek/javafx/tool/FXProgressPane.java +++ /dev/null @@ -1,32 +0,0 @@ -package mediathek.javafx.tool; - -import javafx.concurrent.Task; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressBar; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.layout.HBox; -import mediathek.javafx.CenteredBorderPane; -import mediathek.javafx.VerticalSeparator; - -public class FXProgressPane extends HBox { - public Label lb; - public ProgressBar prog; - - public FXProgressPane() { - super(); - setSpacing(4d); - - lb = new Label(); - prog = new ProgressBar(); - prog.setProgress(ProgressIndicator.INDETERMINATE_PROGRESS); - - getChildren().addAll(new VerticalSeparator(), - new CenteredBorderPane(lb), - new CenteredBorderPane(prog)); - } - - public void bindTask(Task task) { - lb.textProperty().bind(task.messageProperty()); - prog.progressProperty().bind(task.progressProperty()); - } -} diff --git a/src/main/java/mediathek/javafx/tool/JFXHiddenApplication.java b/src/main/java/mediathek/javafx/tool/JFXHiddenApplication.java index e2f6b0c37d..2f5a2b613a 100644 --- a/src/main/java/mediathek/javafx/tool/JFXHiddenApplication.java +++ b/src/main/java/mediathek/javafx/tool/JFXHiddenApplication.java @@ -2,18 +2,10 @@ import javafx.application.Application; import javafx.scene.Scene; -import javafx.scene.control.Alert; -import javafx.scene.control.Button; -import javafx.scene.control.ButtonType; import javafx.scene.image.Image; import javafx.scene.layout.Pane; -import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; -import mediathek.config.Konstanten; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; public class JFXHiddenApplication extends Application { @@ -46,55 +38,4 @@ public void start(Stage primaryStage) { public static Stage getPrimaryStage() { return primaryStage; } - - public static void showJavaFXDialog(Stage fxDialog, JFrame swingParent) { - fxDialog.setOpacity(0d); - fxDialog.show(); - fxDialog.hide(); - fxDialog.setOpacity(1d); - - fxDialog.setX(swingParent.getBounds().getCenterX() - (fxDialog.getWidth() / 2)); - fxDialog.setY(swingParent.getBounds().getCenterY() - (fxDialog.getHeight() / 2)); - - fakeModalDialog(fxDialog, swingParent); - - fxDialog.setAlwaysOnTop(true); - fxDialog.showAndWait(); - } - - public static void showAlert(@NotNull Alert alert, @NotNull JFrame swingParent) { - var dialogPane = alert.getDialogPane(); - var scene = dialogPane.getScene(); - Stage alertStage = new Stage(); - alertStage.getIcons().add(getApplicationImage()); - alertStage.setTitle(Konstanten.PROGRAMMNAME); - alertStage.setScene(scene); - alertStage.initModality(Modality.APPLICATION_MODAL); - alertStage.initOwner(getPrimaryStage()); - final Button btnFoo = (Button) dialogPane.lookupButton( ButtonType.OK ); - btnFoo.setOnAction(evt -> { - alertStage.close(); - evt.consume(); - }); - - showJavaFXDialog(alertStage, swingParent); - } - - private static void fakeModalDialog(Stage fxDialog, JFrame swingParent) { - - fxDialog.setOnShown(e -> SwingUtilities.invokeLater(() -> { - swingParent.setEnabled(false); - if (swingParent.getJMenuBar() != null) { - swingParent.getJMenuBar().setEnabled(false); - } - })); - - fxDialog.setOnHidden(e -> SwingUtilities.invokeLater(() -> { - swingParent.setEnabled(true); - if (swingParent.getJMenuBar() != null) { - swingParent.getJMenuBar().setEnabled(true); - } - swingParent.toFront(); - })); - } } \ No newline at end of file diff --git a/src/main/java/mediathek/javafx/tool/JavaFxUtils.java b/src/main/java/mediathek/javafx/tool/JavaFxUtils.java index a6d9d5bb92..d58c27e3a2 100644 --- a/src/main/java/mediathek/javafx/tool/JavaFxUtils.java +++ b/src/main/java/mediathek/javafx/tool/JavaFxUtils.java @@ -6,9 +6,6 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; -import javax.swing.*; -import java.awt.image.BufferedImage; -import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; @@ -34,33 +31,6 @@ static public void invokeInFxThreadAndWait(final Runnable run) { } } - /** - * Invoke on the JavaFx thread and wait for it to return. Be very careful - * with this because this can cause deadlocks. This method will return - * something. - */ - static public V invokeInFxThreadAndWait(final Callable call) { - V result = null; - - if (Platform.isFxApplicationThread()) { - try { - result = call.call(); - } catch (Exception e) { - logger.error("invokeInFxThreadAndWait() failed", e); - } - } else { - try { - FutureTask future = new FutureTask<>(call); - Platform.runLater(future); - result = future.get(); - } catch (ExecutionException | InterruptedException e) { - logger.error("invokeInFxThreadAndWait() failed", e); - } - } - - return result; - } - /** * Convert a swing color to JavaFX. * @param awtColor the swing color. @@ -74,18 +44,4 @@ public static Color toFXColor(@NotNull java.awt.Color awtColor) { double opacity = a / 255.0; return javafx.scene.paint.Color.rgb(r, g, b, opacity); } - - /** - * Convert ImageIcon to BufferedImage. - * This can be used in a JavaFX ImageView. - * @param icon the ImageIcon from Swing. - * @return the BufferedImage for JavaFX use. - */ - public static BufferedImage toBufferedImage(ImageIcon icon) { - var bi = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); - var g = bi.createGraphics(); - icon.paintIcon(null, g, 0, 0); - g.dispose(); - return bi; - } } diff --git a/src/main/java/mediathek/javafx/tool/TableViewColumnContextMenuHelper.java b/src/main/java/mediathek/javafx/tool/TableViewColumnContextMenuHelper.java index 49efc323db..8a238fb06f 100644 --- a/src/main/java/mediathek/javafx/tool/TableViewColumnContextMenuHelper.java +++ b/src/main/java/mediathek/javafx/tool/TableViewColumnContextMenuHelper.java @@ -41,15 +41,9 @@ private void registerListeners() { } private Optional getMenuButton() { - var tableHeaderRow = getTableHeaderRow(); - if (tableHeaderRow.isEmpty()) { - return Optional.empty(); - } - - // child identified as cornerRegion in TableHeaderRow.java - return tableHeaderRow.get().getChildren().stream() + return getTableHeaderRow().flatMap(headerRow -> headerRow.getChildren().stream() .filter(child -> child.getStyleClass().contains("show-hide-columns-button")) - .findAny(); + .findAny()); } private Optional getTableHeaderRow() { diff --git a/src/main/java/mediathek/mac/MediathekGuiMac.java b/src/main/java/mediathek/mac/MediathekGuiMac.java deleted file mode 100644 index a9c7a3ce7c..0000000000 --- a/src/main/java/mediathek/mac/MediathekGuiMac.java +++ /dev/null @@ -1,139 +0,0 @@ -package mediathek.mac; - -import mediathek.config.Konstanten; -import mediathek.gui.actions.ShowAboutAction; -import mediathek.gui.messages.DownloadFinishedEvent; -import mediathek.gui.messages.DownloadStartEvent; -import mediathek.gui.messages.InstallTabSwitchListenerEvent; -import mediathek.mainwindow.MediathekGui; -import mediathek.tool.notification.INotificationCenter; -import mediathek.tool.notification.MacNotificationCenter; -import mediathek.tool.threads.IndicatorThread; -import net.engio.mbassy.listener.Handler; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.awt.*; -import java.io.IOException; - -public class MediathekGuiMac extends MediathekGui { - protected static final Logger logger = LogManager.getLogger(MediathekGuiMac.class); - private final OsxPowerManager powerManager = new OsxPowerManager(); - - @Override - protected boolean officialLauncherInUse() { - boolean macOSBinaryInuse = true; - final var osxOfficialApp = System.getProperty(Konstanten.MACOS_OFFICIAL_APP); - if (osxOfficialApp == null || osxOfficialApp.isEmpty() || osxOfficialApp.equalsIgnoreCase("false")) { - macOSBinaryInuse = false; - } - return macOSBinaryInuse; - } - - @Override - protected void installAdditionalHelpEntries() { - //unused on macOS - } - - @Override - public void initializeSystemTray() { - //we don´t use it on macOS - } - - @Override - protected INotificationCenter getNotificationCenter() { - return new MacNotificationCenter(); - } - - @Override - protected void shutdownComputer() { - try { - Runtime.getRuntime().exec("nohup bin/mv_shutdown_helper"); - } catch (IOException e) { - logger.error(e); - } - } - - @Override - protected void installMenuTabSwitchListener() { - //do not use on OS X as it violates HIG... - } - - @Override - @Handler - protected void handleInstallTabSwitchListenerEvent(InstallTabSwitchListenerEvent msg) { - //do not use on OS X as it violates HIG... - } - - @Override - protected void initMenus() { - super.initMenus(); - - setupUserInterfaceForOsx(); - } - - @Override - protected IndicatorThread createProgressIndicatorThread() { - return new OsxIndicatorThread(); - } - - @Override - protected void handleDownloadStart(DownloadStartEvent msg) { - super.handleDownloadStart(msg); - powerManager.disablePowerManagement(); - - setDownloadsBadge(numDownloadsStarted.get()); - } - - @Override - protected void handleDownloadFinishedEvent(DownloadFinishedEvent msg) { - super.handleDownloadFinishedEvent(msg); - - final int numDownloads = numDownloadsStarted.get(); - if (numDownloads == 0) - powerManager.enablePowerManagement(); - - setDownloadsBadge(numDownloads); - } - - /** - * Set the OS X dock icon badge to the number of running downloads. - * - * @param numDownloads The number of active downloads. - */ - private void setDownloadsBadge(int numDownloads) { - if (Taskbar.isTaskbarSupported()) { - var taskbar = Taskbar.getTaskbar(); - if (taskbar.isSupported(Taskbar.Feature.ICON_BADGE_NUMBER)) { - if (numDownloads > 0) - taskbar.setIconBadge(Integer.toString(numDownloads)); - else { - taskbar.setIconBadge(""); - } - } - } - } - - /** - * Setup the UI for OS X - */ - private void setupUserInterfaceForOsx() { - Desktop desktop = Desktop.getDesktop(); - desktop.disableSuddenTermination(); - - if (desktop.isSupported(Desktop.Action.APP_QUIT_HANDLER)) - desktop.setQuitHandler((e, response) -> { - if (!beenden(false, false)) { - response.cancelQuit(); - } else { - response.performQuit(); - } - }); - - if (desktop.isSupported(Desktop.Action.APP_ABOUT)) - desktop.setAboutHandler(e -> new ShowAboutAction().actionPerformed(null)); - - if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) - desktop.setPreferencesHandler(e -> getSettingsDialog().setVisible(true)); - } -} diff --git a/src/main/java/mediathek/mac/MediathekGuiMac.kt b/src/main/java/mediathek/mac/MediathekGuiMac.kt new file mode 100644 index 0000000000..385c9220a3 --- /dev/null +++ b/src/main/java/mediathek/mac/MediathekGuiMac.kt @@ -0,0 +1,131 @@ +package mediathek.mac + +import com.formdev.flatlaf.util.SystemInfo +import mediathek.gui.actions.ShowAboutAction +import mediathek.gui.messages.DownloadFinishedEvent +import mediathek.gui.messages.DownloadStartEvent +import mediathek.gui.messages.InstallTabSwitchListenerEvent +import mediathek.mainwindow.MediathekGui +import mediathek.tool.notification.INotificationCenter +import mediathek.tool.notification.MacNotificationCenter +import mediathek.tool.threads.IndicatorThread +import net.engio.mbassy.listener.Handler +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.Logger +import java.awt.Desktop +import java.awt.Taskbar +import java.awt.desktop.QuitEvent +import java.awt.desktop.QuitResponse +import java.io.IOException +import javax.swing.Box + +class MediathekGuiMac : MediathekGui() { + private val powerManager = OsxPowerManager() + override fun installAdditionalHelpEntries() { + //unused on macOS + } + + override fun setupScrollBarWidth() { + // unused on macOS + } + + override fun initializeSystemTray() { + //we don´t use it on macOS + } + + override fun getNotificationCenter(): INotificationCenter { + return MacNotificationCenter() + } + + override fun shutdownComputer() { + try { + Runtime.getRuntime().exec("nohup bin/mv_shutdown_helper") + } catch (e: IOException) { + logger.error(e) + } + } + + override fun installMenuTabSwitchListener() { + //do not use on OS X as it violates HIG... + } + + @Handler + override fun handleInstallTabSwitchListenerEvent(msg: InstallTabSwitchListenerEvent) { + //do not use on OS X as it violates HIG... + } + + override fun initMenus() { + super.initMenus() + setupUserInterfaceForOsx() + } + + override fun createProgressIndicatorThread(): IndicatorThread { + return OsxIndicatorThread() + } + + override fun handleDownloadStart(msg: DownloadStartEvent) { + super.handleDownloadStart(msg) + powerManager.disablePowerManagement() + setDownloadsBadge(numDownloadsStarted.get()) + } + + override fun handleDownloadFinishedEvent(msg: DownloadFinishedEvent) { + super.handleDownloadFinishedEvent(msg) + val numDownloads = numDownloadsStarted.get() + if (numDownloads == 0) powerManager.enablePowerManagement() + setDownloadsBadge(numDownloads) + } + + /** + * Set the OS X dock icon badge to the number of running downloads. + * + * @param numDownloads The number of active downloads. + */ + private fun setDownloadsBadge(numDownloads: Int) { + if (Taskbar.isTaskbarSupported()) { + val taskbar = Taskbar.getTaskbar() + if (taskbar.isSupported(Taskbar.Feature.ICON_BADGE_NUMBER)) { + if (numDownloads > 0) taskbar.setIconBadge(numDownloads.toString()) else { + taskbar.setIconBadge("") + } + } + } + } + + /** + * Setup the UI for OS X + */ + private fun setupUserInterfaceForOsx() { + val desktop = Desktop.getDesktop() + + desktop.disableSuddenTermination() + if (desktop.isSupported(Desktop.Action.APP_QUIT_HANDLER)) { + desktop.setQuitHandler { _: QuitEvent?, response: QuitResponse -> + if (!quitApplication()) { + response.cancelQuit() + } else { + //should never be reached from quitApplication() + response.performQuit() + } + } + } + if (desktop.isSupported(Desktop.Action.APP_ABOUT)) { + desktop.setAboutHandler { ShowAboutAction().actionPerformed(null) } + } + + if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) { + desktop.setPreferencesHandler { settingsDialog.isVisible = true } + } + + getRootPane().putClientProperty("apple.awt.windowTitleVisible", false) + if (SystemInfo.isMacFullWindowContentSupported) { + getRootPane().putClientProperty("apple.awt.fullWindowContent", true) + getRootPane().putClientProperty("apple.awt.transparentTitleBar", true) + commonToolBar.add(Box.createHorizontalStrut(70), 0) + } + } + + companion object { + val logger: Logger = LogManager.getLogger() + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/mac/SpotlightCommentWriter.java b/src/main/java/mediathek/mac/SpotlightCommentWriter.java index b908668850..c81cd3f1b0 100644 --- a/src/main/java/mediathek/mac/SpotlightCommentWriter.java +++ b/src/main/java/mediathek/mac/SpotlightCommentWriter.java @@ -1,9 +1,9 @@ package mediathek.mac; -import javafx.application.Platform; import mediathek.config.Konstanten; import mediathek.daten.DatenDownload; -import mediathek.tool.javafx.FXErrorDialog; +import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SwingErrorDialog; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -62,11 +62,9 @@ public void writeComment(final DatenDownload datenDownload) { builder.start().waitFor(5, TimeUnit.SECONDS); logger.trace("Spotlight writing finished"); } catch (Exception ex) { - Platform.runLater(() -> FXErrorDialog.showErrorDialog("Fehler", - "Fehler beim Schreiben des Spotlight-Kommentars", + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), "Es trat ein Fehler beim Schreiben des Spotlight-Kommentars auf.\n" + - "Sollte dieser häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", - ex)); + "Sollte dieser häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", ex); logger.error("Fehler beim Spotlight schreiben: {}", filmPath.toString(), ex); //AppleScript may not be available if user does not use the official MacApp. //We need to log that as well if there are error reports. diff --git a/src/main/java/mediathek/mainwindow/DownloadInformationLabel.java b/src/main/java/mediathek/mainwindow/DownloadInformationLabel.java new file mode 100644 index 0000000000..033b9752c7 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/DownloadInformationLabel.java @@ -0,0 +1,69 @@ +package mediathek.mainwindow; + +import mediathek.config.Daten; +import mediathek.gui.messages.DownloadInfoUpdateAvailableEvent; +import mediathek.tool.FileSize; +import mediathek.tool.MessageBus; +import net.engio.mbassy.listener.Handler; + +import javax.swing.*; + +public class DownloadInformationLabel extends JLabel { + public DownloadInformationLabel() { + MessageBus.getMessageBus().subscribe(this); + } + + @Handler + private void handleDownloadInfoUpdate(DownloadInfoUpdateAvailableEvent e) { + SwingUtilities.invokeLater(this::setInfoFilme); + } + + private void setInfoFilme() { + setText(getInfoTextDownloads()); + } + + private String getInfoTextDownloads() { + String textLinks; + final var daten = Daten.getInstance(); + final var listeDownloads = daten.getListeDownloads(); + final var info = listeDownloads.getStarts(); + + textLinks = (info.total_num_download_list_entries == 1) ? + "1 Download" : info.total_num_download_list_entries + " Downloads"; + + if (info.hasValues()) { + textLinks += ": "; + + textLinks += (info.running == 1) ? "1 läuft" : info.running + " laufen"; + + if (info.running > 0) { + textLinks += " (" + daten.getDownloadInfos().getBandwidthStr() + ')'; + + var infos = daten.getDownloadInfos(); + final long byteAlleDownloads = infos.getByteAlleDownloads(); + final long byteAktDownloads = infos.getByteAktDownloads(); + if (byteAlleDownloads > 0 || byteAktDownloads > 0) { + textLinks += " ("; + textLinks += "Größe: "; + if (byteAktDownloads > 0) { + textLinks += FileSize.convertSize(byteAktDownloads) + " von " + + FileSize.convertSize(byteAlleDownloads) + " MByte)"; + } else { + textLinks += FileSize.convertSize(byteAlleDownloads) + " MByte)"; + } + } + + } + + textLinks += (info.initialized == 1) ? ", 1 wartet" : ", " + info.initialized + " warten"; + + if (info.finished > 0) + textLinks += (info.finished == 1) ? ", 1 fertig" : ", " + info.finished + " fertig"; + + if (info.error > 0) + textLinks += (info.error == 1) ? ", 1 fehlerhaft" : ", " + info.error + " fehlerhaft"; + } + + return textLinks; + } +} diff --git a/src/main/java/mediathek/mainwindow/FilmAgeLabel.kt b/src/main/java/mediathek/mainwindow/FilmAgeLabel.kt new file mode 100644 index 0000000000..508609e40a --- /dev/null +++ b/src/main/java/mediathek/mainwindow/FilmAgeLabel.kt @@ -0,0 +1,60 @@ +package mediathek.mainwindow + +import mediathek.config.Daten +import org.apache.commons.lang3.time.DurationFormatUtils +import java.awt.event.ActionEvent +import java.awt.event.ActionListener +import java.time.Duration +import java.util.* +import java.util.concurrent.TimeUnit +import javax.swing.JLabel +import javax.swing.Timer + +class FilmAgeLabel : JLabel(), ActionListener { + private data class FilmListAge(val hours: Long, val minutes: Long) + + private var oldAge = FilmListAge(0, 0) + + init { + toolTipText = "Alter der Filmliste" + setAgeToLabel() + + //start the update timer + val timer = Timer(1000, this) + timer.isRepeats = true + timer.start() + } + + private fun calculateFilmListAge(): FilmListAge { + val duration = Duration.ofSeconds(Daten.getInstance().listeFilme.metaData.ageInSeconds) + var minutes = duration.toMinutes() + val hours = minutes / 60 + minutes -= hours * 60 + return FilmListAge(hours, minutes) + } + + @Throws(IllegalFormatException::class) + private fun computeAgeString(age: FilmListAge): String { + return if (age.hours == 0L) { + "Alter: ${age.minutes}m" + } else if (age.hours >= 24) { + val duration = TimeUnit.MILLISECONDS.convert(age.hours * 60 + age.minutes, TimeUnit.MINUTES) + DurationFormatUtils.formatDuration(duration, "'Alter: 'dd'd' HH'h' mm'm'", true) + } else { + "Alter: ${age.hours}h ${age.minutes}m" + } + } + + private fun setAgeToLabel() { + val curAge = calculateFilmListAge() + if (curAge != oldAge) { + val result = computeAgeString(curAge) + text = result + oldAge = curAge + } + } + + override fun actionPerformed(e: ActionEvent) { + setAgeToLabel() + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/mainwindow/FilmListCreationDateLabel.java b/src/main/java/mediathek/mainwindow/FilmListCreationDateLabel.java new file mode 100644 index 0000000000..13c511e279 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/FilmListCreationDateLabel.java @@ -0,0 +1,39 @@ +package mediathek.mainwindow; + +import mediathek.config.Daten; +import mediathek.daten.FilmListMetaData; +import mediathek.gui.messages.FilmListReadStopEvent; +import mediathek.tool.MessageBus; +import net.engio.mbassy.listener.Handler; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +public class FilmListCreationDateLabel extends JLabel implements PropertyChangeListener { + public FilmListCreationDateLabel() { + var blacklist = Daten.getInstance().getListeFilmeNachBlackList(); + setText(blacklist.getMetaData()); + //works only on blacklist! + blacklist.addMetaDataChangeListener(this); + + MessageBus.getMessageBus().subscribe(this); + } + + private void setText(@NotNull FilmListMetaData metaData) { + var text = String.format("Filmliste erstellt: %s Uhr", metaData.getGenerationDateTimeAsString()); + SwingUtilities.invokeLater(() -> setText(text)); + } + + @Handler + private void handleFilmListStop(@NotNull FilmListReadStopEvent e) { + setText(Daten.getInstance().getListeFilmeNachBlackList().getMetaData()); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + var metaData = (FilmListMetaData) evt.getNewValue(); + setText(metaData); + } +} diff --git a/src/main/java/mediathek/mainwindow/FilmSizeInfoLabel.java b/src/main/java/mediathek/mainwindow/FilmSizeInfoLabel.java new file mode 100644 index 0000000000..c5558769b6 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/FilmSizeInfoLabel.java @@ -0,0 +1,71 @@ +package mediathek.mainwindow; + +import mediathek.config.Daten; +import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; +import mediathek.tool.MessageBus; +import net.engio.mbassy.listener.Handler; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class FilmSizeInfoLabel extends JLabel implements ActionListener { + private int oldGesamt; + private int oldRowCount; + private final MediathekGui mediathekGui; + + private final Timer timer; + + public FilmSizeInfoLabel(@NotNull MediathekGui mediathekGui) { + this.mediathekGui = mediathekGui; + + timer = new Timer(1000, this); + timer.setRepeats(true); + timer.start(); + + MessageBus.getMessageBus().subscribe(this); + } + + @Handler + private void handleLeftDisplayUpdate(UpdateStatusBarLeftDisplayEvent e) { + SwingUtilities.invokeLater(this::updateValues); + } + + private void updateValues() { + String textLinks; + final int gesamt = Daten.getInstance().getListeFilme().size(); + final int rowCount = mediathekGui.tabFilme.getTableRowCount(); + + if (gesamt == oldGesamt && rowCount == oldRowCount) + return; + + // Anzahl der Filme + if (gesamt == rowCount) { + textLinks = createFilmLabel(rowCount); + } else { + textLinks = createFilmLabel(rowCount); + textLinks += " (Insgesamt: " + gesamt + ")"; + } + + setText(textLinks); + + oldGesamt = gesamt; + oldRowCount = rowCount; + } + + private String createFilmLabel(final int rowCount) { + String textLinks; + if (rowCount == 1) + textLinks = "1 Film"; + else + textLinks = rowCount + " Filme"; + + return textLinks; + } + + @Override + public void actionPerformed(ActionEvent e) { + updateValues(); + } +} diff --git a/src/main/java/mediathek/mainwindow/FixedRedrawStatusBar.java b/src/main/java/mediathek/mainwindow/FixedRedrawStatusBar.java new file mode 100644 index 0000000000..cd6b7fbd60 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/FixedRedrawStatusBar.java @@ -0,0 +1,40 @@ +package mediathek.mainwindow; + +import org.jdesktop.swingx.JXStatusBar; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; + +/** + * This class tries to fix some redraw issues with JXStatusBar on removal. + */ +public class FixedRedrawStatusBar extends JXStatusBar { + public FixedRedrawStatusBar(@NotNull MediathekGui mediathekGui) { + add(new SelectedListItemsLabel(mediathekGui)); + add(new FilmSizeInfoLabel(mediathekGui)); + add(new DownloadInformationLabel()); + + add(new JPanel(), new Constraint(Constraint.ResizeBehavior.FILL)); + add(new FilmListCreationDateLabel()); + add(new FilmAgeLabel()); + } + + @Override + public void remove(int index) { + super.remove(index); + this.revalidate(); + } + + @Override + public void remove(Component comp) { + super.remove(comp); + this.revalidate(); + } + + @Override + public void removeAll() { + super.removeAll(); + this.revalidate(); + } +} diff --git a/src/main/java/mediathek/mainwindow/FontManager.java b/src/main/java/mediathek/mainwindow/FontManager.java new file mode 100644 index 0000000000..abe985a733 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/FontManager.java @@ -0,0 +1,142 @@ +package mediathek.mainwindow; + +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.extras.FlatAnimatedLafChange; +import com.formdev.flatlaf.ui.FlatUIUtils; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.text.StyleContext; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Helper class to globally change to L&F font sizes + */ +public class FontManager { + private final MediathekGui mediathekGui; + private final String[] availableFontFamilyNames; + private int initialFontMenuItemCount = -1; + + public FontManager(@NotNull MediathekGui mediathekGui) { + this.mediathekGui = mediathekGui; + availableFontFamilyNames = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getAvailableFontFamilyNames().clone(); + Arrays.sort(availableFontFamilyNames); + } + + /** + * Reset used L&F font back to default. + */ + public void resetFont() { + UIManager.put("defaultFont", null); + updateFontMenuItems(); + FlatLaf.updateUI(); + } + + public void increaseFontSize() { + Font font = UIManager.getFont("defaultFont"); + Font newFont = font.deriveFont((float) (font.getSize() + 1)); + UIManager.put("defaultFont", newFont); + + updateFontMenuItems(); + FlatLaf.updateUI(); + } + + public void decreaseFontSize() { + Font font = UIManager.getFont("defaultFont"); + Font newFont = font.deriveFont((float) Math.max(font.getSize() - 1, 10)); + UIManager.put("defaultFont", newFont); + + updateFontMenuItems(); + FlatLaf.updateUI(); + } + + public void updateFontMenuItems() { + if (initialFontMenuItemCount < 0) + initialFontMenuItemCount = mediathekGui.fontMenu.getItemCount(); + else { + // remove old font items + for (int i = mediathekGui.fontMenu.getItemCount() - 1; i >= initialFontMenuItemCount; i--) + mediathekGui.fontMenu.remove(i); + } + + // get current font + Font currentFont = UIManager.getFont("Label.font"); + String currentFamily = currentFont.getFamily(); + String currentSize = Integer.toString(currentFont.getSize()); + + // add font families + mediathekGui.fontMenu.addSeparator(); + ArrayList families = new ArrayList<>(Arrays.asList( + "Arial", "Cantarell", "Comic Sans MS", "DejaVu Sans", + "Dialog", "Liberation Sans", "Noto Sans", "Roboto", + "SansSerif", "Segoe UI", "Serif", "Tahoma", "Ubuntu", "Verdana")); + if (!families.contains(currentFamily)) + families.add(currentFamily); + families.sort(String.CASE_INSENSITIVE_ORDER); + + ButtonGroup familiesGroup = new ButtonGroup(); + for (String family : families) { + if (Arrays.binarySearch(availableFontFamilyNames, family) < 0) + continue; // not available + + JCheckBoxMenuItem item = new JCheckBoxMenuItem(family); + item.setSelected(family.equals(currentFamily)); + item.addActionListener(this::fontFamilyChanged); + mediathekGui.fontMenu.add(item); + + familiesGroup.add(item); + } + + // add font sizes + mediathekGui.fontMenu.addSeparator(); + ArrayList sizes = new ArrayList<>(Arrays.asList( + "10", "11", "12", "14", "16", "18", "20", "24", "28")); + if (!sizes.contains(currentSize)) + sizes.add(currentSize); + sizes.sort(String.CASE_INSENSITIVE_ORDER); + + ButtonGroup sizesGroup = new ButtonGroup(); + for (String size : sizes) { + JCheckBoxMenuItem item = new JCheckBoxMenuItem(size); + item.setSelected(size.equals(currentSize)); + item.addActionListener(this::fontSizeChanged); + mediathekGui.fontMenu.add(item); + + sizesGroup.add(item); + } + + // enabled/disable items + boolean enabled = UIManager.getLookAndFeel() instanceof FlatLaf; + for (Component item : mediathekGui.fontMenu.getMenuComponents()) + item.setEnabled(enabled); + } + + private void fontFamilyChanged(ActionEvent e) { + String fontFamily = e.getActionCommand(); + + FlatAnimatedLafChange.showSnapshot(); + + Font font = UIManager.getFont("defaultFont"); + Font newFont = StyleContext.getDefaultStyleContext().getFont(fontFamily, font.getStyle(), font.getSize()); + // StyleContext.getFont() may return a UIResource, which would cause loosing user scale factor on Windows + newFont = FlatUIUtils.nonUIResource(newFont); + UIManager.put("defaultFont", newFont); + + FlatLaf.updateUI(); + FlatAnimatedLafChange.hideSnapshotWithAnimation(); + } + + private void fontSizeChanged(ActionEvent e) { + String fontSizeStr = e.getActionCommand(); + + Font font = UIManager.getFont("defaultFont"); + Font newFont = font.deriveFont((float) Integer.parseInt(fontSizeStr)); + UIManager.put("defaultFont", newFont); + + FlatLaf.updateUI(); + } +} diff --git a/src/main/java/mediathek/mainwindow/ListSelectedItemsProperty.java b/src/main/java/mediathek/mainwindow/ListSelectedItemsProperty.java new file mode 100644 index 0000000000..1104305cc3 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/ListSelectedItemsProperty.java @@ -0,0 +1,23 @@ +package mediathek.mainwindow; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +public class ListSelectedItemsProperty { + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private long selectedItems; + + public ListSelectedItemsProperty(long selectedItems) { + this.selectedItems = selectedItems; + } + + public void addSelectedItemsChangeListener(PropertyChangeListener listener) { + this.pcs.addPropertyChangeListener(listener); + } + + public void setSelectedItems(long selectedItems) { + long oldValue = this.selectedItems; + this.selectedItems = selectedItems; + this.pcs.firePropertyChange("sel_items", oldValue, selectedItems); + } +} diff --git a/src/main/java/mediathek/mainwindow/Log4jShutdownHookThread.kt b/src/main/java/mediathek/mainwindow/Log4jShutdownHookThread.kt new file mode 100644 index 0000000000..a89a049258 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/Log4jShutdownHookThread.kt @@ -0,0 +1,14 @@ +package mediathek.mainwindow + +import mediathek.tool.Log4jShutdownCallbackRegistry.Companion.execute + +/** + * Gracefully shutdown config and log. + * This may be necessary in case the app is not properly quit. + */ +class Log4jShutdownHookThread : Thread() { + override fun run() { + //shut down log4j + execute() + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index afdf37f701..e169005c8d 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -1,41 +1,25 @@ package mediathek.mainwindow; +import com.sun.jna.platform.win32.VersionHelpers; import javafx.application.Platform; -import javafx.beans.property.IntegerProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.collections.ObservableList; -import javafx.concurrent.WorkerStateEvent; -import javafx.embed.swing.JFXPanel; -import javafx.event.EventHandler; -import javafx.scene.Node; -import javafx.scene.Scene; -import javafx.scene.control.Alert; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.HBox; -import javafx.stage.Modality; import javafx.stage.Stage; import mediathek.Main; -import mediathek.config.Config; -import mediathek.config.Daten; -import mediathek.config.Icons; -import mediathek.config.Konstanten; +import mediathek.config.*; import mediathek.controller.history.SeenHistoryController; import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; +import mediathek.daten.IndexedFilmList; import mediathek.filmeSuchen.ListenerFilmeLaden; import mediathek.filmeSuchen.ListenerFilmeLadenEvent; import mediathek.filmlisten.FilmeLaden; +import mediathek.filmlisten.reader.FilmListReader; import mediathek.gui.MVTray; -import mediathek.gui.TabPaneIndex; import mediathek.gui.actions.*; -import mediathek.gui.actions.export.FilmListExportAction; +import mediathek.gui.actions.export.ExportDecompressedFilmlistAction; +import mediathek.gui.actions.export.ExportReadableFilmlistAction; import mediathek.gui.actions.import_actions.ImportOldAbosAction; import mediathek.gui.actions.import_actions.ImportOldBlacklistAction; import mediathek.gui.actions.import_actions.ImportOldReplacementListAction; -import mediathek.gui.bandwidth.BandwidthMonitorController; import mediathek.gui.dialog.DialogBeenden; import mediathek.gui.dialog.LoadFilmListDialog; import mediathek.gui.dialogEinstellungen.DialogEinstellungen; @@ -45,13 +29,13 @@ import mediathek.gui.messages.*; import mediathek.gui.tabs.tab_downloads.GuiDownloads; import mediathek.gui.tabs.tab_film.GuiFilme; -import mediathek.javafx.*; -import mediathek.javafx.tool.FXProgressPane; +import mediathek.gui.tasks.BlacklistFilterWorker; +import mediathek.gui.tasks.LuceneIndexWorker; +import mediathek.gui.tasks.RefreshAboWorker; import mediathek.javafx.tool.JFXHiddenApplication; import mediathek.javafx.tool.JavaFxUtils; import mediathek.res.GetIcon; import mediathek.tool.*; -import mediathek.tool.http.MVHttpClient; import mediathek.tool.notification.GenericNotificationCenter; import mediathek.tool.notification.INotificationCenter; import mediathek.tool.notification.NullNotificationCenter; @@ -72,7 +56,6 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; -import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.NoSuchElementException; import java.util.concurrent.CompletableFuture; @@ -85,7 +68,7 @@ public class MediathekGui extends JFrame { - protected static final Logger logger = LogManager.getLogger(MediathekGui.class); + protected static final Logger logger = LogManager.getLogger(); private static final String ICON_NAME = "MediathekView.png"; private static final String ICON_PATH = "/mediathek/res/"; private static final int ICON_WIDTH = 58; @@ -98,72 +81,80 @@ public class MediathekGui extends JFrame { * "Pointer" to UI */ private static MediathekGui ui; + public final LoadFilmListAction loadFilmListAction; /** * Number of active downloads */ protected final AtomicInteger numDownloadsStarted = new AtomicInteger(0); protected final Daten daten = Daten.getInstance(); - protected final JTabbedPane tabbedPane = new JTabbedPane(); + protected final PositionSavingTabbedPane tabbedPane = new PositionSavingTabbedPane(); protected final JMenu jMenuHilfe = new JMenu(); + protected final SettingsAction settingsAction = new SettingsAction(this); + final JMenu fontMenu = new JMenu("Schrift"); private final JMenu jMenuDatei = new JMenu(); private final JMenu jMenuFilme = new JMenu(); private final JMenuBar jMenuBar = new JMenuBar(); private final JMenu jMenuDownload = new JMenu(); private final JMenu jMenuAbos = new JMenu(); private final JMenu jMenuAnsicht = new JMenu(); - /** - * this property keeps track how many items are currently selected in the active table view - */ - private final IntegerProperty selectedItemsProperty = new SimpleIntegerProperty(0); - /** - * Helper to determine what tab is currently active - */ - private final ObjectProperty tabPaneIndexProperty = new SimpleObjectProperty<>(TabPaneIndex.NONE); private final HashMap menuListeners = new HashMap<>(); - private final JCheckBoxMenuItem cbBandwidthDisplay = new JCheckBoxMenuItem("Bandbreitennutzung"); - private final JFXPanel statusBarPanel = new JFXPanel(); - private final LoadFilmListAction loadFilmListAction; private final SearchProgramUpdateAction searchProgramUpdateAction; - private final MemoryMonitorAction showMemoryMonitorAction = new MemoryMonitorAction(); + private final MemoryMonitorAction showMemoryMonitorAction = new MemoryMonitorAction(this); private final InfoDialog filmInfo; + private final ManageAboAction manageAboAction = new ManageAboAction(); + private final ShowBandwidthUsageAction showBandwidthUsageAction = new ShowBandwidthUsageAction(this); + public FixedRedrawStatusBar swingStatusBar; public GuiFilme tabFilme; public GuiDownloads tabDownloads; + public EditBlacklistAction editBlacklistAction = new EditBlacklistAction(this); + public ToggleBlacklistAction toggleBlacklistAction = new ToggleBlacklistAction(); + public ShowFilmInformationAction showFilmInformationAction = new ShowFilmInformationAction(); /** - * the global configuration for this app. + * this property keeps track how many items are currently selected in the active table view */ - protected Configuration config = ApplicationConfiguration.getConfiguration(); + public ListSelectedItemsProperty selectedListItemsProperty = new ListSelectedItemsProperty(0); /** - * Bandwidth monitoring for downloads. + * Used for status bar progress. */ - private BandwidthMonitorController bandwidthMonitor; + public JLabel progressLabel = new JLabel(); + /** + * Used for status bar progress. + */ + public JProgressBar progressBar = new JProgressBar(); + /** + * the global configuration for this app. + */ + protected Configuration config = ApplicationConfiguration.getConfiguration(); + protected JToolBar commonToolBar = new JToolBar(); + protected ManageBookmarkAction manageBookmarkAction = new ManageBookmarkAction(this); + protected FontManager fontManager = new FontManager(this); + protected ToggleDarkModeAction toggleDarkModeAction = new ToggleDarkModeAction(); private MVTray tray; private DialogEinstellungen dialogEinstellungen; - private StatusBarController statusBarController; private ProgramUpdateCheck programUpdateChecker; /** * Progress indicator thread for OS X and windows. */ private IndicatorThread progressIndicatorThread; - private ManageAboAction manageAboAction; private AutomaticFilmlistUpdate automaticFilmlistUpdate; - /** - * A weak reference to the table data model filtering progress indicator(s). - */ - private WeakReference indicatorLayout; + private boolean shutdownRequested; public MediathekGui() { ui = this; setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + setupScrollBarWidth(); + UIManager.put("TabbedPane.showTabSeparators", true); + + setupAlternatingRowColors(); + loadFilmListAction = new LoadFilmListAction(this); searchProgramUpdateAction = new SearchProgramUpdateAction(); Main.splashScreen.ifPresent(s -> s.update(UIProgressState.LOAD_MAINWINDOW)); - var contentPane = getContentPane(); - contentPane.setLayout(new BorderLayout()); - contentPane.add(statusBarPanel, BorderLayout.PAGE_END); + getContentPane().setLayout(new BorderLayout()); setIconAndWindowImage(); @@ -195,6 +186,8 @@ public MediathekGui() { setupNotificationCenter(); + createCommonToolBar(); + Main.splashScreen.ifPresent(s -> s.update(UIProgressState.FINISHED)); workaroundJavaFxInitializationBug(); @@ -223,7 +216,7 @@ public MediathekGui() { logger.trace("Loading bandwidth monitor"); if (config.getBoolean(ApplicationConfiguration.APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE, false)) { - getBandwidthMonitorController().setVisibility(); + showBandwidthUsageAction.actionPerformed(null); } logger.trace("Finished loading bandwidth monitor"); @@ -241,6 +234,48 @@ public static MediathekGui ui() { return ui; } + protected void setupScrollBarWidth() { + // win and linux users complain about scrollbars being too small... + UIManager.put( "ScrollBar.width", 16 ); + } + + public void setupAlternatingRowColors() { + // install alternate row color only for windows >8 and macOS, Linux + boolean installAlternateRowColor; + if (SystemUtils.IS_OS_WINDOWS && VersionHelpers.IsWindows8OrGreater()) { + installAlternateRowColor = true; + } else installAlternateRowColor = SystemUtils.IS_OS_MAC_OSX || SystemUtils.IS_OS_LINUX; + + if (installAlternateRowColor) + UIManager.put("Table.alternateRowColor", MVColor.getAlternatingRowColor()); + } + + protected void createCommonToolBar() { + commonToolBar.add(loadFilmListAction); + commonToolBar.add(showFilmInformationAction); + commonToolBar.add(toggleBlacklistAction); + commonToolBar.addSeparator(); + commonToolBar.add(editBlacklistAction); + commonToolBar.add(manageAboAction); + commonToolBar.add(manageBookmarkAction); + commonToolBar.addSeparator(); + commonToolBar.add(settingsAction); + commonToolBar.add(Box.createHorizontalGlue()); + commonToolBar.add(toggleDarkModeAction); + + if (!SystemUtils.IS_OS_MAC_OSX) { + commonToolBar.setFloatable(true); + commonToolBar.setName("Allgemein"); + } + + + if (!SystemUtils.IS_OS_MAC_OSX) { + tabbedPane.putClientProperty("JTabbedPane.trailingComponent", commonToolBar); + } + else + getContentPane().add(commonToolBar, BorderLayout.PAGE_START); + } + /** * Check if we encountered invalid regexps and warn user if necessary. * This needs to be delayed unfortunately as we can see result only after table has been filled. @@ -260,16 +295,12 @@ private void checkInvalidRegularExpressions() { """, regexStr); Filter.regExpErrorList.clear(); - Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText("Ungültige reguläre Ausdrücke gefunden"); - alert.setContentText(message); - alert.initModality(Modality.APPLICATION_MODAL); - alert.show(); - }); + JOptionPane.showMessageDialog(this, + message, + Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE); } - }, 30, TimeUnit.SECONDS); + }, 15, TimeUnit.SECONDS); } /** @@ -296,6 +327,7 @@ private void setupNotificationCenter() { /** * Return the platform-specific notification implementation. + * * @return generic or platform-specific notification implementation. */ protected INotificationCenter getNotificationCenter() { @@ -320,7 +352,7 @@ protected void closeNotificationCenter() { * This shutdown hook will try to save both log messages and write config changes to disk before app terminates. */ private void setupShutdownHook() { - Runtime.getRuntime().addShutdownHook(new ShutdownHookThread()); + Runtime.getRuntime().addShutdownHook(new Log4jShutdownHookThread()); } private void setupSystemTray() { @@ -334,14 +366,6 @@ public JTabbedPane getTabbedPane() { return tabbedPane; } - private BandwidthMonitorController getBandwidthMonitorController() { - if (bandwidthMonitor == null) { - bandwidthMonitor = new BandwidthMonitorController(this); - } - - return bandwidthMonitor; - } - private void setupTaskbarMenu() { var taskbar = Taskbar.getTaskbar(); if (taskbar.isSupported(Taskbar.Feature.MENU)) { @@ -387,6 +411,9 @@ private void createMenuBar() { jMenuAbos.setText("Abos"); jMenuBar.add(jMenuAbos); + if (!SystemUtils.IS_OS_MAC_OSX) + jMenuBar.add(fontMenu); + jMenuAnsicht.setMnemonic('a'); jMenuAnsicht.setText("Ansicht"); jMenuBar.add(jMenuAnsicht); @@ -420,7 +447,8 @@ protected void workaroundJavaFxInitializationBug() { } private void createMemoryMonitor() { - if (Config.isDebugModeEnabled()) + boolean visible = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.MemoryMonitorDialog.VISIBLE, false); + if (visible) showMemoryMonitorAction.showMemoryMonitor(); } @@ -428,65 +456,75 @@ private void createMemoryMonitor() { * Read a local filmlist or load a new one in auto mode. */ private void loadFilmlist() { - Platform.runLater(() -> { - //don´t write filmlist when we are reading only... - var writeCondition = !(GuiFunktionen.getFilmListUpdateType() == FilmListUpdateType.AUTOMATIC && daten.getListeFilme().needsUpdate()); - Daten.dontWriteFilmlistOnStartup.set(writeCondition); - - FXProgressPane progressPane = new FXProgressPane(); - - FilmListReaderTask filmListReaderTask = new FilmListReaderTask(); - filmListReaderTask.setOnRunning(e -> { - statusBarController.getStatusBar().getRightItems().add(progressPane); - progressPane.bindTask(filmListReaderTask); - }); - - FilmListNetworkReaderTask networkTask = new FilmListNetworkReaderTask(); - networkTask.setOnRunning(e -> progressPane.bindTask(networkTask)); - - FilmListFilterTask filterTask = new FilmListFilterTask(true); - filterTask.setOnRunning(e -> progressPane.bindTask(filterTask)); - final EventHandler workerStateEventEventHandler = e -> statusBarController.getStatusBar().getRightItems().remove(progressPane); - filterTask.setOnSucceeded(workerStateEventEventHandler); - filterTask.setOnFailed(workerStateEventEventHandler); - - CompletableFuture.runAsync(filmListReaderTask) - .thenRun(networkTask) - .thenRun(filterTask); - - //reset after first load has happened - Daten.dontWriteFilmlistOnStartup.set(false); - }); - } + //don´t write filmlist when we are reading only... + var writeCondition = !(GuiFunktionen.getFilmListUpdateType() == FilmListUpdateType.AUTOMATIC && daten.getListeFilme().needsUpdate()); + Daten.dontWriteFilmlistOnStartup.set(writeCondition); + + swingStatusBar.add(progressLabel); + swingStatusBar.add(progressBar); + + var worker = CompletableFuture.runAsync(() -> { + logger.trace("Reading local filmlist"); + MessageBus.getMessageBus().publishAsync(new FilmListReadStartEvent()); + + try (FilmListReader reader = new FilmListReader()) { + final int num_days = ApplicationConfiguration.getConfiguration().getInt(ApplicationConfiguration.FilmList.LOAD_NUM_DAYS, 0); + reader.readFilmListe(StandardLocations.getFilmlistFilePathString(), daten.getListeFilme(), num_days); + } + MessageBus.getMessageBus().publishAsync(new FilmListReadStopEvent()); + }) + .thenRun(() -> { + logger.trace("Check for filmlist updates"); + if (GuiFunktionen.getFilmListUpdateType() == FilmListUpdateType.AUTOMATIC && daten.getListeFilme().needsUpdate()) { + daten.getFilmeLaden().loadFilmlist("", true); + } + }) + .thenRun(new RefreshAboWorker(progressLabel, progressBar)) + .thenRun(new BlacklistFilterWorker(progressLabel, progressBar)); + + if (daten.getListeFilmeNachBlackList() instanceof IndexedFilmList){ + worker = worker.thenRun(new LuceneIndexWorker(progressLabel, progressBar)); + } - public IntegerProperty getSelectedItemsProperty() { - return selectedItemsProperty; + worker.thenRun(() -> SwingUtilities.invokeLater(() -> Daten.getInstance().getFilmeLaden().notifyFertig(new ListenerFilmeLadenEvent("", "", 100, 100, false)))) + .thenRun(() -> Daten.dontWriteFilmlistOnStartup.set(false)) + .thenRun(() -> SwingUtilities.invokeLater(() -> { + swingStatusBar.remove(progressBar); + swingStatusBar.remove(progressLabel); + })); } /** * Create the status bar item. */ private void createStatusBar() { - statusBarController = new StatusBarController(daten); + swingStatusBar = new FixedRedrawStatusBar(this); + getContentPane().add(swingStatusBar, BorderLayout.SOUTH); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - statusBarPanel.setScene(new Scene(statusBarController.createStatusBar())); - installSelectedItemsLabel(); - }); + createFilmlistDownloadProgress(); } - private void installSelectedItemsLabel() { - ObservableList leftItems = statusBarController.getStatusBar().getLeftItems(); - leftItems.add(0, new SelectedItemsLabel(selectedItemsProperty)); - leftItems.add(1, new VerticalSeparator()); - } - - public StatusBarController getStatusBarController() { - return statusBarController; - } + private void createFilmlistDownloadProgress() { + daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { + @Override + public void start(ListenerFilmeLadenEvent event) { + swingStatusBar.add(progressLabel); + swingStatusBar.add(progressBar); + } - public ObjectProperty tabPaneIndexProperty() { - return tabPaneIndexProperty; + @Override + public void progress(ListenerFilmeLadenEvent event) { + if (event.max == 0 || event.progress == event.max) { + progressBar.setIndeterminate(true); + } else { + progressBar.setIndeterminate(false); + progressBar.setMinimum(0); + progressBar.setMaximum(event.max); + progressBar.setValue(event.progress); + } + progressLabel.setText(event.text); + } + }); } public InfoDialog getFilmInfoDialog() { @@ -501,31 +539,6 @@ private void handleTabVisualSettingsChangedEvent(TabVisualSettingsChangedEvent e }); } - @Handler - private void handleTableModelChangeEvent(TableModelChangeEvent evt) { - Platform.runLater(() -> { - var statusBar = statusBarController.getStatusBar(); - var rightItems = statusBar.getRightItems(); - if (evt.active) { - HBox hb = new HBox(); - var progressIndicator = new ProgressIndicator(); - progressIndicator.setTooltip(new Tooltip("Filmdaten werden verarbeitet")); - hb.getChildren().add(progressIndicator); - rightItems.add(hb); - indicatorLayout = new WeakReference<>(hb); - } - else { - //hide progress and label - rightItems.remove(indicatorLayout.get()); - } - }); - } - @Handler - private void handleBandwidthMonitorStateChangedEvent(BandwidthMonitorStateChangedEvent e) { - final var vis = config.getBoolean(ApplicationConfiguration.APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE, false); - SwingUtilities.invokeLater(() -> cbBandwidthDisplay.setSelected(vis)); - } - private void setWindowTitle() { setTitle(Konstanten.PROGRAMMNAME + ' ' + Konstanten.MVVERSION); } @@ -616,7 +629,7 @@ public void windowClosing(WindowEvent evt) { if (tray != null && config.getBoolean(ApplicationConfiguration.APPLICATION_UI_USE_TRAY, false)) { setVisible(false); } else { - beenden(false, false); + quitApplication(); } } }); @@ -677,7 +690,10 @@ private void initTabs() { Main.splashScreen.ifPresent(s -> s.update(UIProgressState.ADD_TABS_TO_UI)); tabbedPane.addTab(GuiFilme.NAME, tabFilme); tabbedPane.addTab(GuiDownloads.NAME, tabDownloads); - tabbedPane.setSelectedComponent(tabFilme); + + if (ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.APPLICATION_RESTORE_SELECTED_TAB, false)) + tabbedPane.restoreSavedTabPosition(); + tabbedPane.installChangeListener(); Main.splashScreen.ifPresent(s -> s.update(UIProgressState.CONFIGURE_TABS)); configureTabPlacement(); @@ -802,7 +818,8 @@ private void createFileMenu() { jMenuDatei.add(loadFilmListAction); jMenuDatei.addSeparator(); var exportMenu = new JMenu("Export"); - exportMenu.add(new FilmListExportAction(this)); + exportMenu.add(new ExportReadableFilmlistAction()); + exportMenu.add(new ExportDecompressedFilmlistAction()); var importMenu = new JMenu("Import"); importMenu.add(new ImportOldAbosAction()); @@ -815,43 +832,54 @@ private void createFileMenu() { //on macOS we will use native handlers instead... if (!SystemUtils.IS_OS_MAC_OSX) { jMenuDatei.addSeparator(); - jMenuDatei.add(new SettingsAction(this)); + jMenuDatei.add(settingsAction); jMenuDatei.addSeparator(); jMenuDatei.add(new QuitAction(this)); } } private void createViewMenu() { - cbBandwidthDisplay.setSelected(config.getBoolean(ApplicationConfiguration.APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE, false)); - cbBandwidthDisplay.addActionListener(e -> { - config.setProperty(ApplicationConfiguration.APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE, cbBandwidthDisplay.isSelected()); - getBandwidthMonitorController().setVisibility(); - }); - - JMenuItem showFilmFilterDialog = new JMenuItem("Filterdialog anzeigen"); - showFilmFilterDialog.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0)); - showFilmFilterDialog.addActionListener(l -> { - var dlg = tabFilme.filmActionPanel.filterDialog; - if (dlg != null) { - if (!dlg.isVisible()) { - dlg.setVisible(true); - } - } - - }); - - JMenuItem showBookmarkList = new JMenuItem("Merkliste anzeigen"); - showBookmarkList.addActionListener(l -> JavaFxUtils.invokeInFxThreadAndWait(() -> tabFilme.showBookmarkWindow())); - jMenuAnsicht.addSeparator(); jMenuAnsicht.add(showMemoryMonitorAction); - jMenuAnsicht.add(cbBandwidthDisplay); + jMenuAnsicht.add(showBandwidthUsageAction); jMenuAnsicht.addSeparator(); - jMenuAnsicht.add(showFilmFilterDialog); + jMenuAnsicht.add(tabFilme.toggleFilterDialogVisibilityAction); jMenuAnsicht.addSeparator(); - jMenuAnsicht.add(new ShowFilmInformationAction(true)); + jMenuAnsicht.add(showFilmInformationAction); jMenuAnsicht.addSeparator(); - jMenuAnsicht.add(showBookmarkList); + jMenuAnsicht.add(manageBookmarkAction); + } + + private void createFontMenu() { + var restoreFontMenuItem = new JMenuItem(); + restoreFontMenuItem.setText("Schrift zurücksetzen"); + restoreFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_0, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); + restoreFontMenuItem.addActionListener(e -> fontManager.resetFont()); + fontMenu.add(restoreFontMenuItem); + + var incrFontMenuItem = new JMenuItem(); + incrFontMenuItem.setText("Schrift vergrößern"); + incrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); + incrFontMenuItem.addActionListener(e -> fontManager.increaseFontSize()); + fontMenu.add(incrFontMenuItem); + + var decrFontMenuItem = new JMenuItem(); + decrFontMenuItem.setText("Schrift verkleinern"); + decrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); + decrFontMenuItem.addActionListener(e -> fontManager.decreaseFontSize()); + fontMenu.add(decrFontMenuItem); + + fontManager.updateFontMenuItems(); + } + + @Handler + private void handleFilmlistWriteStartEvent(FilmListWriteStartEvent e) { + SwingUtilities.invokeLater(() -> loadFilmListAction.setEnabled(false)); + } + + @Handler + private void handleFilmlistWriteStopEvent(FilmListWriteStopEvent e) { + SwingUtilities.invokeLater(() -> loadFilmListAction.setEnabled(true)); } private void createHelpMenu() { @@ -862,43 +890,18 @@ private void createHelpMenu() { jMenuHilfe.add(new ResetAboHistoryAction(this)); jMenuHilfe.add(new DeleteLocalFilmlistAction(this)); jMenuHilfe.addSeparator(); + jMenuHilfe.add(new ResetFilterDialogPosition(this)); + jMenuHilfe.addSeparator(); //do not show menu entry if we have external update support - var externalUpdateCheck = System.getProperty(Konstanten.EXTERNAL_UPDATE_PROPERTY); - if (externalUpdateCheck == null || !externalUpdateCheck.equalsIgnoreCase("true")) { + if (GuiFunktionen.isNotUsingExternalUpdater()) { jMenuHilfe.add(searchProgramUpdateAction); } jMenuHilfe.add(new ShowProgramInfosAction()); - /* - we use Shenandoah GC and a signed app on macOS. Therefore there is no need to modify vm settings. - */ - if (officialLauncherInUse() && !SystemUtils.IS_OS_MAC_OSX) { - jMenuHilfe.addSeparator(); - jMenuHilfe.add(new SetAppMemoryAction()); - } - installAdditionalHelpEntries(); - } - /** - * Test if MediathekView is launched by our official downloads. - * @return true if official binary is used, false otherwise. - */ - protected boolean officialLauncherInUse() { - boolean winBinaryInUse = true; - final var externalUpdateCheck = System.getProperty(Konstanten.EXTERNAL_UPDATE_PROPERTY); - if (externalUpdateCheck == null || !externalUpdateCheck.equalsIgnoreCase("true")) { - winBinaryInUse = false; - } - - return winBinaryInUse; - } - - protected void installChangeGlobalFontSettingMenuEntry() { - //unused on macOS and Windows + installAdditionalHelpEntries(); } protected void installAdditionalHelpEntries() { - installChangeGlobalFontSettingMenuEntry(); - jMenuHilfe.addSeparator(); jMenuHilfe.add(new ShowAboutAction()); } @@ -910,19 +913,30 @@ protected void initMenus() { tabFilme.installMenuEntries(jMenuFilme); tabDownloads.installMenuEntries(jMenuDownload); + createFontMenu(); createViewMenu(); tabFilme.installViewMenuEntry(jMenuAnsicht); createAboMenu(); + if (Config.isDebugModeEnabled()) + createDeveloperMenu(); createHelpMenu(); } + private void createDeveloperMenu() { + JMenu devMenu = new JMenu("Entwickler"); + + JMenuItem miGc = new JMenuItem("GC ausführen"); + miGc.addActionListener(l -> System.gc()); + + devMenu.add(miGc); + jMenuBar.add(devMenu); + } + private void createAboMenu() { jMenuAbos.add(new CreateNewAboAction(daten.getListeAbo())); jMenuAbos.add(new ShowAboHistoryAction(MediathekGui.ui())); jMenuAbos.addSeparator(); - manageAboAction = new ManageAboAction(); - tabFilme.filmActionPanel.manageAboAction = manageAboAction; jMenuAbos.add(manageAboAction); } @@ -946,97 +960,131 @@ public DialogEinstellungen getSettingsDialog() { return dialogEinstellungen; } - public boolean beenden(boolean showOptionTerminate, boolean shutDown) { + public boolean isShutdownRequested() { + return shutdownRequested; + } + + public void setShutdownRequested(boolean shutdownRequested) { + this.shutdownRequested = shutdownRequested; + } + + public boolean quitApplication() { if (daten.getListeDownloads().unfinishedDownloads() > 0) { // erst mal prüfen ob noch Downloads laufen DialogBeenden dialogBeenden = new DialogBeenden(this); - if (showOptionTerminate) { - dialogBeenden.setComboWaitAndTerminate(); - } - dialogBeenden.setVisible(true); - if (!dialogBeenden.applicationCanTerminate()) { + if (!dialogBeenden.getApplicationCanTerminate()) { return false; } - shutDown = dialogBeenden.isShutdownRequested(); + setShutdownRequested(dialogBeenden.isShutdownRequested()); } if (automaticFilmlistUpdate != null) automaticFilmlistUpdate.close(); + endProgramUpdateChecker(); + showMemoryMonitorAction.closeMemoryMonitor(); - if (bandwidthMonitor != null) - bandwidthMonitor.close(); + showBandwidthUsageAction.getDialogOptional().ifPresent(dlg -> { + dlg.dispose(); + //little hack, we must preserve the visible state since it was open when app quits... + config.setProperty(ApplicationConfiguration.APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE, true); + }); - endProgramUpdateChecker(); + manageAboAction.closeDialog(); + + ShutdownDialogController shutdownProgress = new ShutdownDialogController(this); + shutdownProgress.show(); - ShutdownDialog dialog = new ShutdownDialog(this); - dialog.show(); + var historyWorker = CompletableFuture.runAsync(() -> { + try (SeenHistoryController history = new SeenHistoryController()) { + history.performMaintenance(); + } + }); + + var bookmarkWorker = CompletableFuture.runAsync(() -> + daten.getListeBookmarkList().saveToFile(StandardLocations.getBookmarkFilePath())); // stop the download thread - dialog.setStatusText(ShutdownState.TERMINATE_STARTER_THREAD); + shutdownProgress.setStatus(ShutdownState.TERMINATE_STARTER_THREAD); daten.getStarterClass().getStarterThread().interrupt(); - dialog.setStatusText(ShutdownState.SHUTDOWN_NOTIFICATION_CENTER); + shutdownProgress.setStatus(ShutdownState.SHUTDOWN_NOTIFICATION_CENTER); closeNotificationCenter(); - manageAboAction.closeDialog(); - - tabFilme.saveSettings(); // needs thread pools active! - - dialog.setStatusText(ShutdownState.SHUTDOWN_THREAD_POOL); - shutdownTimerPool(); - waitForCommonPoolToComplete(); - - dialog.setStatusText(ShutdownState.PERFORM_SEEN_HISTORY_MAINTENANCE); - try (SeenHistoryController history = new SeenHistoryController()) { - history.performMaintenance(); - } - // Tabelleneinstellungen merken - dialog.setStatusText(ShutdownState.SAVE_FILM_DATA); + shutdownProgress.setStatus(ShutdownState.SAVE_FILM_DATA); tabFilme.tabelleSpeichern(); + tabFilme.saveSettings(); // needs thread pools active! + tabFilme.filmActionPanel.getFilterDialog().dispose(); - dialog.setStatusText(ShutdownState.SAVE_DOWNLOAD_DATA); + shutdownProgress.setStatus(ShutdownState.SAVE_DOWNLOAD_DATA); tabDownloads.tabelleSpeichern(); - dialog.setStatusText(ShutdownState.STOP_DOWNLOADS); + shutdownProgress.setStatus(ShutdownState.STOP_DOWNLOADS); stopDownloads(); - dialog.setStatusText(ShutdownState.SAVE_BOOKMARKS); - daten.getListeBookmarkList().saveToFile(Daten.getBookmarkFilePath()); - - dialog.setStatusText(ShutdownState.SAVE_APP_DATA); + shutdownProgress.setStatus(ShutdownState.SAVE_APP_DATA); daten.allesSpeichern(); - dialog.setStatusText(ShutdownState.COMPLETE); - dialog.hide(); + shutdownProgress.setStatus(ShutdownState.SHUTDOWN_THREAD_POOL); + shutdownTimerPool(); + waitForCommonPoolToComplete(); - tabFilme.filmActionPanel.filterDialog.dispose(); + //shutdown JavaFX + shutdownProgress.setStatus(ShutdownState.TERMINATE_JAVAFX_SUPPORT); + shutdownJavaFx(); - RuntimeStatistics.printRuntimeStatistics(); + try { + shutdownProgress.setStatus(ShutdownState.SAVE_BOOKMARKS); + bookmarkWorker.get(); - dispose(); + shutdownProgress.setStatus(ShutdownState.PERFORM_SEEN_HISTORY_MAINTENANCE); + historyWorker.get(); + } catch (InterruptedException | ExecutionException e) { + logger.error("Async task error", e); + } - JavaFxUtils.invokeInFxThreadAndWait(() -> JFXHiddenApplication.getPrimaryStage().close()); + //close main window + dispose(); //write all settings if not done already... ApplicationConfiguration.getInstance().writeConfiguration(); - //shutdown JavaFX - Platform.exit(); + RuntimeStatistics.printRuntimeStatistics(); + RuntimeStatistics.printDataUsageStatistics(); + shutdownProgress.setStatus(ShutdownState.COMPLETE); - if (shutDown) { + cleanupLuceneIndex(); + + if (isShutdownRequested()) { shutdownComputer(); } - var byteCounter = MVHttpClient.getInstance().getByteCounter(); - logger.trace("total data sent: {}", FileUtils.humanReadableByteCountBinary(byteCounter.totalBytesWritten())); - logger.trace("total data received: {}", FileUtils.humanReadableByteCountBinary(byteCounter.totalBytesRead())); System.exit(0); - return false; + return true; + } + + private void cleanupLuceneIndex() { + if (daten.getListeFilmeNachBlackList() instanceof IndexedFilmList filmList) { + try (var writer = filmList.getWriter()) { + writer.deleteAll(); + writer.commit(); + //filmList.getLuceneDirectory().close(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + /** + * Gracefully shutdown the JavaFX environment. + */ + private void shutdownJavaFx() { + JavaFxUtils.invokeInFxThreadAndWait(() -> JFXHiddenApplication.getPrimaryStage().close()); + Platform.exit(); } private void shutdownTimerPool() { @@ -1086,18 +1134,4 @@ protected void shutdownComputer() { //default is none } - /** - * Gracefully shutdown config and log. - * This may be necessary in case the app is not properly quit. - */ - static class ShutdownHookThread extends Thread { - @Override - public void run() { - //write all settings if not done already...just to be sure - ApplicationConfiguration.getInstance().writeConfiguration(); - - //shut down log4j - Log4jShutdownCallbackRegistry.Companion.execute(); - } - } } diff --git a/src/main/java/mediathek/mainwindow/MemoryUsagePanel.java b/src/main/java/mediathek/mainwindow/MemoryUsagePanel.java new file mode 100644 index 0000000000..e566390338 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/MemoryUsagePanel.java @@ -0,0 +1,130 @@ +package mediathek.mainwindow; + +import mediathek.gui.messages.DarkModeChangeEvent; +import mediathek.tool.MessageBus; +import net.engio.mbassy.listener.Handler; +import org.jetbrains.annotations.NotNull; +import org.jfree.chart.ChartMouseEvent; +import org.jfree.chart.ChartMouseListener; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.DateAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYSplineRenderer; +import org.jfree.chart.ui.RectangleInsets; +import org.jfree.data.time.Millisecond; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.text.DecimalFormat; +import java.util.concurrent.TimeUnit; + +public class MemoryUsagePanel extends JPanel { + + private final TimeSeries total = new TimeSeries("Total Memory"); + private final DateAxis domain = new DateAxis("Time"); + private final NumberAxis range = new NumberAxis("Memory"); + + public MemoryUsagePanel(int maxAge, @NotNull TimeUnit timeUnit) { + + super(new BorderLayout()); + + total.setMaximumItemAge(TimeUnit.MILLISECONDS.convert(maxAge, timeUnit)); + + TimeSeriesCollection dataset = new TimeSeriesCollection(); + dataset.addSeries(total); + + range.setAutoRange(true); + + setLabelColors(); + + var renderer = new XYSplineRenderer(); + renderer.setDefaultShapesVisible(false); + renderer.setSeriesPaint(0, Color.red); + + var plot = new XYPlot(dataset, domain, range, renderer); + plot.setBackgroundPaint(Color.BLACK); + plot.setDomainGridlinePaint(Color.white); + plot.setRangeGridlinePaint(Color.white); + plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0)); + + domain.setAutoRange(true); + domain.setLowerMargin(0.0); + domain.setUpperMargin(0.0); + domain.setTickLabelsVisible(true); + + range.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); + range.setNumberFormatOverride(new DecimalFormat("#######.##")); + + var chart = new JFreeChart(plot); + chart.removeLegend(); + var chartPanel = new ChartPanel(chart); + chartPanel.addChartMouseListener(new GarbageCollectionMouseListener()); + chartPanel.setPopupMenu(null); + add(chartPanel); + + MessageBus.getMessageBus().subscribe(this); + } + + @Handler + private void handleDarkModeChange(DarkModeChangeEvent e) { + SwingUtilities.invokeLater(this::setLabelColors); + } + + private void setLabelColors() { + var color = UIManager.getColor("Label.foreground"); + + domain.setLabelPaint(color); + domain.setTickLabelPaint(color); + domain.setTickMarkPaint(color); + + range.setLabelPaint(color); + range.setTickLabelPaint(color); + range.setTickMarkPaint(color); + } + + private void addTotalObservation(double y) { + total.add(new Millisecond(), y); + } + + private static class GarbageCollectionMouseListener implements ChartMouseListener { + @Override + public void chartMouseClicked(ChartMouseEvent event) { + System.gc(); + } + + @Override + public void chartMouseMoved(ChartMouseEvent event) { + + } + } + + public class MemoryUsageDataGenerator extends Timer implements ActionListener { + + /** + * Constructor. + * + * @param interval the interval + */ + public MemoryUsageDataGenerator(int interval, @NotNull TimeUnit timeUnit) { + super((int) TimeUnit.MILLISECONDS.convert(interval, timeUnit), null); + addActionListener(this); + } + + /** + * Adds a new total memory reading (converted to MByte) to the dataset. + * + * @param event the action event. + */ + public void actionPerformed(ActionEvent event) { + long t = Runtime.getRuntime().totalMemory() / (1024 * 1024); + addTotalObservation(t); + } + + } +} diff --git a/src/main/java/mediathek/mainwindow/PositionSavingTabbedPane.java b/src/main/java/mediathek/mainwindow/PositionSavingTabbedPane.java new file mode 100644 index 0000000000..75f433a9aa --- /dev/null +++ b/src/main/java/mediathek/mainwindow/PositionSavingTabbedPane.java @@ -0,0 +1,28 @@ +package mediathek.mainwindow; + +import mediathek.tool.ApplicationConfiguration; + +import javax.swing.*; + +public class PositionSavingTabbedPane extends JTabbedPane { + + public PositionSavingTabbedPane() { + } + + public void installChangeListener() { + addChangeListener(l -> saveTabPosition()); + } + + public void restoreSavedTabPosition() { + var idx = ApplicationConfiguration.getConfiguration().getInt(Config.TAB_POSITION, -1); + setSelectedIndex(Math.max(idx, 0)); + } + + private void saveTabPosition() { + ApplicationConfiguration.getConfiguration().setProperty(Config.TAB_POSITION, getSelectedIndex()); + } + + private static class Config { + public static final String TAB_POSITION = "app.ui.tab_position"; + } +} diff --git a/src/main/java/mediathek/mainwindow/SelectedListItemsLabel.java b/src/main/java/mediathek/mainwindow/SelectedListItemsLabel.java new file mode 100644 index 0000000000..67c5d2c736 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/SelectedListItemsLabel.java @@ -0,0 +1,21 @@ +package mediathek.mainwindow; + +import javax.swing.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +public class SelectedListItemsLabel extends JLabel implements PropertyChangeListener { + public SelectedListItemsLabel(MediathekGui mediathekGui) { + setText("0"); + setToolTipText("Ausgewählte Einträge der aktiven Tabelle"); + mediathekGui.selectedListItemsProperty.addSelectedItemsChangeListener(this); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + SwingUtilities.invokeLater(() -> { + long items = (long) evt.getNewValue(); + setText(Long.toString(items)); + }); + } +} diff --git a/src/main/java/mediathek/mainwindow/ShutdownDialogController.kt b/src/main/java/mediathek/mainwindow/ShutdownDialogController.kt new file mode 100644 index 0000000000..6b2c5bbb1e --- /dev/null +++ b/src/main/java/mediathek/mainwindow/ShutdownDialogController.kt @@ -0,0 +1,44 @@ +package mediathek.mainwindow + +import mediathek.gui.AppShutdownWindow +import mediathek.tool.ShutdownState +import java.util.* +import java.util.concurrent.TimeUnit + +/** + * Display a wait dialog with some status message to inform user what is happening currently. + */ +class ShutdownDialogController(private val gui: MediathekGui) { + private val window: AppShutdownWindow = AppShutdownWindow(gui) + private var curSteps = 0.0 + + init { + window.progress.maximum = EnumSet.allOf(ShutdownState::class.java).size + } + + fun show() { + gui.isEnabled = false + window.label1.isBusy = true + window.isVisible = true + } + + fun setStatus(state: ShutdownState) { + curSteps++ + window.message.text = state.toString() + window.message.paintImmediately(0, 0, window.message.width, window.message.height) + window.progress.value = curSteps.toInt() + window.progress.paintImmediately(0, 0, window.progress.width, window.progress.height) + window.label1.paintImmediately(0, 0, window.label1.width, window.label1.height) + /*try { + TimeUnit.MILLISECONDS.sleep(750) + } + catch (ignored: InterruptedException) {}*/ + if (state == ShutdownState.COMPLETE) { + try { + TimeUnit.MILLISECONDS.sleep(500) + window.dispose() + } catch (ignored: InterruptedException) { + } + } + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/tool/AppTerminationIndefiniteProgress.java b/src/main/java/mediathek/tool/AppTerminationIndefiniteProgress.java new file mode 100644 index 0000000000..e56d092447 --- /dev/null +++ b/src/main/java/mediathek/tool/AppTerminationIndefiniteProgress.java @@ -0,0 +1,38 @@ +package mediathek.tool; + +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; +import org.jdesktop.swingx.JXBusyLabel; + +import javax.swing.*; + +/** + * This will display a JPanel with a indefinite progress indicator and some status + * messages used as a glass pane overlay during app termination. + */ +public class AppTerminationIndefiniteProgress extends JPanel { + private final JLabel lblMessage = new JLabel("Warte auf Abschluss der Downloads..."); + + public AppTerminationIndefiniteProgress(boolean willbeShutDown) { + super(); + + setLayout(new MigLayout(new LC().hideMode(3), + new AC().fill().fill(),new AC())); + + var busyLabel = new JXBusyLabel(); + add(busyLabel, new CC().cell(0,0).span(1,3)); + busyLabel.setBusy(true); + + add(lblMessage, new CC().cell(1,0)); + if (willbeShutDown) { + add(new JLabel("Der Rechner wird danach heruntergefahren."), new CC().cell(1,1)); + } + add(new JLabel("Sie können den Vorgang mit Escape abbrechen."), new CC().cell(1,2)); + } + + public void setMessage(String text) { + SwingUtilities.invokeLater(() -> lblMessage.setText(text)); + } +} diff --git a/src/main/java/mediathek/tool/ApplicationConfiguration.java b/src/main/java/mediathek/tool/ApplicationConfiguration.java index e7e1ac6f79..790997266c 100644 --- a/src/main/java/mediathek/tool/ApplicationConfiguration.java +++ b/src/main/java/mediathek/tool/ApplicationConfiguration.java @@ -1,8 +1,9 @@ package mediathek.tool; +import com.fasterxml.jackson.databind.ObjectMapper; import mediathek.config.Konstanten; import mediathek.config.StandardLocations; -import mediathek.daten.GeoblockingField; +import mediathek.daten.Country; import org.apache.commons.configuration2.Configuration; import org.apache.commons.configuration2.XMLConfiguration; import org.apache.commons.configuration2.event.ConfigurationEvent; @@ -25,9 +26,12 @@ * The global application configuration class. This will contain all the config data in the future. */ public class ApplicationConfiguration { + public static final String APPLICATION_DARK_MODE = "application.dark_mode"; public static final String APPLICATION_USER_AGENT = "application.user_agent"; + public static final String APPLICATION_USE_MODERN_SEARCH = "application.use.modern_search"; public static final String APPLICATION_INSTALL_TAB_SWITCH_LISTENER = "application.ui.install_tab_listeners"; + public static final String APPLICATION_RESTORE_SELECTED_TAB = "application.ui.restore_selected_tab"; public static final String APPLICATION_UI_TAB_POSITION_TOP = "application.ui.tab_position.top"; public static final String APPLICATION_UI_MAINWINDOW_MAXIMIZED = "application.ui.mainwindow.maximized"; @@ -42,9 +46,8 @@ public class ApplicationConfiguration { public static final String APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE = "application.ui.bandwidth_monitor.visible"; public static final String APPLICATION_UI_USE_TRAY = "application.ui.tray.use"; - public static final String APPLICATION_UI_FONT_SIZE = "application.ui.font_size"; public static final String APPLICATION_UI_DOWNLOAD_TAB_DIVIDER_LOCATION = - "application.ui.download.tab.divider.location"; + "application.ui.download_tab.divider.location"; public static final String APPLICATION_UI_BOOKMARKLIST = "application.ui.bookmarklist"; public static final String APPLICATION_SHOW_NOTIFICATIONS = "application.notifications.show"; public static final String APPLICATION_SHOW_ORF_CONFIG_HELP = "application.orf.show_config_help"; @@ -56,10 +59,6 @@ public class ApplicationConfiguration { public static final String APPLICATION_NETWORKING_DNS_MODE = "application.networking.dns.ip_mode"; public static final String APPLICATION_BUTTONS_PANEL_VISIBLE = "application.buttons_panel.visible"; - public static final String GEO_REPORT = "geo.report"; - public static final String GEO_LOCATION = "geo.location"; - public static final String BLACKLIST_DO_NOT_SHOW_GEOBLOCKED_FILMS = "blacklist.show_geoblocked"; - public static final String DOWNLOAD_RATE_LIMIT = "download.rate.limit"; public static final String DOWNLOAD_SHOW_LAST_USED_PATH = "download.path.last_used.show"; public static final String DOWNLOAD_SOUND_BEEP = "download.sound.beep"; public static final String DOWNLOAD_SHOW_DESCRIPTION = "download.show_description"; @@ -68,12 +67,18 @@ public class ApplicationConfiguration { "searchfield.film.search_through_description"; public static final String FILM_SHOW_DESCRIPTION = "film.show_description"; public static final String CONFIG_AUTOMATIC_UPDATE_CHECK = "application.automatic_update_check"; - public static final String TAB_FILM_FONT_SIZE = "tab.film.font_size"; public static final String CLI_CLIENT_DOWNLOAD_LIST_FORMAT = "cli.client.download_list_format"; + private static final String GEO_LOCATION = "geo.location"; + private static final String BLACKLIST_DO_NOT_SHOW_GEOBLOCKED_FILMS = "blacklist.show_geoblocked"; /** * logger for {@link TimerTaskListener} inner class. */ private static final Logger logger = LogManager.getLogger(); + private static final ObjectMapper mapper = new ObjectMapper(); + /** + * A custom small thread scheduler exclusively for config changes. + */ + private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2); private XMLConfiguration config; private FileHandler handler; /** @@ -95,8 +100,7 @@ private ApplicationConfiguration() { config.setProperty("config.major", version.getMajor()); config.setProperty("config.minor", version.getMinor()); config.setProperty("config.patch", version.getPatch()); - } - finally { + } finally { config.unlock(LockMode.WRITE); } } @@ -109,6 +113,40 @@ public static Configuration getConfiguration() { return getInstance().config; } + public Country getGeographicLocation() { + try { + var str = config.getString(GEO_LOCATION); + // str has no quotation marks if it was set before the update but object mapper now expects them... + if (!str.startsWith("\"") && !str.endsWith("\"")) + str = "\"" + str + "\""; + return mapper.readValue(str, Country.class); + } + catch (Exception ex) { + logger.error("Unable to parse country, resetting to GERMANY", ex); + setGeographicLocation(Country.DE); + return Country.DE; + } + } + + public void setGeographicLocation(Country country) { + try { + var newValue = mapper.writeValueAsString(country); + config.setProperty(GEO_LOCATION, newValue); + } + catch (Exception ex) { + logger.error("Error setting location, setting to GERMANY", ex); + setGeographicLocation(Country.DE); + } + } + + public boolean getBlacklistDoNotShowGeoblockedFilms() { + return config.getBoolean(BLACKLIST_DO_NOT_SHOW_GEOBLOCKED_FILMS, false); + } + + public void setBlacklistDoNotShowGeoblockedFilms(boolean newValue) { + config.setProperty(BLACKLIST_DO_NOT_SHOW_GEOBLOCKED_FILMS, newValue); + } + private void initializeTimedEventWriting() { config.addEventListener(ConfigurationEvent.ANY, new TimerTaskListener()); } @@ -153,8 +191,7 @@ public void writeConfiguration() { private void createDefaultConfigSettings() { try { config.setProperty(APPLICATION_USER_AGENT, Konstanten.PROGRAMMNAME); - config.setProperty(GEO_REPORT, true); - config.setProperty(GEO_LOCATION, GeoblockingField.GEO_DE); + setGeographicLocation(Country.DE); handler.save(); } catch (ConfigurationException configurationException) { @@ -167,17 +204,20 @@ private void createDefaultConfigSettings() { } private void updateNewerDefaults() { - if (!config.containsKey(GEO_REPORT)) { - config.setProperty(GEO_REPORT, true); - } if (!config.containsKey(GEO_LOCATION)) { - config.setProperty(GEO_LOCATION, GeoblockingField.GEO_DE); + //object mapper expects quotation marks in the string! + config.setProperty(GEO_LOCATION, "\"DE\""); } if (!config.containsKey(APPLICATION_INSTALL_TAB_SWITCH_LISTENER)) { config.setProperty(APPLICATION_INSTALL_TAB_SWITCH_LISTENER, !SystemUtils.IS_OS_MAC_OSX); } } + public static class DownloadRateLimiter { + public static final String LIMIT = "download.rate.limit"; + public static final String ACTIVE = "download.rate.active"; + } + /** * Part of the Bill Pugh Singleton implementation */ @@ -217,9 +257,26 @@ public static class FilterDialog { } public static class FilmInfoDialog { - public static final String FILM_INFO_VISIBLE = "film.information.visible"; - public static final String FILM_INFO_LOCATION_X = "film.information.location.x"; - public static final String FILM_INFO_LOCATION_Y = "film.information.location.y"; + public static final String VISIBLE = "film.information.visible"; + public static final String X = "film.information.location.x"; + public static final String Y = "film.information.location.y"; + public static final String WIDTH = "film.information.location.width"; + public static final String HEIGHT = "film.information.location.height"; + } + + public static class MemoryMonitorDialog { + public static final String VISIBLE = "memory_monitor.visible"; + public static final String X = "memory_monitor.x"; + public static final String Y = "memory_monitor.y"; + public static final String WIDTH = "memory_monitor.width"; + public static final String HEIGHT = "memory_monitor.height"; + } + + public static class EditDownloadDialog { + public static final String X = "edit_download_dialog.x"; + public static final String Y = "edit_download_dialog.y"; + public static final String WIDTH = "edit_download_dialog.width"; + public static final String HEIGHT = "edit_download_dialog.height"; } public static class SettingsDialog { @@ -229,11 +286,6 @@ public static class SettingsDialog { public static final String Y = "application.ui.settings_dialog.y"; } - /** - * A custom small thread scheduler exclusively for config changes. - */ - private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2); - /** * This class will issue a timer to write config to file 5 seconds after onEvent call. In case * this listener is called several times in a row the timer will get reset in order to ensure that diff --git a/src/main/java/mediathek/tool/CompoundIcon.java b/src/main/java/mediathek/tool/CompoundIcon.java new file mode 100644 index 0000000000..fef707c86b --- /dev/null +++ b/src/main/java/mediathek/tool/CompoundIcon.java @@ -0,0 +1,289 @@ +package mediathek.tool; + +import javax.swing.*; +import java.awt.*; + +/** + * The CompoundIcon will paint two, or more, Icons as a single Icon. The + * Icons are painted in the order in which they are added. + * + * The Icons are layed out on the specified axis: + *
    + *
  • X-Axis (horizontally) + *
  • Y-Axis (vertically) + *
  • Z-Axis (stacked) + *
+ * + */ +public class CompoundIcon implements Icon +{ + public enum Axis + { + X_AXIS, + Y_AXIS, + Z_AXIS + } + + public final static float TOP = 0.0f; + public final static float LEFT = 0.0f; + public final static float CENTER = 0.5f; + public final static float BOTTOM = 1.0f; + public final static float RIGHT = 1.0f; + + private final Icon[] icons; + + private final Axis axis; + + private final int gap; + + private float alignmentX = CENTER; + private float alignmentY = CENTER; + + /** + * Convenience contructor for creating a CompoundIcon where the + * icons are layed out on on the X-AXIS, the gap is 0 and the + * X/Y alignments will default to CENTER. + * + * @param icons the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Icon... icons) + { + this(Axis.X_AXIS, icons); + } + + /** + * Convenience contructor for creating a CompoundIcon where the + * gap is 0 and the X/Y alignments will default to CENTER. + * + * @param axis the axis used to lay out the icons for painting. + * Must be one of the Axis enums: X_AXIS, Y_AXIS, Z_Axis. + * @param icons the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Axis axis, Icon... icons) + { + this(axis, 0, icons); + } + + /** + * Convenience contructor for creating a CompoundIcon where the + * X/Y alignments will default to CENTER. + * + * @param axis the axis used to lay out the icons for painting + * Must be one of the Axis enums: X_AXIS, Y_AXIS, Z_Axis. + * @param gap the gap between the icons + * @param icons the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Axis axis, int gap, Icon... icons) + { + this(axis, gap, CENTER, CENTER, icons); + } + + /** + * Create a CompoundIcon specifying all the properties. + * + * @param axis the axis used to lay out the icons for painting + * Must be one of the Axis enums: X_AXIS, Y_AXIS, Z_Axis. + * @param gap the gap between the icons + * @param alignmentX the X alignment of the icons. Common values are + * LEFT, CENTER, RIGHT. Can be any value between 0.0 and 1.0 + * @param alignmentY the Y alignment of the icons. Common values are + * TOP, CENTER, BOTTOM. Can be any value between 0.0 and 1.0 + * @param icons the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Axis axis, int gap, float alignmentX, float alignmentY, Icon... icons) + { + this.axis = axis; + this.gap = gap; + this.alignmentX = alignmentX > 1.0f ? 1.0f : Math.max(alignmentX, 0.0f); + this.alignmentY = alignmentY > 1.0f ? 1.0f : Math.max(alignmentY, 0.0f); + + for (int i = 0; i < icons.length; i++) + { + if (icons[i] == null) + { + String message = "Icon (" + i + ") cannot be null"; + throw new IllegalArgumentException( message ); + } + } + + this.icons = icons; + } + + /** + * Get the Axis along which each icon is painted. + * + * @return the Axis + */ + public Axis getAxis() + { + return axis; + } + + /** + * Get the gap between each icon + * + * @return the gap in pixels + */ + public int getGap() + { + return gap; + } + + /** + * Get the alignment of the icon on the x-axis + * + * @return the alignment + */ + public float getAlignmentX() + { + return alignmentX; + } + + /** + * Get the alignment of the icon on the y-axis + * + * @return the alignment + */ + public float getAlignmentY() + { + return alignmentY; + } + + /** + * Get the number of Icons contained in this CompoundIcon. + * + * @return the total number of Icons + */ + public int getIconCount() + { + return icons.length; + } + + /** + * Get the Icon at the specified index. + * + * @param index the index of the Icon to be returned + * @return the Icon at the specifed index + * @exception IndexOutOfBoundsException if the index is out of range + */ + public Icon getIcon(int index) + { + return icons[ index ]; + } +// +// Implement the Icon Interface +// + /** + * Gets the width of this icon. + * + * @return the width of the icon in pixels. + */ + @Override + public int getIconWidth() + { + int width = 0; + + // Add the width of all Icons while also including the gap + + if (axis == Axis.X_AXIS) + { + width += (icons.length - 1) * gap; + + for (Icon icon : icons) + width += icon.getIconWidth(); + } + else // Just find the maximum width + { + for (Icon icon : icons) + width = Math.max(width, icon.getIconWidth()); + } + + return width; + } + + /** + * Gets the height of this icon. + * + * @return the height of the icon in pixels. + */ + @Override + public int getIconHeight() + { + int height = 0; + + // Add the height of all Icons while also including the gap + + if (axis == Axis.Y_AXIS) + { + height += (icons.length - 1) * gap; + + for (Icon icon : icons) + height += icon.getIconHeight(); + } + else // Just find the maximum height + { + for (Icon icon : icons) + height = Math.max(height, icon.getIconHeight()); + } + + return height; + } + + /** + * Paint the icons of this compound icon at the specified location + * + * @param c The component on which the icon is painted + * @param g the graphics context + * @param x the X coordinate of the icon's top-left corner + * @param y the Y coordinate of the icon's top-left corner + */ + @Override + public void paintIcon(Component c, Graphics g, int x, int y) + { + if (axis == Axis.X_AXIS) + { + int height = getIconHeight(); + + for (Icon icon : icons) + { + int iconY = getOffset(height, icon.getIconHeight(), alignmentY); + icon.paintIcon(c, g, x, y + iconY); + x += icon.getIconWidth() + gap; + } + } + else if (axis == Axis.Y_AXIS) + { + int width = getIconWidth(); + + for (Icon icon : icons) + { + int iconX = getOffset(width, icon.getIconWidth(), alignmentX); + icon.paintIcon(c, g, x + iconX, y); + y += icon.getIconHeight() + gap; + } + } + else // must be Z_AXIS + { + int width = getIconWidth(); + int height = getIconHeight(); + + for (Icon icon : icons) + { + int iconX = getOffset(width, icon.getIconWidth(), alignmentX); + int iconY = getOffset(height, icon.getIconHeight(), alignmentY); + icon.paintIcon(c, g, x + iconX, y + iconY); + } + } + } + + /* + * When the icon value is smaller than the maximum value of all icons the + * icon needs to be aligned appropriately. Calculate the offset to be used + * when painting the icon to achieve the proper alignment. + */ + private int getOffset(int maxValue, int iconValue, float alignment) + { + float offset = (maxValue - iconValue) * alignment; + return Math.round(offset); + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/tool/DarkModeFactory.kt b/src/main/java/mediathek/tool/DarkModeFactory.kt new file mode 100644 index 0000000000..91bb2a4c0f --- /dev/null +++ b/src/main/java/mediathek/tool/DarkModeFactory.kt @@ -0,0 +1,14 @@ +package mediathek.tool + +import com.formdev.flatlaf.FlatDarkLaf +import com.formdev.flatlaf.themes.FlatMacDarkLaf +import org.apache.commons.lang3.SystemUtils +import javax.swing.LookAndFeel + +object DarkModeFactory { + @JvmStatic + val lookAndFeel: LookAndFeel + get() { + return if (SystemUtils.IS_OS_MAC_OSX) FlatMacDarkLaf() else FlatDarkLaf() + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/tool/EscapeKeyHandler.java b/src/main/java/mediathek/tool/EscapeKeyHandler.java index 40aba5a4f5..4e07582f87 100644 --- a/src/main/java/mediathek/tool/EscapeKeyHandler.java +++ b/src/main/java/mediathek/tool/EscapeKeyHandler.java @@ -7,7 +7,6 @@ import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; -@SuppressWarnings("serial") public class EscapeKeyHandler { private static final String CANCEL_KEY_HANDLER = "key_cancel"; diff --git a/src/main/java/mediathek/tool/FilmSize.java b/src/main/java/mediathek/tool/FilmSize.java index 8df0a01b71..47364b8dc8 100644 --- a/src/main/java/mediathek/tool/FilmSize.java +++ b/src/main/java/mediathek/tool/FilmSize.java @@ -1,39 +1,42 @@ package mediathek.tool; -import mediathek.daten.DatenFilm; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; /** - * Store film size in bytes. + * Store film size in Megabytes. */ public class FilmSize implements Comparable { private int size; private static final Logger logger = LogManager.getLogger(); - public FilmSize(final int size) { - this.size = size; + public FilmSize() { + this.size = 0; } - public FilmSize(DatenFilm film) { - if (film.getSize().equalsIgnoreCase("<1")) { - film.setSize("1"); + public void setSize(String strSize) { + if (strSize.equalsIgnoreCase("<1")) { + size = 1; } try { - if (!film.getSize().isEmpty()) { - size = Integer.parseInt(film.getSize()); + if (!strSize.isEmpty()) { + size = Integer.parseInt(strSize); } } catch (NumberFormatException ex) { - logger.error("String: {}", film.getSize(),ex); + logger.error("String: {}", strSize,ex); size = 0; } } + public Integer toInteger() { + return (size == 0) ? 0 : size; + } + @Override public String toString() { - return (size == 0) ? "" : Long.toString(size); + return (size == 0) ? "" : Integer.toString(size); } @Override diff --git a/src/main/java/mediathek/tool/Filter.java b/src/main/java/mediathek/tool/Filter.java index 73287757f6..7ad73a4406 100644 --- a/src/main/java/mediathek/tool/Filter.java +++ b/src/main/java/mediathek/tool/Filter.java @@ -3,7 +3,6 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import mediathek.config.MVColor; import mediathek.daten.DatenFilm; import mediathek.daten.abo.DatenAbo; import org.apache.commons.lang3.StringUtils; @@ -11,14 +10,16 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; -import javax.swing.*; -import java.awt.*; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; public class Filter { + /** + * Stores the regexp strings that were rejected as invalid. + */ + public static final Set regExpErrorList = new HashSet<>(); /** * The cache for already compiled RegExp. * Entries will be removed if the haven´t been accessed for more than 5 minutes. @@ -27,10 +28,6 @@ public class Filter { .expireAfterAccess(5, TimeUnit.MINUTES) .build(new PatternCacheLoader()); private static final Logger logger = LogManager.getLogger(Filter.class); - /** - * Stores the regexp strings that were rejected as invalid. - */ - public static final Set regExpErrorList = new HashSet<>(); public static boolean aboExistiertBereits(DatenAbo aboExistiert, DatenAbo aboPruefen) { // prüfen ob "aboExistiert" das "aboPrüfen" mit abdeckt, also die gleichen (oder mehr) @@ -248,6 +245,7 @@ public static Pattern makePattern(final String regExpStr) { /** * Create pattern without using the cache. * Used for interactive search field where cache pollution is not wanted. + * * @param regExpStr the regexp pattern * @return Pattern if successful, otherwise null. */ @@ -276,26 +274,6 @@ public static boolean regExpErrorsOccured() { return !regExpErrorList.isEmpty(); } - /** - * Check if entry in JTextField is a regexp pattern and its validity. - * If a recognized pattern is invalid, change the background color of the JTextField. - * - * @param tf The control that will be validated - */ - public static void validatePatternInput(JTextField tf) { - String text = tf.getText(); - if (Filter.isPattern(text)) { - if (Filter.makePattern(text) == null) { - //soll Pattern sein, ist aber falsch - tf.setBackground(MVColor.FILTER_REGEX_FEHLER.color); - } else { - tf.setBackground(MVColor.FILTER_REGEX.color); - } - } else { - tf.setBackground(Color.WHITE); - } - } - /** * This loader will compile regexp patterns when they are not in cache. */ diff --git a/src/main/java/mediathek/tool/GuiFunktionen.java b/src/main/java/mediathek/tool/GuiFunktionen.java index d974caf661..b7d6814d98 100644 --- a/src/main/java/mediathek/tool/GuiFunktionen.java +++ b/src/main/java/mediathek/tool/GuiFunktionen.java @@ -6,12 +6,15 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.intellij.lang.annotations.MagicConstant; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.awt.datatransfer.StringSelection; import java.awt.event.InputEvent; import java.io.File; +import java.util.concurrent.TimeUnit; public class GuiFunktionen { @@ -25,6 +28,103 @@ public class GuiFunktionen { private static final int UPDATE_FILME_AUTO = 2; private static final Logger logger = LogManager.getLogger(); + /** + * Property string to indicate usage of install4j's external updater. + */ + private static final String EXTERNAL_UPDATE_PROPERTY = "externalUpdateCheck"; + + /** + * Check whether or not we are using Install4j's external update mechanism. + * + * @return true if it is NOT used, true otherwise. + */ + public static boolean isNotUsingExternalUpdater() { + var externalUpdateCheck = System.getProperty(EXTERNAL_UPDATE_PROPERTY); + boolean ret = false; + if (externalUpdateCheck != null) { + if (externalUpdateCheck.equalsIgnoreCase("true") || externalUpdateCheck.isEmpty()) + ret = true; + } + + return !ret; + } + + /** + * Create an auto-dismissable JOptionPane-like dialog with return value. + * Uses internally {@link JOptionPane} to create and handle the values. + * @param parentComponent the parent object. + * @param title the dialog title. + * @param message the message. + * @param optionType Currently I support only DEFAULT ok-dialog and YES_NO_OPTION confirmDialog. + * @param defaultValue the default value to return. + * @param defaultDelay the number of time units to wait until close. + * @param timeUnit The time unit of defaultDelay. + * @param style the warning, error or information style from {@link JOptionPane}. + * @return the clicked value or the defaultValue. + */ + public static int createDismissableMessageDialog(@Nullable Component parentComponent, + @NotNull String title, + @NotNull String message, + @MagicConstant(intValues = {JOptionPane.YES_NO_OPTION, JOptionPane.DEFAULT_OPTION, JOptionPane.YES_NO_CANCEL_OPTION}) int optionType, + @MagicConstant(intValues = {JOptionPane.OK_OPTION, JOptionPane.CANCEL_OPTION, JOptionPane.YES_OPTION, JOptionPane.NO_OPTION}) int defaultValue, + int defaultDelay, @NotNull TimeUnit timeUnit, + @MagicConstant(intValues = {JOptionPane.WARNING_MESSAGE, JOptionPane.QUESTION_MESSAGE, JOptionPane.INFORMATION_MESSAGE, JOptionPane.ERROR_MESSAGE}) int style) { + var op = new JOptionPane(message, style, optionType, null, null); + var dialog = op.createDialog(parentComponent, title); + new Timer((int)TimeUnit.MILLISECONDS.convert(defaultDelay,timeUnit), e -> op.setValue(defaultValue)).start(); + dialog.setVisible(true); + return (int)op.getValue(); + } + + /** + * Show a "red box" around a component to indicate error condition + * @param component the target + * @param hasError if true, set error box around component, otherwise remove it. + */ + public static void showErrorIndication(@NotNull JComponent component, boolean hasError) { + if (hasError) + component.putClientProperty("JComponent.outline", "error"); + else + component.putClientProperty("JComponent.outline", ""); + } + + public static boolean isUsingExternalUpdater() { + return !isNotUsingExternalUpdater(); + } + /** + * Determine the image's size while keeping aspect ratio within a given boundary box. + * + * @param imgSize The size of the original image. + * @param boundary The bounds where the image needs to fit into. + * @return The calculated image dimensions for fitting into boundary. + */ + public static Dimension calculateFittedDimension(Dimension imgSize, Dimension boundary) { + + int original_width = imgSize.width; + int original_height = imgSize.height; + int bound_width = boundary.width; + int bound_height = boundary.height; + int new_width = original_width; + int new_height = original_height; + + // first check if we need to scale width + if (original_width > bound_width) { + //scale width to fit + new_width = bound_width; + //scale height to maintain aspect ratio + new_height = (new_width * original_height) / original_width; + } + + // then check if we need to scale even with the new height + if (new_height > bound_height) { + //scale height to fit instead + new_height = bound_height; + //scale width to maintain aspect ratio + new_width = (new_height * original_width) / original_height; + } + + return new Dimension(new_width, new_height); + } public static void copyToClipboard(String s) { Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(s), null); @@ -201,6 +301,7 @@ public static int getPlatformControlKey() { /** * Get the the user set filmlist update type. + * * @return MANUAL or AUTOMATIC based on config. Default is AUTOMATIC. */ public static FilmListUpdateType getFilmListUpdateType() { @@ -225,6 +326,7 @@ public static FilmListUpdateType getFilmListUpdateType() { /** * Store filmlist update mode in config. + * * @param type MANUAL or AUTOMATIC mode. */ public static void setFilmListUpdateType(FilmListUpdateType type) { diff --git a/src/main/java/mediathek/tool/InfiniteProgressPanel.java b/src/main/java/mediathek/tool/InfiniteProgressPanel.java new file mode 100644 index 0000000000..1bf643df61 --- /dev/null +++ b/src/main/java/mediathek/tool/InfiniteProgressPanel.java @@ -0,0 +1,414 @@ +package mediathek.tool; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.*; + +/** + * An infinite progress panel displays a rotating figure and + * a message to notice the user of a long, duration unknown + * task. The shape and the text are drawn upon a white veil + * which alpha level (or shield value) lets the underlying + * component shine through. This panel is meant to be used + * asa glass pane in the window performing the long + * operation. + *

+ * On the contrary to regular glass panes, you don't need to + * set it visible or not by yourself. Once you've started the + * animation all the mouse events are intercepted by this + * panel, preventing them from being forwared to the + * underlying components. + *

+ * The panel can be controlled by the start(), + * stop() and interrupt() methods. + *

+ * Example: + *

+ *
InfiniteProgressPanel pane = new InfiniteProgressPanel();
+ * frame.setGlassPane(pane);
+ * pane.start()
+ *

+ * Several properties can be configured at creation time. The + * message and its font can be changed at runtime. Changing the + * font can be done using setFont() and + * setForeground(). + * + * @author Romain Guy + * @version 1.0 + */ + +public class InfiniteProgressPanel extends JComponent implements MouseListener +{ + /** Contains the bars composing the circular shape. */ + protected Area[] ticker = null; + /** The animation thread is responsible for fade in/out and rotation. */ + protected Thread animation = null; + /** Notifies whether the animation is running or not. */ + protected boolean started = false; + /** Alpha level of the veil, used for fade in/out. */ + protected int alphaLevel = 0; + /** Duration of the veil's fade in/out. */ + protected int rampDelay = 300; + /** Alpha level of the veil. */ + protected float shield = 0.70f; + /** Message displayed below the circular shape. */ + protected String text = ""; + /** Amount of bars composing the circular shape. */ + protected int barsCount = 14; + /** Amount of frames per seconde. Lowers this to save CPU. */ + protected float fps = 15.0f; + /** Rendering hints to set anti aliasing. */ + protected RenderingHints hints = null; + + /** + * Creates a new progress panel with default values:
+ *
    + *
  • No message
  • + *
  • 14 bars
  • + *
  • Veil's alpha level is 70%
  • + *
  • 15 frames per second
  • + *
  • Fade in/out last 300 ms
  • + *
+ */ + public InfiniteProgressPanel() + { + this(""); + } + + /** + * Creates a new progress panel with default values:
+ *
    + *
  • 14 bars
  • + *
  • Veil's alpha level is 70%
  • + *
  • 15 frames per second
  • + *
  • Fade in/out last 300 ms
  • + *
+ * @param text The message to be displayed. Can be null or empty. + */ + public InfiniteProgressPanel(String text) + { + this(text, 14); + } + + /** + * Creates a new progress panel with default values:
+ *
    + *
  • Veil's alpha level is 70%
  • + *
  • 15 frames per second
  • + *
  • Fade in/out last 300 ms
  • + *
+ * @param text The message to be displayed. Can be null or empty. + * @param barsCount The amount of bars composing the circular shape + */ + public InfiniteProgressPanel(String text, int barsCount) + { + this(text, barsCount, 0.70f); + } + + /** + * Creates a new progress panel with default values:
+ *
    + *
  • 15 frames per second
  • + *
  • Fade in/out last 300 ms
  • + *
+ * @param text The message to be displayed. Can be null or empty. + * @param barsCount The amount of bars composing the circular shape. + * @param shield The alpha level between 0.0 and 1.0 of the colored + * shield (or veil). + */ + public InfiniteProgressPanel(String text, int barsCount, float shield) + { + this(text, barsCount, shield, 15.0f); + } + + /** + * Creates a new progress panel with default values:
+ *
    + *
  • Fade in/out last 300 ms
  • + *
+ * @param text The message to be displayed. Can be null or empty. + * @param barsCount The amount of bars composing the circular shape. + * @param shield The alpha level between 0.0 and 1.0 of the colored + * shield (or veil). + * @param fps The number of frames per second. Lower this value to + * decrease CPU usage. + */ + public InfiniteProgressPanel(String text, int barsCount, float shield, float fps) + { + this(text, barsCount, shield, fps, 300); + } + + /** + * Creates a new progress panel. + * @param text The message to be displayed. Can be null or empty. + * @param barsCount The amount of bars composing the circular shape. + * @param shield The alpha level between 0.0 and 1.0 of the colored + * shield (or veil). + * @param fps The number of frames per second. Lower this value to + * decrease CPU usage. + * @param rampDelay The duration, in milli seconds, of the fade in and + * the fade out of the veil. + */ + public InfiniteProgressPanel(String text, int barsCount, float shield, float fps, int rampDelay) + { + this.text = text; + this.rampDelay = Math.max(rampDelay, 0); + this.shield = Math.max(shield, 0.0f); + this.fps = fps > 0.0f ? fps : 15.0f; + this.barsCount = barsCount > 0 ? barsCount : 14; + + this.hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + this.hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + this.hints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } + + /** + * Changes the displayed message at runtime. + * + * @param text The message to be displayed. Can be null or empty. + */ + public void setText(String text) + { + this.text = text; + repaint(); + } + + /** + * Returns the current displayed message. + */ + public String getText() + { + return text; + } + + /** + * Starts the waiting animation by fading the veil in, then + * rotating the shapes. This method handles the visibility + * of the glass pane. + */ + public void start() + { + addMouseListener(this); + setVisible(true); + ticker = buildTicker(); + animation = new Thread(new Animator(true)); + animation.start(); + } + + /** + * Stops the waiting animation by stopping the rotation + * of the circular shape and then by fading out the veil. + * This methods sets the panel invisible at the end. + */ + public void stop() + { + //removeMouseListener(this); + + if (animation != null) { + animation.interrupt(); + animation = null; + animation = new Thread(new Animator(false)); + animation.start(); + } + } + + /** + * Interrupts the animation, whatever its state is. You + * can use it when you need to stop the animation without + * running the fade out phase. + * This method sets the panel invisible at the end. + */ + public void interrupt() + { + if (animation != null) { + animation.interrupt(); + animation = null; + + tidy(); + } + } + + private void tidy() { + removeMouseListener(this); + setVisible(false); + } + public void paintComponent(Graphics g) + { + if (started) + { + int width = getWidth(); + + double maxY = 0.0; + + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHints(hints); + + g2.setColor(new Color(255, 255, 255, (int) (alphaLevel * shield))); + g2.fillRect(0, 0, getWidth(), getHeight()); + + for (int i = 0; i < ticker.length; i++) + { + int channel = 224 - 128 / (i + 1); + g2.setColor(new Color(channel, channel, channel, alphaLevel)); + g2.fill(ticker[i]); + + Rectangle2D bounds = ticker[i].getBounds2D(); + if (bounds.getMaxY() > maxY) + maxY = bounds.getMaxY(); + } + + if (text != null && !text.isEmpty()) + { + FontRenderContext context = g2.getFontRenderContext(); + TextLayout layout = new TextLayout(text, getFont(), context); + Rectangle2D bounds = layout.getBounds(); + g2.setColor(getForeground()); + layout.draw(g2, (float) (width - bounds.getWidth()) / 2, + (float) (maxY + layout.getLeading() + 2 * layout.getAscent())); + } + } + } + + /** + * Builds the circular shape and returns the result as an array of + * Area. Each Area is one of the bars + * composing the shape. + */ + private Area[] buildTicker() + { + Area[] ticker = new Area[barsCount]; + Point2D.Double center = new Point2D.Double((double) getWidth() / 2, (double) getHeight() / 2); + double fixedAngle = 2.0 * Math.PI / ((double) barsCount); + + for (double i = 0.0; i < (double) barsCount; i++) + { + Area primitive = buildPrimitive(); + + AffineTransform toCenter = AffineTransform.getTranslateInstance(center.getX(), center.getY()); + AffineTransform toBorder = AffineTransform.getTranslateInstance(45.0, -6.0); + AffineTransform toCircle = AffineTransform.getRotateInstance(-i * fixedAngle, center.getX(), center.getY()); + + AffineTransform toWheel = new AffineTransform(); + toWheel.concatenate(toCenter); + toWheel.concatenate(toBorder); + + primitive.transform(toWheel); + primitive.transform(toCircle); + + ticker[(int) i] = primitive; + } + + return ticker; + } + + /** + * Builds a bar. + */ + private Area buildPrimitive() + { + Rectangle2D.Double body = new Rectangle2D.Double(6, 0, 30, 12); + Ellipse2D.Double head = new Ellipse2D.Double(0, 0, 12, 12); + Ellipse2D.Double tail = new Ellipse2D.Double(30, 0, 12, 12); + + Area tick = new Area(body); + tick.add(new Area(head)); + tick.add(new Area(tail)); + + return tick; + } + + /** + * Animation thread. + */ + private class Animator implements Runnable + { + private final boolean rampUp; + + protected Animator(boolean rampUp) + { + this.rampUp = rampUp; + } + + public void run() + { + Point2D.Double center = new Point2D.Double((double) getWidth() / 2, (double) getHeight() / 2); + double fixedIncrement = 2.0 * Math.PI / ((double) barsCount); + AffineTransform toCircle = AffineTransform.getRotateInstance(fixedIncrement, center.getX(), center.getY()); + + long start = System.currentTimeMillis(); + if (rampDelay == 0) + alphaLevel = rampUp ? 255 : 0; + + started = true; + boolean inRamp = rampUp; + + while (!Thread.interrupted()) + { + if (!inRamp) + { + for (Area area : ticker) + area.transform(toCircle); + } + + repaint(); + + if (rampUp) + { + if (alphaLevel < 255) + { + alphaLevel = (int) (255 * (System.currentTimeMillis() - start) / rampDelay); + if (alphaLevel >= 255) + { + alphaLevel = 255; + inRamp = false; + } + } + } else if (alphaLevel > 0) { + alphaLevel = (int) (255 - (255 * (System.currentTimeMillis() - start) / rampDelay)); + if (alphaLevel <= 0) + { + alphaLevel = 0; + break; + } + } + + try + { + Thread.sleep(inRamp ? 10 : (int) (1000 / fps)); + } catch (InterruptedException ie) { + break; + } + Thread.yield(); + } + + if (!rampUp) + { + started = false; + // there has been an EDT violation warning... + SwingUtilities.invokeLater(() -> { + repaint(); + tidy(); + }); + } + } + } + + public void mouseClicked(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + } + + public void mouseReleased(MouseEvent e) { + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/tool/Ipify.kt b/src/main/java/mediathek/tool/Ipify.kt index fdb272ed7d..22114a1507 100644 --- a/src/main/java/mediathek/tool/Ipify.kt +++ b/src/main/java/mediathek/tool/Ipify.kt @@ -3,7 +3,12 @@ package mediathek.tool import java.io.BufferedReader import java.io.IOException import java.io.InputStreamReader +import java.net.URI import java.net.URL +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse + object Ipify { /** @@ -27,4 +32,21 @@ object Ipify { } return ip } + + @JvmStatic + @get:Throws(IOException::class) + val publicIpNew: String + get() { + val client: HttpClient = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .followRedirects(HttpClient.Redirect.ALWAYS) + .build() + + val request = HttpRequest.newBuilder() + .uri(URI.create("https://api64.ipify.org")) + .build() + + val response = client.send(request, HttpResponse.BodyHandlers.ofString()) + return response.body() + } } \ No newline at end of file diff --git a/src/main/java/mediathek/tool/LightModeFactory.kt b/src/main/java/mediathek/tool/LightModeFactory.kt new file mode 100644 index 0000000000..df10176910 --- /dev/null +++ b/src/main/java/mediathek/tool/LightModeFactory.kt @@ -0,0 +1,14 @@ +package mediathek.tool + +import com.formdev.flatlaf.FlatLightLaf +import com.formdev.flatlaf.themes.FlatMacLightLaf +import org.apache.commons.lang3.SystemUtils +import javax.swing.LookAndFeel + +object LightModeFactory { + @JvmStatic + val lookAndFeel: LookAndFeel + get() { + return if (SystemUtils.IS_OS_MAC_OSX) FlatMacLightLaf() else FlatLightLaf() + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/tool/MVInfoFile.kt b/src/main/java/mediathek/tool/MVInfoFile.kt index b944bf098d..f9c42193b3 100644 --- a/src/main/java/mediathek/tool/MVInfoFile.kt +++ b/src/main/java/mediathek/tool/MVInfoFile.kt @@ -27,7 +27,7 @@ open class MVInfoFile { sb = appendFormattedTableLine(sb, formatString, FILM_TITEL, film.title).append(System.lineSeparator()) sb = appendFormattedTableLine(sb, formatString, FILM_DATUM, film.sendeDatum) sb = appendFormattedTableLine(sb, formatString, FILM_ZEIT, film.sendeZeit) - sb = appendFormattedTableLine(sb, formatString, FILM_DAUER, film.dauer) + sb = appendFormattedTableLine(sb, formatString, FILM_DAUER, film.filmLengthAsString) if (fileSize > FileSize.INVALID_SIZE) sb = appendFormattedTableLine(sb, formatString, FILM_GROESSE, FileUtils.humanReadableByteCountBinary(fileSize)) else @@ -36,7 +36,7 @@ open class MVInfoFile { sb.append(System.lineSeparator()) sb.append("Website") sb.append(System.lineSeparator()) - sb.append(film.websiteLink) + sb.append(film.websiteUrl) sb.append(System.lineSeparator()) sb.append(System.lineSeparator()) sb.append(FILM_URL) diff --git a/src/main/java/mediathek/tool/MVSubtitle.java b/src/main/java/mediathek/tool/MVSubtitle.java index 69b520786d..b4095f5f82 100644 --- a/src/main/java/mediathek/tool/MVSubtitle.java +++ b/src/main/java/mediathek/tool/MVSubtitle.java @@ -5,13 +5,14 @@ import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; +import okio.BufferedSource; +import okio.Okio; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; +import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -30,31 +31,69 @@ private void createDirectory(String targetDirectory) { } } - private void writeNetworkData(InputStream is, Path ttmlPath) throws IOException { - try (OutputStream fos = Files.newOutputStream(ttmlPath)) { - final byte[] buffer = new byte[64 * 1024]; - int n; - while ((n = is.read(buffer)) != -1) { - fos.write(buffer, 0, n); + private void writeNetworkData(@NotNull BufferedSource bufferedSource, @NotNull Path ttmlPath) throws IOException { + try (var fileSink = Okio.sink(ttmlPath); + var bufferedSink = Okio.buffer(fileSink)) { + bufferedSink.writeAll(bufferedSource); + logger.trace("Untertitel-Datei wurde geschrieben"); + } + + } + + /** + * Write a subtitle URL to a local file. + * The original extension (if applicable) will be removed and a .srt and .ttml file will be created. + * + * @param url The URL to the subtitle file. + * @param file File location; only filename will be used for processing. + * @throws IOException when an IO exception occurs. + */ + public void writeSubtitle(@NotNull String url, @NotNull File file) throws IOException { + if (url.isEmpty()) + return; + + var plainFileName = GuiFunktionen.getFileNameWithoutSuffix(file.getAbsolutePath()); + + final Request request = new Request.Builder().url(url).get().build(); + try (Response response = MVHttpClient.getInstance().getHttpClient().newCall(request).execute(); + ResponseBody body = response.body()) { + if (response.isSuccessful()) { + logger.trace("Untertitel {} schreiben nach {}", url, plainFileName); + + try (var source = body.source()) { + String suffix = GuiFunktionen.getSuffixFromUrl(url); + if (!suffix.endsWith(SUFFIX_SRT) && !suffix.endsWith(SUFFIX_VTT)) { + suffix = SUFFIX_TTML; + } + + final String strSubtitleFile = plainFileName + '.' + suffix; + final Path ttmlPath = Paths.get(strSubtitleFile); + + writeNetworkData(source, ttmlPath); + + convertSubtitle(ttmlPath); + } + } else { + //not successful + logger.error("HTTP Response Code {} for URL: {}", response.code(), url); } - logger.info("Untertitel-Datei wurde geschrieben"); } } public void writeSubtitle(@NotNull DatenDownload datenDownload) { final String urlSubtitle = datenDownload.arr[DatenDownload.DOWNLOAD_URL_SUBTITLE]; - final String targetDirectory = datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_PFAD]; - if (urlSubtitle.isEmpty()) return; + final String targetDirectory = datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_PFAD]; + final Request request = new Request.Builder().url(urlSubtitle).get().build(); try (Response response = MVHttpClient.getInstance().getHttpClient().newCall(request).execute(); ResponseBody body = response.body()) { - if (body != null && response.isSuccessful()) { - logger.info("Untertitel {} schreiben nach {}", urlSubtitle, targetDirectory); + if (response.isSuccessful()) { + logger.trace("Untertitel {} schreiben nach {}", urlSubtitle, targetDirectory); - try (InputStream is = body.byteStream()) { + try (var source = body.source()) { String suffix = GuiFunktionen.getSuffixFromUrl(urlSubtitle); if (!suffix.endsWith(SUFFIX_SRT) && !suffix.endsWith(SUFFIX_VTT)) { suffix = SUFFIX_TTML; @@ -65,9 +104,9 @@ public void writeSubtitle(@NotNull DatenDownload datenDownload) { final String strSubtitleFile = datenDownload.getFileNameWithoutSuffix() + '.' + suffix; final Path ttmlPath = Paths.get(strSubtitleFile); - writeNetworkData(is, ttmlPath); + writeNetworkData(source, ttmlPath); - convertSubtitle(datenDownload, ttmlPath, strSubtitleFile); + convertSubtitle(ttmlPath); } } else { //not successful @@ -78,14 +117,20 @@ public void writeSubtitle(@NotNull DatenDownload datenDownload) { } } - private void convertSubtitle(DatenDownload datenDownload, Path ttmlPath, String strSubtitleFile) { - try (TimedTextMarkupLanguageParser ttmlp = new TimedTextMarkupLanguageParser()) { - if (!strSubtitleFile.endsWith('.' + SUFFIX_SRT) && !strSubtitleFile.endsWith("." + SUFFIX_VTT)) { - final Path srt = Paths.get(datenDownload.getFileNameWithoutSuffix() + "." + SUFFIX_SRT); - if (ttmlp.parse(ttmlPath)) { - ttmlp.toSrt(srt); - } else if (ttmlp.parseXmlFlash(ttmlPath)) { - ttmlp.toSrt(srt); + /** + * Convert a TTML subtitle file to SRT format. + * @param ttmlPath The path to the origin TTML subtitle file. + */ + private void convertSubtitle(@NotNull Path ttmlPath) { + var subtitleFileStr = GuiFunktionen.getFileNameWithoutSuffix(ttmlPath.toAbsolutePath().toString()); + + try (TimedTextMarkupLanguageParser parser = new TimedTextMarkupLanguageParser()) { + if (!subtitleFileStr.endsWith('.' + SUFFIX_SRT) && !subtitleFileStr.endsWith("." + SUFFIX_VTT)) { + final Path srt = Paths.get(subtitleFileStr + "." + SUFFIX_SRT); + if (parser.parse(ttmlPath)) { + parser.toSrt(srt); + } else if (parser.parseXmlFlash(ttmlPath)) { + parser.toSrt(srt); } logger.info("Untertitel-Datei wurde konvertiert."); } diff --git a/src/main/java/mediathek/tool/NoSelectionErrorDialog.java b/src/main/java/mediathek/tool/NoSelectionErrorDialog.java index 89a098afe2..5ee29582b7 100644 --- a/src/main/java/mediathek/tool/NoSelectionErrorDialog.java +++ b/src/main/java/mediathek/tool/NoSelectionErrorDialog.java @@ -1,18 +1,16 @@ package mediathek.tool; -import javafx.application.Platform; -import javafx.scene.control.Alert; import mediathek.config.Konstanten; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; public class NoSelectionErrorDialog { - public static void show() { - Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText("Befehl kann nicht ausgeführt werden."); - alert.setContentText("Sie haben keinen Tabelleneintrag ausgewählt."); - alert.show(); - }); + public static void show(@Nullable Component parent) { + JOptionPane.showMessageDialog(parent, "Der Befehl kann nicht ausgeführt werden.\n" + + "Sie haben keinen Tabelleneintrag ausgewählt.", + Konstanten.PROGRAMMNAME, JOptionPane.ERROR_MESSAGE); } } diff --git a/src/main/java/mediathek/tool/RuntimeStatistics.java b/src/main/java/mediathek/tool/RuntimeStatistics.java index 5d380c93ce..9371e0e74a 100644 --- a/src/main/java/mediathek/tool/RuntimeStatistics.java +++ b/src/main/java/mediathek/tool/RuntimeStatistics.java @@ -1,5 +1,7 @@ package mediathek.tool; +import mediathek.config.Config; +import mediathek.tool.http.MVHttpClient; import org.apache.logging.log4j.LogManager; import java.time.Duration; @@ -25,4 +27,13 @@ public static void printRuntimeStatistics() { logger.info(" --> Ende: {}", formatter.format(endZeit)); logger.info(" --> Laufzeit: {}h {}m {}s", runTime.getHour(),runTime.getMinute(),runTime.getSecond()); } + + public static void printDataUsageStatistics() { + if (Config.isEnhancedLoggingEnabled()) { + var byteCounter = MVHttpClient.getInstance().getByteCounter(); + var logger = LogManager.getLogger(); + logger.info("total data sent: {}", FileUtils.humanReadableByteCountBinary(byteCounter.totalBytesWritten())); + logger.info("total data received: {}", FileUtils.humanReadableByteCountBinary(byteCounter.totalBytesRead())); + } + } } diff --git a/src/main/java/mediathek/tool/SVGIconUtilities.java b/src/main/java/mediathek/tool/SVGIconUtilities.java new file mode 100644 index 0000000000..9796fe2a91 --- /dev/null +++ b/src/main/java/mediathek/tool/SVGIconUtilities.java @@ -0,0 +1,16 @@ +package mediathek.tool; + +import com.formdev.flatlaf.extras.FlatSVGIcon; +import org.jetbrains.annotations.NotNull; + +public class SVGIconUtilities { + public static FlatSVGIcon createSVGIcon(@NotNull String resource) { + return createSVGIcon(resource, 15f); + } + + public static FlatSVGIcon createSVGIcon(@NotNull String resource, float height) { + FlatSVGIcon icon = new FlatSVGIcon(resource); + float scaleFactor = (1f / icon.getIconHeight()) * height; + return icon.derive(scaleFactor); + } +} diff --git a/src/main/java/mediathek/tool/ShutdownState.java b/src/main/java/mediathek/tool/ShutdownState.java index ce636f7a8f..f2af880aba 100644 --- a/src/main/java/mediathek/tool/ShutdownState.java +++ b/src/main/java/mediathek/tool/ShutdownState.java @@ -10,6 +10,8 @@ public enum ShutdownState { STOP_DOWNLOADS("Downloads anhalten"), SAVE_APP_DATA("Programmdaten sichern"), SAVE_BOOKMARKS("Merkliste sichern"), + + TERMINATE_JAVAFX_SUPPORT("Beende JavaFX Support..."), COMPLETE("Fertig."); private final String title; diff --git a/src/main/java/mediathek/tool/SwingErrorDialog.java b/src/main/java/mediathek/tool/SwingErrorDialog.java new file mode 100644 index 0000000000..79d35dfd0c --- /dev/null +++ b/src/main/java/mediathek/tool/SwingErrorDialog.java @@ -0,0 +1,42 @@ +package mediathek.tool; + +import mediathek.config.Konstanten; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.io.PrintWriter; +import java.io.StringWriter; + +public class SwingErrorDialog { + public static void showExceptionMessage(@Nullable Component parentComponent, + @NotNull String messageText, + @NotNull Exception exception) throws HeadlessException { + + StringWriter stringWriter = new StringWriter(); + exception.printStackTrace(new PrintWriter(stringWriter)); + + JLabel message = new JLabel(messageText); + message.setBorder(BorderFactory.createEmptyBorder(3, 0, 10, 0)); + + JTextArea text = new JTextArea(); + text.setEditable(false); + text.setFont(UIManager.getFont("Label.font")); + text.setText(stringWriter.toString()); + text.setCaretPosition(0); + + JScrollPane scroller = new JScrollPane(text); + scroller.setPreferredSize(new Dimension(640, 350)); + + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + + panel.add(message, BorderLayout.NORTH); + panel.add(scroller, BorderLayout.SOUTH); + + JOptionPane.showMessageDialog(parentComponent, panel, Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE); + + } +} diff --git a/src/main/java/mediathek/tool/TimerPool.kt b/src/main/java/mediathek/tool/TimerPool.kt index 9109323a92..6c56e3191a 100644 --- a/src/main/java/mediathek/tool/TimerPool.kt +++ b/src/main/java/mediathek/tool/TimerPool.kt @@ -13,16 +13,14 @@ object TimerPool { private val logger: Logger = LogManager.getLogger() @JvmStatic - val timerPool = - ScheduledThreadPoolExecutor((Runtime.getRuntime().availableProcessors() / 2).coerceIn(2, 4), - TimerPoolThreadFactory()) + val timerPool = ScheduledThreadPoolExecutor(2, TimerPoolThreadFactory()) init { logger.trace("Initializing timer pool...") //get rid of cancelled tasks immediately... timerPool.removeOnCancelPolicy = true timerPool.allowCoreThreadTimeOut(true) - timerPool.setKeepAliveTime(1, TimeUnit.MINUTES) + timerPool.setKeepAliveTime(30, TimeUnit.SECONDS) timerPool.scheduleWithFixedDelay({ messageBus.publishAsync(TimerEvent()) }, 4, 1, TimeUnit.SECONDS) } diff --git a/src/main/java/mediathek/tool/Version.kt b/src/main/java/mediathek/tool/Version.kt index 02f1d488d1..e53aafc186 100644 --- a/src/main/java/mediathek/tool/Version.kt +++ b/src/main/java/mediathek/tool/Version.kt @@ -1,5 +1,6 @@ package mediathek.tool +import mediathek.config.ApplicationType import mediathek.config.Konstanten import org.apache.logging.log4j.LogManager @@ -28,10 +29,11 @@ data class Version(val major: Int, val minor: Int, val patch: Int) { } override fun toString(): String { - return if (Konstanten.APP_IS_NIGHTLY) - String.format("%d.%d.%d-nightly", major, minor, patch) - else - String.format("%d.%d.%d", major, minor, patch) + var version = String.format("%d.%d.%d", major, minor, patch) + if (Konstanten.APPLICATION_TYPE == ApplicationType.NIGHTLY) + version += "-nightly" + + return version } /** diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java index a8399f4d4d..34d46b7a34 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java @@ -1,6 +1,7 @@ package mediathek.tool.cellrenderer; import com.formdev.flatlaf.util.ScaledImageIcon; +import mediathek.tool.GuiFunktionen; import mediathek.tool.sender_icon_cache.MVSenderIconCache; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; @@ -8,6 +9,7 @@ import javax.swing.*; import javax.swing.table.DefaultTableCellRenderer; import java.awt.*; +import java.util.concurrent.atomic.AtomicReference; /** * Base class for all cell renderer. @@ -25,34 +27,30 @@ public class CellRendererBase extends DefaultTableCellRenderer { * @param sender Name of the sender. */ protected void setSenderIcon(@NotNull String sender, @NotNull Dimension targetDim) { - var key = new SenderCacheKey(sender, targetDim); - var cachedIcon = senderCellIconCache.getOrDefault(key, null); - if (cachedIcon == null) { - //creeate icon and store in cache - var origIcon = MVSenderIconCache.get(sender); - if (origIcon.isPresent()) { - var icon = origIcon.get(); - Dimension iconDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); - var scaleDim = getScaledDimension(iconDim, targetDim); - Icon imgIcon; - //Of course Windows is again the only OS which sucks at automatic scaling... - if (SystemUtils.IS_OS_WINDOWS) { - imgIcon = new ScaledImageIcon(new ImageIcon(icon.getImage()), scaleDim.width, scaleDim.height); - } else { - Image newimg = icon.getImage().getScaledInstance(scaleDim.width, scaleDim.height, Image.SCALE_SMOOTH); - imgIcon = new ImageIcon(newimg); - } + // make target dims for icon slightly smaller + if (SystemUtils.IS_OS_LINUX) { + targetDim.width -= 4; + targetDim.height -= 4; + } - cachedIcon = imgIcon; - senderCellIconCache.put(key, cachedIcon); - } + var key = new SenderCacheKey(sender, targetDim); + final AtomicReference cachedIcon = new AtomicReference<>(); + cachedIcon.set(senderCellIconCache.getOrDefault(key, null)); + if (cachedIcon.get() == null) { + MVSenderIconCache.get(sender).ifPresentOrElse(icon -> { + var imageDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); + var destDim = GuiFunktionen.calculateFittedDimension(imageDim, targetDim); + cachedIcon.set(new ScaledImageIcon(icon, destDim.width, destDim.height)); + senderCellIconCache.put(key, cachedIcon.get()); + }, () -> cachedIcon.set(null)); } - if (cachedIcon != null) { - setHorizontalAlignment(SwingConstants.CENTER); + if (cachedIcon.get() != null) { setText(""); - setIcon(cachedIcon); + setIcon(cachedIcon.get()); } + setVerticalAlignment(SwingConstants.CENTER); + setHorizontalAlignment(SwingConstants.CENTER); } /** @@ -71,23 +69,4 @@ protected Dimension getSenderCellDimension(@NotNull JTable table, int row, int c targetDim.width -= 4; return targetDim; } - - /** - * Calculate the target dimensions based on image size and a boundary. - * - * @param imageSize the size of the original image. - * @param boundary the boundary size. - * @return the target boundary while maintaining aspect ratio. - */ - protected Dimension getScaledDimension(@NotNull Dimension imageSize, @NotNull Dimension boundary) { - - double widthRatio = boundary.getWidth() / imageSize.getWidth(); - double heightRatio = boundary.getHeight() / imageSize.getHeight(); - double ratio = Math.min(widthRatio, heightRatio); - - return new Dimension((int) (imageSize.width * ratio), - (int) (imageSize.height * ratio)); - } - - } diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java index 860e320685..c50f736999 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java @@ -1,31 +1,115 @@ package mediathek.tool.cellrenderer; +import com.formdev.flatlaf.extras.FlatSVGIcon; import mediathek.config.MVColor; import mediathek.controller.starter.Start; -import mediathek.gui.messages.GeoStateChangedEvent; +import mediathek.daten.Country; +import mediathek.daten.DatenFilm; import mediathek.tool.ApplicationConfiguration; +import mediathek.tool.CompoundIcon; import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; +import mediathek.tool.SVGIconUtilities; import org.apache.commons.configuration2.Configuration; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.stream.Collectors; /** * CellRenderer base class for all custom renderer associated with a Start. */ public class CellRendererBaseWithStart extends CellRendererBase { + private static final EnumSet euCountryList = EnumSet.of(Country.DE, Country.AT, Country.FR); protected final Configuration config = ApplicationConfiguration.getConfiguration(); - protected boolean geoMelden; + protected final FlatSVGIcon lockedIcon; + protected final FlatSVGIcon lockedIconSelected; + protected final FlatSVGIcon unlockedIcon; + protected final FlatSVGIcon unlockedIconSelected; + /** + * Temporary storage for the icons that will be assembled to a compound icon. + */ + private final List iconList = new ArrayList<>(); + private final FlatSVGIcon subtitleIcon; + private final FlatSVGIcon subtitleIconSelected; + private final FlatSVGIcon highQualityIcon; + private final FlatSVGIcon highQualityIconSelected; + private final FlatSVGIcon liveStreamIcon; + private final FlatSVGIcon liveStreamIconSelected; + private final FlatSVGIcon audioDescription; + private final FlatSVGIcon audioDescriptionSelected; + protected FlatSVGIcon.ColorFilter whiteColorFilter = new FlatSVGIcon.ColorFilter(color -> Color.WHITE); public CellRendererBaseWithStart() { MessageBus.getMessageBus().subscribe(this); - geoMelden = config.getBoolean(ApplicationConfiguration.GEO_REPORT, false); + + lockedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock.svg"); + lockedIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock.svg"); + lockedIconSelected.setColorFilter(whiteColorFilter); + + unlockedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock-open.svg"); + unlockedIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock-open.svg"); + unlockedIconSelected.setColorFilter(whiteColorFilter); + + subtitleIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/closed-captioning.svg"); + subtitleIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/closed-captioning.svg"); + subtitleIconSelected.setColorFilter(whiteColorFilter); + + highQualityIcon = SVGIconUtilities.createSVGIcon("icons/derreisende77/high-quality.svg"); + highQualityIconSelected = SVGIconUtilities.createSVGIcon("icons/derreisende77/high-quality.svg"); + highQualityIconSelected.setColorFilter(whiteColorFilter); + + liveStreamIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/tower-cell.svg"); + liveStreamIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/tower-cell.svg"); + liveStreamIconSelected.setColorFilter(whiteColorFilter); + + audioDescription = SVGIconUtilities.createSVGIcon("icons/fontawesome/audio-description.svg"); + audioDescriptionSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/audio-description.svg"); + audioDescriptionSelected.setColorFilter(whiteColorFilter); + } + + protected void drawGeolocationIcons(@NotNull DatenFilm film, boolean isSelected) { + setHorizontalAlignment(SwingConstants.CENTER); + setText(""); + if (film.countrySet.isEmpty()) { + setToolTipText("Keine Geoinformationen vorhanden"); + if (isSelected) + setIcon(unlockedIconSelected); + else + setIcon(unlockedIcon); + } + else { + var geoString = film.countrySet.stream().map(Country::toString).collect(Collectors.joining("-")); + setToolTipText(geoString); + if (filmIsCountryUnlocked(film)) { + //we are unlocked + if (isSelected) + setIcon(unlockedIconSelected); + else + setIcon(unlockedIcon); + } + else { + // locked + if (isSelected) + setIcon(lockedIconSelected); + else + setIcon(lockedIcon); + } + } } - @Handler - private void handleGeoStateChanged(GeoStateChangedEvent e) { - SwingUtilities.invokeLater(() -> geoMelden = config.getBoolean(ApplicationConfiguration.GEO_REPORT, false)); + private boolean filmIsCountryUnlocked(@NotNull DatenFilm film) { + var curLocation = ApplicationConfiguration.getInstance().getGeographicLocation(); + //EU consists of many states therefore we have to extend the country test... + if (film.countrySet.contains(Country.EU)) { + return film.countrySet.contains(curLocation) || euCountryList.contains(curLocation); + } + else { + return film.countrySet.contains(curLocation); + } } protected void resetComponent() { @@ -40,52 +124,96 @@ protected void setBackgroundColor(final Component c, final Start s, final boolea if (s != null) { Color color = null; switch (s.status) { - case Start.STATUS_INIT: + case Start.STATUS_INIT -> { if (isSelected) color = MVColor.DOWNLOAD_WAIT_SEL.color; else color = MVColor.DOWNLOAD_WAIT.color; - break; - - case Start.STATUS_RUN: + } + case Start.STATUS_RUN -> { if (isSelected) color = MVColor.DOWNLOAD_RUN_SEL.color; else color = MVColor.DOWNLOAD_RUN.color; - break; - - case Start.STATUS_FERTIG: + } + case Start.STATUS_FERTIG -> { if (isSelected) color = MVColor.DOWNLOAD_FERTIG_SEL.color; else color = MVColor.DOWNLOAD_FERTIG.color; - break; - - case Start.STATUS_ERR: + } + case Start.STATUS_ERR -> { if (isSelected) color = MVColor.DOWNLOAD_FEHLER_SEL.color; else color = MVColor.DOWNLOAD_FEHLER.color; - break; + } } c.setBackground(color); } } - private void setGeoblockingBackgroundColor(final Component c, final boolean isSelected) { - final Color color; - if (isSelected) - color = MVColor.FILM_GEOBLOCK_BACKGROUND_SEL.color; - else - color = MVColor.FILM_GEOBLOCK_BACKGROUND.color; + /** + * Show "CC" and/or "HQ" icon(s) when supported by the film. + * + * @param datenFilm film information + * @param isSelected is row selected. + */ + protected void setIndicatorIcons(@NotNull JTable table, @NotNull DatenFilm datenFilm, boolean isSelected) { + if (!datenFilm.countrySet.isEmpty()) { + if (!filmIsCountryUnlocked(datenFilm)) { + //locked + if (isSelected) + iconList.add(lockedIconSelected); + else + iconList.add(lockedIcon); + } + } - c.setBackground(color); - } + var tc = table.getColumn("HQ"); + // if HQ column is NOT visible add icon + if (tc.getWidth() == 0) { + if (datenFilm.isHighQuality()) { + if (isSelected) + iconList.add(highQualityIconSelected); + else + iconList.add(highQualityIcon); + } + } - protected void setupGeoblockingBackground(final Component c, final String geo, final boolean isSelected) { - if (!geo.isEmpty()) { - if (!geo.contains(config.getString(ApplicationConfiguration.GEO_LOCATION))) - setGeoblockingBackgroundColor(c, isSelected); + if (datenFilm.isAudioVersion()) { + if (isSelected) + iconList.add(audioDescriptionSelected); + else + iconList.add(audioDescription); } + + tc = table.getColumn("UT"); + //if UT column is NOT visible + if (tc.getWidth() == 0) { + if (datenFilm.hasSubtitle()) { + if (isSelected) + iconList.add(subtitleIconSelected); + else + iconList.add(subtitleIcon); + } + } + + if (datenFilm.isLivestream()) { + if (isSelected) + iconList.add(liveStreamIconSelected); + else + iconList.add(liveStreamIcon); + } + + Icon icon; + if (iconList.size() == 1) + icon = iconList.get(0); + else + icon = new CompoundIcon(CompoundIcon.Axis.X_AXIS, 3, iconList.toArray(new Icon[0])); + setIcon(icon); + + //always clear at the end + iconList.clear(); } } diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java index 2b27e6db6d..9c3669ec95 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java @@ -1,18 +1,16 @@ package mediathek.tool.cellrenderer; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; +import com.formdev.flatlaf.extras.FlatSVGIcon; import mediathek.config.MVColor; import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.table.MVTable; -import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.swing.*; import javax.swing.border.Border; -import javax.swing.plaf.basic.BasicProgressBarUI; import java.awt.*; public class CellRendererDownloads extends CellRendererBaseWithStart { @@ -22,60 +20,46 @@ public class CellRendererDownloads extends CellRendererBaseWithStart { private final static String DOWNLOAD_ENTFERNEN = "Download entfernen"; private final static String PLAY_DOWNLOADED_FILM = "gespeicherten Film abspielen"; private static final Logger logger = LogManager.getLogger(CellRendererDownloads.class); - private final Icon film_start_tab; + private final FlatSVGIcon film_start_tab; private final Icon film_start_sw_tab; - private final Border emptyBorder = BorderFactory.createEmptyBorder(); + private final Border emptyBorder = BorderFactory.createEmptyBorder(3,2,3,2); private final Border largeBorder = BorderFactory.createEmptyBorder(9, 2, 9, 2); private final JPanel panel; - private final Icon download_stop_tab; - private final Icon download_stop_sw_tab; - private final Icon download_start_tab; + private final FlatSVGIcon download_stop_tab; + private final FlatSVGIcon download_stop_sw_tab; + private final FlatSVGIcon download_start_tab; private final Icon download_start_sw_tab; - private final Icon download_clear_tab_selected; + private final FlatSVGIcon download_clear_tab_selected; private final Icon download_clear_sw_tab; - private final Icon download_del_tab_selected; + private final FlatSVGIcon download_del_tab_selected; private final Icon download_del_sw_tab; - private JProgressBar progressBar; + private final JProgressBar progressBar = new JProgressBar(0, 1000); public CellRendererDownloads() { - download_stop_tab = IconFontSwing.buildIcon(FontAwesome.STOP, 16, Color.WHITE); - download_stop_sw_tab = IconFontSwing.buildIcon(FontAwesome.STOP, 16); - download_start_tab = IconFontSwing.buildIcon(FontAwesome.CARET_DOWN, 16, Color.WHITE); - download_start_sw_tab = IconFontSwing.buildIcon(FontAwesome.CARET_DOWN, 16); - download_clear_tab_selected = IconFontSwing.buildIcon(FontAwesome.ERASER, 16, Color.WHITE); - download_clear_sw_tab = IconFontSwing.buildIcon(FontAwesome.ERASER, 16); - - download_del_tab_selected = IconFontSwing.buildIcon(FontAwesome.TRASH, 16, Color.WHITE); - download_del_sw_tab = IconFontSwing.buildIcon(FontAwesome.TRASH, 16); - - film_start_tab = IconFontSwing.buildIcon(FontAwesome.PLAY, 16, Color.WHITE); - film_start_sw_tab = IconFontSwing.buildIcon(FontAwesome.PLAY, 16); - - setupProgressBar(); + var whiteColorFilter = new FlatSVGIcon.ColorFilter(color -> Color.WHITE); + + download_stop_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/stop.svg"); + download_stop_tab.setColorFilter(whiteColorFilter); + download_stop_sw_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/stop.svg"); + download_start_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/caret-down.svg"); + download_start_tab.setColorFilter(whiteColorFilter); + download_start_sw_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/caret-down.svg"); + download_clear_tab_selected = SVGIconUtilities.createSVGIcon("icons/fontawesome/eraser.svg"); + download_clear_tab_selected.setColorFilter(whiteColorFilter); + download_clear_sw_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/eraser.svg"); + + download_del_tab_selected = SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg"); + download_del_tab_selected.setColorFilter(whiteColorFilter); + download_del_sw_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg"); + + film_start_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg"); + film_start_tab.setColorFilter(whiteColorFilter); + film_start_sw_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg"); panel = new JPanel(new BorderLayout()); panel.add(progressBar); } - private void setupProgressBar() { - progressBar = new JProgressBar(0, 1000); - progressBar.setStringPainted(true); - //on OSX the OS provided progress bar looks much better... - if (!SystemUtils.IS_OS_MAC_OSX) { - progressBar.setUI(new BasicProgressBarUI() { - @Override - protected Color getSelectionBackground() { - return UIManager.getDefaults().getColor("Table.foreground"); - } - - @Override - protected Color getSelectionForeground() { - return Color.WHITE; - } - }); - } - } - @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { @@ -101,14 +85,13 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole handleAboColumn(textArea, datenDownload); } setBackgroundColor(textArea, datenDownload.start, isSelected); - handleGeoBlocking(textArea, datenDownload, isSelected); return textArea; } } } switch (columnModelIndex) { - case DatenDownload.DOWNLOAD_PROGRESS: + case DatenDownload.DOWNLOAD_PROGRESS -> { setHorizontalAlignment(SwingConstants.CENTER); if (((MVTable) table).showSenderIcons() && !((MVTable) table).useSmallSenderIcons) { progressBar.setBorder(largeBorder); @@ -133,80 +116,51 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole } else { setText(""); } - break; - - case DatenDownload.DOWNLOAD_RESTZEIT: - setHorizontalAlignment(SwingConstants.CENTER); - if (datenDownload.start != null && datenDownload.start.beginnAnschauen) { - setForeground(MVColor.DOWNLOAD_ANSEHEN.color); - } - break; - - case DatenDownload.DOWNLOAD_FILM_NR: + } + case DatenDownload.DOWNLOAD_FILM_NR -> { if ((int) table.getModel().getValueAt(rowModelIndex, DatenDownload.DOWNLOAD_FILM_NR) == 0) { setText(""); } setHorizontalAlignment(SwingConstants.CENTER); - break; - - case DatenDownload.DOWNLOAD_ART: + } + case DatenDownload.DOWNLOAD_ART -> { switch (datenDownload.art) { case DatenDownload.ART_DOWNLOAD -> setText(DatenDownload.ART_DOWNLOAD_TXT); case DatenDownload.ART_PROGRAMM -> setText(DatenDownload.ART_PROGRAMM_TXT); } - break; - case DatenDownload.DOWNLOAD_QUELLE: + } + case DatenDownload.DOWNLOAD_QUELLE -> { switch (datenDownload.quelle) { case DatenDownload.QUELLE_ALLE -> setText(DatenDownload.QUELLE_ALLE_TXT); case DatenDownload.QUELLE_ABO -> setText(DatenDownload.QUELLE_ABO_TXT); case DatenDownload.QUELLE_BUTTON -> setText(DatenDownload.QUELLE_BUTTON_TXT); case DatenDownload.QUELLE_DOWNLOAD -> setText(DatenDownload.QUELLE_DOWNLOAD_TXT); } - break; - - case DatenDownload.DOWNLOAD_BUTTON_START: - handleButtonStartColumn(datenDownload, isSelected); - break; - - case DatenDownload.DOWNLOAD_BUTTON_DEL: - handleButtonDeleteColumn(datenDownload, isSelected); - break; - - case DatenDownload.DOWNLOAD_GROESSE: - setHorizontalAlignment(SwingConstants.RIGHT); - break; - - case DatenDownload.DOWNLOAD_ABO: - handleAboColumn(datenDownload); - break; - - case DatenDownload.DOWNLOAD_NR: - case DatenDownload.DOWNLOAD_DATUM: - case DatenDownload.DOWNLOAD_ZEIT: - case DatenDownload.DOWNLOAD_DAUER: - case DatenDownload.DOWNLOAD_BANDBREITE: - setHorizontalAlignment(SwingConstants.CENTER); - break; - - case DatenDownload.DOWNLOAD_SENDER: + } + case DatenDownload.DOWNLOAD_BUTTON_START -> handleButtonStartColumn(datenDownload, isSelected); + case DatenDownload.DOWNLOAD_BUTTON_DEL -> handleButtonDeleteColumn(datenDownload, isSelected); + case DatenDownload.DOWNLOAD_GROESSE -> setHorizontalAlignment(SwingConstants.RIGHT); + case DatenDownload.DOWNLOAD_ABO -> handleAboColumn(datenDownload); + case DatenDownload.DOWNLOAD_NR, DatenDownload.DOWNLOAD_DATUM, DatenDownload.DOWNLOAD_ZEIT, + DatenDownload.DOWNLOAD_DAUER, DatenDownload.DOWNLOAD_BANDBREITE, + DatenDownload.DOWNLOAD_RESTZEIT -> + setHorizontalAlignment(SwingConstants.CENTER); + case DatenDownload.DOWNLOAD_SENDER -> { if (((MVTable) table).showSenderIcons()) { Dimension targetDim = getSenderCellDimension(table, row, columnModelIndex); setSenderIcon(value.toString(), targetDim); } - break; + } + case DatenDownload.DOWNLOAD_GEO -> drawGeolocationIcons(datenDownload.film, isSelected); } if (columnModelIndex == DatenDownload.DOWNLOAD_TITEL) { if (datenDownload.film != null) { - var title = datenDownload.film.getTitle(); - var columnWidth = table.getColumnModel().getColumn(columnModelIndex).getWidth(); - if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) - setToolTipText(title); + setIndicatorIcons(table, datenDownload.film, isSelected); } } setBackgroundColor(this, datenDownload.start, isSelected); - handleGeoBlocking(this, datenDownload, isSelected); } catch (Exception ex) { logger.error(ex); } @@ -264,14 +218,6 @@ private void handleAboColumn(final DatenDownload datenDownload) { } } - private void handleGeoBlocking(Component c, final DatenDownload datenDownload, final boolean isSelected) { - if (geoMelden) { - if (datenDownload.start == null) { - setupGeoblockingBackground(c, datenDownload.arr[DatenDownload.DOWNLOAD_GEO], isSelected); - } - } - } - private void handleButtonDeleteColumn(final DatenDownload datenDownload, final boolean isSelected) { setHorizontalAlignment(SwingConstants.CENTER); if (datenDownload.start != null) { diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index c5af2a3b1b..85c5c7754c 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -1,13 +1,13 @@ package mediathek.tool.cellrenderer; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; +import com.formdev.flatlaf.extras.FlatSVGIcon; import mediathek.config.Daten; import mediathek.config.MVColor; import mediathek.controller.history.SeenHistoryController; import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; import mediathek.daten.DatenFilm; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.table.MVTable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -18,30 +18,40 @@ public class CellRendererFilme extends CellRendererBaseWithStart { private static final Logger logger = LogManager.getLogger(CellRendererFilme.class); - private final Icon selectedStopIcon; - private final Icon normalStopIcon; + private final FlatSVGIcon selectedStopIcon; + private final FlatSVGIcon normalStopIcon; private final SeenHistoryController history = new SeenHistoryController(); - private final Icon selectedDownloadIcon; - private final Icon normalDownloadIcon; - private final Icon selectedPlayIcon; - private final Icon normalPlayIcon; - private final Icon selectedBookmarkIcon; - private final Icon normalBookmarkIcon; - private final Icon selectedBookmarkIconHighlighted; + private final FlatSVGIcon selectedDownloadIcon; + private final FlatSVGIcon normalDownloadIcon; + private final FlatSVGIcon selectedPlayIcon; + private final FlatSVGIcon normalPlayIcon; + private final FlatSVGIcon selectedBookmarkIcon; + private final FlatSVGIcon normalBookmarkIcon; + private final FlatSVGIcon selectedBookmarkIconHighlighted; public CellRendererFilme() { - selectedDownloadIcon = IconFontSwing.buildIcon(FontAwesome.DOWNLOAD, 16, Color.WHITE); - normalDownloadIcon = IconFontSwing.buildIcon(FontAwesome.DOWNLOAD, 16); + selectedDownloadIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg"); + selectedDownloadIcon.setColorFilter(whiteColorFilter); - selectedPlayIcon = IconFontSwing.buildIcon(FontAwesome.PLAY, 16, Color.WHITE); - normalPlayIcon = IconFontSwing.buildIcon(FontAwesome.PLAY, 16); + normalDownloadIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg"); - selectedStopIcon = IconFontSwing.buildIcon(FontAwesome.STOP, 16, Color.WHITE); - normalStopIcon = IconFontSwing.buildIcon(FontAwesome.STOP, 16); + selectedPlayIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg"); + selectedPlayIcon.setColorFilter(whiteColorFilter); - selectedBookmarkIcon = IconFontSwing.buildIcon(FontAwesome.BOOKMARK, 16, Color.BLUE); - selectedBookmarkIconHighlighted = IconFontSwing.buildIcon(FontAwesome.BOOKMARK, 16, Color.yellow); - normalBookmarkIcon = IconFontSwing.buildIcon(FontAwesome.BOOKMARK_O, 16); + normalPlayIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg"); + + selectedStopIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/stop.svg"); + selectedStopIcon.setColorFilter(whiteColorFilter); + + normalStopIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/stop.svg"); + + selectedBookmarkIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg"); + selectedBookmarkIcon.setColorFilter(whiteColorFilter); + + selectedBookmarkIconHighlighted = SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg"); + selectedBookmarkIconHighlighted.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.ORANGE)); + + normalBookmarkIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg"); } private JTextArea createTextArea(String content) { @@ -75,10 +85,9 @@ public Component getTableCellRendererComponent( final int columnModelIndex = table.convertColumnIndexToModel(column); final DatenFilm datenFilm = (DatenFilm) table.getModel().getValueAt(rowModelIndex, DatenFilm.FILM_REF); final DatenDownload datenDownload = Daten.getInstance().getListeDownloadsButton().getDownloadUrlFilm(datenFilm.getUrlNormalQuality()); - final boolean isBookMarked = datenFilm.isBookmarked(); - final var mvTable = (MVTable)table; + final boolean isBookMarked = datenFilm.isBookmarked(); + final var mvTable = (MVTable) table; - setFont((mvTable.getDefaultFont())); //shortcut if we want to have line breaks, use text areas and skip the rest if (mvTable.isLineBreak()) { @@ -96,7 +105,7 @@ public Component getTableCellRendererComponent( //here comes the content... switch (columnModelIndex) { case DatenFilm.FILM_DAUER: - setText(datenFilm.getDauer()); + setText(datenFilm.getFilmLengthAsString()); break; case DatenFilm.FILM_ABSPIELEN: @@ -106,24 +115,26 @@ public Component getTableCellRendererComponent( case DatenFilm.FILM_AUFZEICHNEN: handleButtonDownloadColumn(isSelected); break; - + case DatenFilm.FILM_MERKEN: handleButtonBookmarkColumn(isBookMarked, isSelected, datenFilm.isLivestream()); break; - + case DatenFilm.FILM_SENDER: if (mvTable.showSenderIcons()) { - Dimension targetDim = getSenderCellDimension(table, row,columnModelIndex); + Dimension targetDim = getSenderCellDimension(table, row, columnModelIndex); setSenderIcon(value.toString(), targetDim); } break; - case DatenFilm.FILM_TITEL: - var title = datenFilm.getTitle(); - var columnWidth = table.getColumnModel().getColumn(columnModelIndex).getWidth(); - if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) - setToolTipText(title); - break; + case DatenFilm.FILM_TITEL: + setText(datenFilm.getTitle()); + setIndicatorIcons(table, datenFilm, isSelected); + break; + + case DatenFilm.FILM_GEO: + drawGeolocationIcons(datenFilm, isSelected); + break; } applyColorSettings(this, datenFilm, datenDownload, isSelected, isBookMarked); @@ -136,11 +147,13 @@ public Component getTableCellRendererComponent( /** * Apply the specific horizontal alignment to the cell based on column + * * @param columnModelIndex the current column index */ private void applyHorizontalAlignment(final int columnModelIndex) { switch (columnModelIndex) { - case DatenFilm.FILM_NR, DatenFilm.FILM_DATUM, DatenFilm.FILM_ZEIT, DatenFilm.FILM_DAUER, DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN -> setHorizontalAlignment(SwingConstants.CENTER); + case DatenFilm.FILM_NR, DatenFilm.FILM_DATUM, DatenFilm.FILM_ZEIT, DatenFilm.FILM_DAUER, DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN -> + setHorizontalAlignment(SwingConstants.CENTER); case DatenFilm.FILM_GROESSE -> setHorizontalAlignment(SwingConstants.RIGHT); } } @@ -155,27 +168,17 @@ private void applyColorSettings(Component c, @NotNull DatenFilm datenFilm, Daten setBackgroundColor(c, datenDownload.start, isSelected); } else { //not a start, set specific background colors - if (datenFilm.isLivestream()) { - // bei livestreams keine History anzeigen - c.setForeground(MVColor.FILM_LIVESTREAM.color); - } else if (hasBeenSeen) { + if (hasBeenSeen) { if (!isSelected) { c.setBackground(MVColor.FILM_HISTORY.color); } } else if (datenFilm.isNew()) { // fix #259 if (!isSelected) - c.setForeground(MVColor.FILM_NEU.color); + c.setForeground(MVColor.getNewColor()); } else if (isBookMarked && !isSelected) { c.setBackground(MVColor.FILM_BOOKMARKED.color); } - - if (geoMelden) { - //only apply geo block colors when we haven´t changed the background for seen history - if (!hasBeenSeen) { - setupGeoblockingBackground(c, datenFilm.getGeo().orElse(""), isSelected); - } - } } } @@ -209,16 +212,25 @@ private void handleButtonDownloadColumn(final boolean isSelected) { // Button Aufzeichnen setIconAndToolTip(isSelected, normalDownloadIcon, selectedDownloadIcon, "Film aufzeichnen"); } - + private void handleButtonBookmarkColumn(final boolean isBookMarked, final boolean isSelected, boolean isLivestream) { if (isLivestream) { setIcon(null); setToolTipText(""); - } - else { + } else { // Button Merken setToolTipText(isBookMarked ? "Film aus Merkliste entfernen" : "Film merken"); - setIcon(isBookMarked ? (isSelected ? selectedBookmarkIconHighlighted : selectedBookmarkIcon) : normalBookmarkIcon); + if (isBookMarked) { + if (isSelected) { + setIcon(selectedBookmarkIconHighlighted); + } else + setIcon(selectedBookmarkIconHighlighted); + } else { + if (isSelected) { + setIcon(selectedBookmarkIcon); + } else + setIcon(normalBookmarkIcon); + } } } } diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java index a6e2728821..e2784a8a25 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java @@ -1,7 +1,7 @@ package mediathek.tool.cellrenderer; -import mediathek.config.Icons; import mediathek.daten.DatenProg; +import mediathek.tool.SVGIconUtilities; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -9,10 +9,8 @@ import javax.swing.table.DefaultTableCellRenderer; import java.awt.*; -@SuppressWarnings("serial") public class CellRendererProgramme extends DefaultTableCellRenderer { - private static final ImageIcon ja_16 = Icons.ICON_TABELLE_EIN; - private static final ImageIcon nein_12 = Icons.ICON_TABELLE_AUS; + private static final ImageIcon ja_16 = SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg"); private static final Logger logger = LogManager.getLogger(); public CellRendererProgramme() { @@ -36,7 +34,7 @@ public Component getTableCellRendererComponent( if (getText().equals(Boolean.TRUE.toString())) { setIcon(ja_16); } else { - setIcon(nein_12); + setIcon(null); } setText(""); } diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java index 165014553f..7e28787130 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java @@ -1,7 +1,7 @@ package mediathek.tool.cellrenderer; -import mediathek.config.Icons; import mediathek.daten.DatenPset; +import mediathek.tool.SVGIconUtilities; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -9,10 +9,8 @@ import javax.swing.table.DefaultTableCellRenderer; import java.awt.*; -@SuppressWarnings("serial") public class CellRendererPset extends DefaultTableCellRenderer { - private static final ImageIcon ja_16 = Icons.ICON_TABELLE_EIN; - private static final ImageIcon nein_12 = Icons.ICON_TABELLE_AUS; + private static final ImageIcon ja_16 = SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg"); private static final Logger logger = LogManager.getLogger(); public CellRendererPset() { @@ -49,7 +47,7 @@ public Component getTableCellRendererComponent( if (datenPset.istAbspielen()) { setIcon(ja_16); } else { - setIcon(nein_12); + setIcon(null); } } if (c == DatenPset.PROGRAMMSET_IST_SPEICHERN) { @@ -58,7 +56,7 @@ public Component getTableCellRendererComponent( if (datenPset.istSpeichern()) { setIcon(ja_16); } else { - setIcon(nein_12); + setIcon(null); } } } catch (Exception ex) { diff --git a/src/main/java/mediathek/tool/datum/DateUtil.java b/src/main/java/mediathek/tool/datum/DateUtil.java index b16ec8d2d6..75e3df1f4f 100644 --- a/src/main/java/mediathek/tool/datum/DateUtil.java +++ b/src/main/java/mediathek/tool/datum/DateUtil.java @@ -1,5 +1,8 @@ package mediathek.tool.datum; +import mediathek.daten.DatenFilm; +import org.jetbrains.annotations.NotNull; + import java.time.LocalDate; import java.time.ZoneId; import java.time.format.DateTimeFormatter; @@ -10,6 +13,12 @@ public class DateUtil { public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy") .withZone(MV_DEFAULT_TIMEZONE); + private static final ZoneId UTC_ZONE_ID = ZoneId.of("UTC"); + + public static long convertFilmDateToLuceneDate(@NotNull DatenFilm film) { + var ldt = DateUtil.convertToLocalDate(film.getDatumFilm()).atStartOfDay(); + return ldt.atZone(UTC_ZONE_ID).toInstant().toEpochMilli(); + } public static LocalDate convertToLocalDate(Date dateToConvert) { return dateToConvert.toInstant() diff --git a/src/main/java/mediathek/tool/javafx/FXErrorDialog.java b/src/main/java/mediathek/tool/javafx/FXErrorDialog.java deleted file mode 100644 index 318d7f324a..0000000000 --- a/src/main/java/mediathek/tool/javafx/FXErrorDialog.java +++ /dev/null @@ -1,79 +0,0 @@ -package mediathek.tool.javafx; - -import javafx.scene.control.Alert; -import javafx.scene.control.Label; -import javafx.scene.control.TextArea; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Priority; -import javafx.stage.Modality; -import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.mainwindow.MediathekGui; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; - -public class FXErrorDialog { - private static GridPane createExceptionPanel(@NotNull Exception ex) throws IOException { - GridPane expContent = new GridPane(); - try (StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw)) { - ex.printStackTrace(pw); - String exceptionText = sw.toString(); - - Label label = new Label("Stacktrace:"); - - TextArea textArea = new TextArea(exceptionText); - textArea.setEditable(false); - textArea.setWrapText(true); - - textArea.setMaxWidth(Double.MAX_VALUE); - textArea.setMaxHeight(Double.MAX_VALUE); - GridPane.setVgrow(textArea, Priority.ALWAYS); - GridPane.setHgrow(textArea, Priority.ALWAYS); - - expContent.setMaxWidth(Double.MAX_VALUE); - expContent.add(label, 0, 0); - expContent.add(textArea, 0, 1); - } - - return expContent; - } - - private static Alert getBaseAlert(@NotNull String title, @NotNull String header, @NotNull String detailedErrorMessage) { - var alert = new Alert(Alert.AlertType.ERROR); - alert.setTitle(title); - alert.setHeaderText(header); - alert.setContentText(detailedErrorMessage); - alert.initModality(Modality.APPLICATION_MODAL); - - return alert; - } - - private static void createExceptionContent(@NotNull Alert alert, @NotNull Exception ex) { - try { - var content = createExceptionPanel(ex); - alert.getDialogPane().setExpandableContent(content); - } - catch (IOException ignored) { - // do not set content - } - } - - public static void showErrorDialog(String title, String header, String detailedErrorMessage, @NotNull Exception ex) { - Alert alert = getBaseAlert(title,header, detailedErrorMessage); - createExceptionContent(alert, ex); - alert.initOwner(JFXHiddenApplication.getPrimaryStage()); - JFXHiddenApplication.showAlert(alert, MediathekGui.ui()); - } - - public static void showErrorDialogWithoutParent(String title, String header, String detailedErrorMessage, @NotNull Exception ex) { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - Alert alert = getBaseAlert(title,header, detailedErrorMessage); - createExceptionContent(alert, ex); - alert.showAndWait(); - }); - } -} diff --git a/src/main/java/mediathek/tool/listener/BeobTableHeader.java b/src/main/java/mediathek/tool/listener/BeobTableHeader.java index daa322f9c1..806be4bb03 100644 --- a/src/main/java/mediathek/tool/listener/BeobTableHeader.java +++ b/src/main/java/mediathek/tool/listener/BeobTableHeader.java @@ -1,7 +1,5 @@ package mediathek.tool.listener; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.MVConfig; import mediathek.tool.table.MVTable; import org.jetbrains.annotations.NotNull; @@ -14,10 +12,6 @@ * Rechte Maustaste in der Tabelle (Kontextmenü) */ public class BeobTableHeader extends MouseAdapter { - /** - * Size factor to increase/decrease the current font size. - */ - private static final float FONT_SIZE_FACTOR = 2f; protected final MVTable tabelle; private final String[] columns; private final boolean[] spaltenAnzeigen; @@ -32,14 +26,7 @@ public class BeobTableHeader extends MouseAdapter { private final boolean displaySenderIconMenus; private final MVConfig.Configs configKey; private JCheckBoxMenuItem[] box; - private JMenuItem miIncreaseFont; - private JMenuItem miDecreaseFont; - /** - * Indicate whether the used table (and cell renderer) is capable of changing font size. - */ - private boolean fontSizeChangeCapable; private JMenuItem miResetColumns; - private JMenuItem miResetFontSize; /** * Context Menu for manipulation of table visual appearance from table header. @@ -75,31 +62,6 @@ private void createStaticMenuEntries() { miResetColumns = new JMenuItem("Spalten zurücksetzen"); miResetColumns.addActionListener(e -> tabelle.resetTabelle()); - miDecreaseFont = new JMenuItem("Schrift verkleinern"); - miDecreaseFont.setIcon(IconFontSwing.buildIcon(FontAwesome.MINUS, 16)); - miDecreaseFont.addActionListener(e -> { - var oldFont = tabelle.getDefaultFont(); - final var oldSize = oldFont.getSize2D(); - final var newSize = oldSize - FONT_SIZE_FACTOR; - tabelle.setDefaultFont(oldFont.deriveFont(newSize)); - tabelle.calculateRowHeight(); - }); - - miIncreaseFont = new JMenuItem("Schrift vergrößern"); - miIncreaseFont.setIcon(IconFontSwing.buildIcon(FontAwesome.PLUS, 16)); - miIncreaseFont.addActionListener(e -> { - var oldFont = tabelle.getDefaultFont(); - final var oldSize = oldFont.getSize2D(); - final var newSize = oldSize + FONT_SIZE_FACTOR; - tabelle.setDefaultFont(oldFont.deriveFont(newSize)); - tabelle.calculateRowHeight(); - }); - - miResetFontSize = new JMenuItem("Schriftgröße zurücksetzen"); - miResetFontSize.addActionListener(e -> { - tabelle.setDefaultFont(UIManager.getDefaults().getFont("Table.font")); - tabelle.calculateRowHeight(); - }); } @Override @@ -197,24 +159,9 @@ protected JPopupMenu prepareMenu() { // Tabellenspalten zurücksetzen jPopupMenu.add(miResetColumns); - if (isFontSizeChangeCapable()) { - jPopupMenu.addSeparator(); - jPopupMenu.add(miIncreaseFont); - jPopupMenu.add(miDecreaseFont); - jPopupMenu.add(miResetFontSize); - } - return jPopupMenu; } - public boolean isFontSizeChangeCapable() { - return fontSizeChangeCapable; - } - - public void setFontSizeChangeCapable(boolean fontSizeChangeCapable) { - this.fontSizeChangeCapable = fontSizeChangeCapable; - } - private void showMenu(MouseEvent evt) { var popupMenu = prepareMenu(); popupMenu.show(evt.getComponent(), evt.getX(), evt.getY()); diff --git a/src/main/java/mediathek/tool/migrator/SettingsMigrator.java b/src/main/java/mediathek/tool/migrator/SettingsMigrator.java index cbb0d324b6..9f9a01a7b1 100644 --- a/src/main/java/mediathek/tool/migrator/SettingsMigrator.java +++ b/src/main/java/mediathek/tool/migrator/SettingsMigrator.java @@ -92,7 +92,7 @@ private void migrateDoNotShowGeoFilms(Element element) { var node = element.getFirstChild(); if (node != null) { boolean result = Boolean.parseBoolean(node.getNodeValue()); - config.setProperty(ApplicationConfiguration.BLACKLIST_DO_NOT_SHOW_GEOBLOCKED_FILMS, result); + ApplicationConfiguration.getInstance().setBlacklistDoNotShowGeoblockedFilms(result); logger.debug("migrateDoNotShowGeoFilms"); } } diff --git a/src/main/java/mediathek/tool/models/TModelFilm.java b/src/main/java/mediathek/tool/models/TModelFilm.java index 5a3b55085d..04d53b5666 100644 --- a/src/main/java/mediathek/tool/models/TModelFilm.java +++ b/src/main/java/mediathek/tool/models/TModelFilm.java @@ -98,11 +98,11 @@ public Object getValueAt(int row, int column) { case DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN -> ""; case DatenFilm.FILM_DATUM -> film.getDatumFilm(); case DatenFilm.FILM_ZEIT -> film.getSendeZeit(); - case DatenFilm.FILM_DAUER -> film.getDuration(); - case DatenFilm.FILM_GROESSE -> film.getFilmSize(); + case DatenFilm.FILM_DAUER -> film.getFilmLength(); + case DatenFilm.FILM_GROESSE -> film.getFileSize(); case DatenFilm.FILM_HD -> film.isHighQuality(); case DatenFilm.FILM_UT -> film.hasSubtitle(); - case DatenFilm.FILM_GEO -> film.getGeo().orElse(""); + case DatenFilm.FILM_GEO -> film.countrySet; case DatenFilm.FILM_URL -> film.getUrlNormalQuality(); case DatenFilm.FILM_REF -> film; default -> throw new IndexOutOfBoundsException("UNKNOWN COLUMN VALUE: " + column); diff --git a/src/main/java/mediathek/tool/sql/SqlDatabaseConfig.kt b/src/main/java/mediathek/tool/sql/SqlDatabaseConfig.kt index a0386e4a1d..e383ce5a21 100644 --- a/src/main/java/mediathek/tool/sql/SqlDatabaseConfig.kt +++ b/src/main/java/mediathek/tool/sql/SqlDatabaseConfig.kt @@ -18,7 +18,7 @@ object SqlDatabaseConfig { val conf = SQLiteConfig() conf.setEncoding(SQLiteConfig.Encoding.UTF8) conf.setLockingMode(SQLiteConfig.LockingMode.NORMAL) - conf.setSharedCache(true) + conf.setSharedCache(false) conf.setSynchronous(SQLiteConfig.SynchronousMode.OFF) conf.enableLoadExtension(false) conf.setJournalMode(SQLiteConfig.JournalMode.WAL) diff --git a/src/main/java/mediathek/tool/swing/SwingUIFontChanger.java b/src/main/java/mediathek/tool/swing/SwingUIFontChanger.java deleted file mode 100644 index 1367318f0d..0000000000 --- a/src/main/java/mediathek/tool/swing/SwingUIFontChanger.java +++ /dev/null @@ -1,65 +0,0 @@ -package mediathek.tool.swing; - -import mediathek.config.Config; -import org.apache.commons.lang3.SystemUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.swing.*; -import java.awt.*; -import java.util.ArrayList; -import java.util.Arrays; - -/** - * Utility class which changes the font size for each swing font key. - * Useful for enhancing the font size of an application. - */ -public class SwingUIFontChanger { - private static final Logger logger = LogManager.getLogger(); - public static final String NIMBUS_DEFAULT_FONT = "defaultFont"; - public static final String WINDOWS_DEFAULT_FONT = "Table.font"; - - public void changeFontSize(float size) { - //do not change size on macOS - if (SystemUtils.IS_OS_MAC_OSX) - return; - - switch (LookAndFeelType.get(UIManager.getLookAndFeel().getClass().getName())) { - case Windows -> changeWindowsFontSize(size); - case Nimbus -> changeNimbusFontSize(size); - default -> logger.error("Unknown Look&Feel, cannot change font size"); - } - } - - private void changeNimbusFontSize(float size) { - var defaults = UIManager.getLookAndFeel().getDefaults(); - Font font = (Font) defaults.get(NIMBUS_DEFAULT_FONT); - font = font.deriveFont(size); - logger.info("Changing Nimbus font size"); - defaults.put("defaultFont", font); - } - - private void changeWindowsFontSize(float size) { - var keyList = new ArrayList(); - var defaults = UIManager.getDefaults(); - var keys = defaults.keys(); - while (keys.hasMoreElements()) { - if (keys.nextElement() instanceof String str_key) { - if (str_key.endsWith(".font")) - keyList.add(str_key); - } - } - - var ui_keys = keyList.stream().distinct().sorted().toArray(String[]::new); - - if (Config.isDebugModeEnabled()) - Arrays.stream(ui_keys).forEach(logger::debug); - - logger.info("Changing Windows font sizes to {}", size); - Arrays.stream(ui_keys).forEach(key -> { - var font = defaults.getFont(key); - font = font.deriveFont(size); - UIManager.put(key, font); - }); - } -} diff --git a/src/main/java/mediathek/tool/table/MVDownloadsTable.java b/src/main/java/mediathek/tool/table/MVDownloadsTable.java index 44427fd7c0..58988c0004 100644 --- a/src/main/java/mediathek/tool/table/MVDownloadsTable.java +++ b/src/main/java/mediathek/tool/table/MVDownloadsTable.java @@ -14,6 +14,7 @@ import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.dnd.DragSource; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Optional; @@ -29,6 +30,38 @@ public MVDownloadsTable() { setModel(new TModelDownload()); } + @Override public String getToolTipText(MouseEvent e) { + var p = e.getPoint(); // MouseEvent + final int viewColumn = columnAtPoint(p); + final int modelColumnIndex = convertColumnIndexToModel(viewColumn); + + //only show title as tooltip for TITEL column... + if (modelColumnIndex != DatenDownload.DOWNLOAD_TITEL) + return super.getToolTipText(e); + + String toolTipText = null; + final int viewRow = rowAtPoint(p); + var comp = prepareRenderer(getCellRenderer(viewRow, viewColumn), viewRow, viewColumn); + var bounds = getCellRect(viewRow, viewColumn, false); + + + try { + //comment row, exclude heading + if (comp.getPreferredSize().width > bounds.width) { + final int modelRowIndex = convertRowIndexToModel(viewRow); + final DatenDownload datenDownload = (DatenDownload) getModel().getValueAt(modelRowIndex, DatenDownload.DOWNLOAD_REF); + if (datenDownload.film != null) + toolTipText = datenDownload.film.getTitle(); + else + toolTipText = ""; + } + } catch (RuntimeException ignored) { + //catch null pointer exception if mouse is over an empty line + } + + return toolTipText; + } + private void setupDragnDrop() { setDragEnabled(true); setDropMode(DropMode.INSERT_ROWS); diff --git a/src/main/java/mediathek/tool/table/MVFilmTable.java b/src/main/java/mediathek/tool/table/MVFilmTable.java index a892c94120..348932ad78 100644 --- a/src/main/java/mediathek/tool/table/MVFilmTable.java +++ b/src/main/java/mediathek/tool/table/MVFilmTable.java @@ -3,17 +3,14 @@ import mediathek.config.MVConfig; import mediathek.daten.DatenFilm; import mediathek.gui.tabs.tab_film.GuiFilme; -import mediathek.tool.ApplicationConfiguration; import mediathek.tool.FilmSize; -import mediathek.tool.models.TModelFilm; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; -import java.awt.*; -import java.util.ArrayList; +import java.awt.event.MouseEvent; import java.util.Comparator; import java.util.List; import java.util.Optional; @@ -21,10 +18,37 @@ public class MVFilmTable extends MVTable { private static final Logger logger = LogManager.getLogger(); private MyRowSorter sorter; - /** - * Stores the selected FILM NUMBERS that need to be restored after updates. - */ - private final List selectedFilmNumbers = new ArrayList<>(); + + @Override public String getToolTipText(MouseEvent e) { + var p = e.getPoint(); // MouseEvent + final int viewColumn = columnAtPoint(p); + final int modelColumnIndex = convertColumnIndexToModel(viewColumn); + + //only show title as tooltip for TITEL column... + if (modelColumnIndex != DatenFilm.FILM_TITEL) + return super.getToolTipText(e); + + String toolTipText = null; + final int viewRow = rowAtPoint(p); + var comp = prepareRenderer(getCellRenderer(viewRow, viewColumn), viewRow, viewColumn); + var bounds = getCellRect(viewRow, viewColumn, false); + + + try { + //comment row, exclude heading + if (comp.getPreferredSize().width > bounds.width) { + final int modelRowIndex = convertRowIndexToModel(viewRow); + final DatenFilm datenFilm = (DatenFilm) getModel().getValueAt(modelRowIndex, DatenFilm.FILM_REF); + + toolTipText = datenFilm.getTitle(); + } + } catch (RuntimeException ignored) { + //catch null pointer exception if mouse is over an empty line + } + + return toolTipText; + } + public MVFilmTable() { super(DatenFilm.MAX_ELEM, GuiFilme.VISIBLE_COLUMNS, @@ -33,36 +57,19 @@ public MVFilmTable() { Optional.of(MVConfig.Configs.SYSTEM_EIGENSCHAFTEN_TABELLE_FILME)); setAutoCreateRowSorter(false); - addPropertyChangeListener("model", evt -> { - //System.out.println("TABLE MODEL CHANGED"); + //we need to setup sorter later as the model is invalid at ctor point... + var model = (TableModel) evt.getNewValue(); if (sorter == null) { - sorter = new MyRowSorter<>(getModel()); - //sorter.addRowSorterListener(evt1 -> System.out.println("SORT ORDER HAS CHANGED")); + sorter = new MyRowSorter<>(model); + sorter.setModel(model); + setRowSorter(sorter); } - setRowSorter(sorter); - sorter.setModel(getModel()); + else + sorter.setModel(model); }); } - @Override - protected void loadDefaultFontSize() { - var config = ApplicationConfiguration.getConfiguration(); - try { - final var fontSize = config.getFloat(ApplicationConfiguration.TAB_FILM_FONT_SIZE); - var newFont = getDefaultFont().deriveFont(fontSize); - setDefaultFont(newFont); - } - catch (Exception ignored) {} - } - - @Override - protected void saveDefaultFontSize() { - var config = ApplicationConfiguration.getConfiguration(); - final var fontSize = getDefaultFont().getSize2D(); - config.setProperty(ApplicationConfiguration.TAB_FILM_FONT_SIZE, fontSize); - } - private void resetFilmeTab(int i) { //logger.debug("resetFilmeTab()"); @@ -172,8 +179,6 @@ public void setSpalten() { } static class MyRowSorter extends TableRowSorter { - //private static final Logger rsLogger = LogManager.getLogger(); - public MyRowSorter(M model) { super(model); } @@ -181,6 +186,8 @@ public MyRowSorter(M model) { @Override public void setModel(M model) { super.setModel(model); + + //must be set after each model change // do not sort buttons setSortable(DatenFilm.FILM_ABSPIELEN, false); setSortable(DatenFilm.FILM_AUFZEICHNEN, false); @@ -190,98 +197,18 @@ public void setModel(M model) { setComparator(DatenFilm.FILM_SENDER, (Comparator) String::compareTo); setComparator(DatenFilm.FILM_ZEIT, (Comparator) String::compareTo); setComparator(DatenFilm.FILM_URL, (Comparator) String::compareTo); + setComparator(DatenFilm.FILM_DAUER, Comparator.naturalOrder()); } @Override public void setSortKeys(List sortKeys) { - //FIXME something is wrong in MVTable with setting sort keys + // MV config stores only ONE sort key + // here we make sure that only one will be set on the table... if (sortKeys != null) { - if (sortKeys.size() > 1) { - //rsLogger.error("BULLSHIT SORTKEYS IN"); + while (sortKeys.size() > 1) sortKeys.remove(1); - } } super.setSortKeys(sortKeys); -/* - var list = getSortKeys(); - if (list != null) { - rsLogger.debug("SORT KEYS:"); - for (var key : list) { - rsLogger.debug("COLUMN: " + key.getColumn() + ",SortOrder: " + key.getSortOrder().toString()); - } - } -*/ - } - } - - /** - * Scroll to a selected row, but center it in the middle of the visible rectangle. - * @param rowIndex The row to be displayed centered. - */ - private void scrollToSelectionCentered(int rowIndex) { - Rectangle rect = getCellRect(rowIndex, 0, true); - Rectangle viewRect = getVisibleRect(); - rect.setLocation(rect.x - viewRect.x, rect.y - viewRect.y); - - int centerX = (viewRect.width - rect.width) / 2; - int centerY = (viewRect.height - rect.height) / 2; - if (rect.x < centerX) { - centerX = -centerX; - } - if (rect.y < centerY) { - centerY = -centerY; - } - rect.translate(centerX, centerY); - scrollRectToVisible(rect); - } - - @Override - protected void scrollToIndexDelegate(int index) { - // film list moves selected entries into center of JTable view - scrollToSelectionCentered(index); - } - @Override - protected void restoreSelectedTableRows() { - if (!selectedFilmNumbers.isEmpty()) { - boolean found = false; - - selectionModel.setValueIsAdjusting(true); - final var tModel = (TModelFilm) getModel(); - for (var i : selectedFilmNumbers) { - int r = tModel.getModelRowForFilmNumber(i); - if (r >= 0) { - // ansonsten gibts die Zeile nicht mehr - r = convertRowIndexToView(r); - addRowSelectionInterval(r, r); - found = true; - } - } - if (!found && selRow >= 0 && getRowCount() > selRow) { - setRowSelectionInterval(selRow,selRow); - } else if (!found && selRow >= 0 && getRowCount() > 0) { - final var rowCount = tModel.getRowCount() - 1; - setRowSelectionInterval(rowCount, rowCount); - } - selectionModel.setValueIsAdjusting(false); - } - - selectedFilmNumbers.clear(); - } - - @Override - public void saveSelectedTableRows() { - super.saveSelectedTableRows(); - - int selIndex = -1; - if (selRow >= 0) { - selIndex = (int) getModel().getValueAt(convertRowIndexToModel(selRow), DatenFilm.FILM_NR); - } - - selectedFilmNumbers.clear(); - if (selIndex >= 0) { - for (int i : selRows) { - selectedFilmNumbers.add((int) getModel().getValueAt(convertRowIndexToModel(i), DatenFilm.FILM_NR)); - } } } } diff --git a/src/main/java/mediathek/tool/table/MVTable.java b/src/main/java/mediathek/tool/table/MVTable.java index 9012e60efc..973dd086fc 100644 --- a/src/main/java/mediathek/tool/table/MVTable.java +++ b/src/main/java/mediathek/tool/table/MVTable.java @@ -12,7 +12,6 @@ import javax.swing.RowSorter.SortKey; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumnModel; -import java.awt.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -27,17 +26,12 @@ public abstract class MVTable extends JTable { protected final int[] reihe; public boolean useSmallSenderIcons; protected final int maxSpalten; - protected int[] selRows; - protected int selRow = -1; + private int[] selRows = {}; protected final boolean[] spaltenAnzeigen; protected final Optional columnConfigurationDataConfigKey; protected final Optional showIconsConfigKey; protected final Optional smallSenderIconConfigKey; protected List listeSortKeys; - /** - * This is the UI provided default font used for calculating the size area - */ - private Font defaultFont = UIManager.getDefaults().getFont("Table.font"); private boolean showSenderIcon; private boolean lineBreak = true; @@ -56,9 +50,6 @@ public MVTable(int maxColumns, boolean @NotNull [] visibleColumStore, setAutoCreateRowSorter(true); setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - getTableHeader().addMouseListener(new WidthAdjuster(this)); - - breite = new int[maxSpalten]; Arrays.fill(breite,-1); @@ -68,7 +59,6 @@ public MVTable(int maxColumns, boolean @NotNull [] visibleColumStore, showIconsConfigKey.ifPresent( key -> showSenderIcon = Boolean.parseBoolean(MVConfig.get(key))); smallSenderIconConfigKey.ifPresent(key -> useSmallSenderIcons = Boolean.parseBoolean(MVConfig.get(key))); - loadDefaultFontSize(); calculateRowHeight(); applyWindowsSevenTableEffects(); @@ -85,27 +75,6 @@ private void applyWindowsSevenTableEffects() { } } - /** - * Load font size from settings and replace default font. - */ - protected void loadDefaultFontSize() { - //unused here - } - - /** - * Store default font size in settings - */ - protected void saveDefaultFontSize() { - //unused here - } - - public Font getDefaultFont() { return defaultFont;} - - public void setDefaultFont(Font newFont) { - defaultFont = newFont; - saveDefaultFontSize(); - } - private SortKey sortKeyLesen(String s, String strSortOrder) { SortKey sk; @@ -154,7 +123,7 @@ public void invertSelection() { */ private int getSizeArea() { final int sizeArea; - var fm = getFontMetrics(defaultFont); + var fm = getFontMetrics(getFont()); final var height = fm.getHeight(); if (lineBreak) { @@ -172,7 +141,7 @@ private int getSizeArea() { */ public void calculateRowHeight() { var sizeArea = getSizeArea(); - var fm = getFontMetrics(defaultFont); + var fm = getFontMetrics(getFont()); var height = fm.getHeight() + 5; // add some extra spacing for the height @@ -287,15 +256,19 @@ protected void scrollToIndexDelegate(int index) { scrollRectToVisible(getCellRect(index, 0, true)); } - public void saveSelectedTableRows() { + protected void saveSelectedTableRows() { // Einstellungen der Tabelle merken - selRow = getSelectedRow(); selRows = getSelectedRows(); } protected void restoreSelectedTableRows() { - if (selRows != null) { - if (selRows.length > 0) { + if (selRows.length > 0) { + final int visibleRow; + if (selRows.length == 1) { + final var selectedRow = selRows[0]; + selectionModel.setSelectionInterval(selectedRow, selectedRow); + visibleRow = selectedRow; + } else { selectionModel.setValueIsAdjusting(true); for (int selectedRow : selRows) { if (selectedRow < getRowCount()) { @@ -303,7 +276,10 @@ protected void restoreSelectedTableRows() { } } selectionModel.setValueIsAdjusting(false); + visibleRow = selRows[0]; } + scrollToIndexDelegate(visibleRow); + requestFocus(); } } @@ -411,7 +387,8 @@ public void resetTabelle() { * @return the configuration data as string. */ private String prepareTableConfigurationData() { - String b, r; + StringBuilder b; + StringBuilder r; int[] reihe_ = new int[maxSpalten]; int[] breite_ = new int[maxSpalten]; for (int i = 0; i < reihe_.length && i < getModel().getColumnCount(); ++i) { @@ -423,11 +400,11 @@ private String prepareTableConfigurationData() { breite_[i] = model.getColumn(convertColumnIndexToView(i)).getWidth(); } - b = Integer.toString(breite_[0]); - r = Integer.toString(reihe_[0]); + b = new StringBuilder(Integer.toString(breite_[0])); + r = new StringBuilder(Integer.toString(reihe_[0])); for (int i = 1; i < breite.length; i++) { - b = b + ',' + breite_[i]; - r = r + ',' + reihe_[i]; + b.append(',').append(breite_[i]); + r.append(',').append(reihe_[i]); } listeSortKeys = this.getRowSorter().getSortKeys(); diff --git a/src/main/java/mediathek/tool/table/WidthAdjuster.java b/src/main/java/mediathek/tool/table/WidthAdjuster.java deleted file mode 100644 index be5807e6c6..0000000000 --- a/src/main/java/mediathek/tool/table/WidthAdjuster.java +++ /dev/null @@ -1,80 +0,0 @@ -package mediathek.tool.table; - -import javax.swing.*; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.List; - -class WidthAdjuster extends MouseAdapter { - - private static final int EPSILON = 5; //boundary sensitivity - private final JTable table; - private List listeSortKeys = null; - - protected WidthAdjuster(final JTable table) { - this.table = table; - } - - @Override - public void mousePressed(final MouseEvent evt) { - if (evt.getClickCount() == 1) { - if (table.getRowSorter() != null) { - listeSortKeys = table.getRowSorter().getSortKeys(); - } else { - listeSortKeys = null; - } - } - if (evt.getClickCount() > 1 && isUsingResizeCursor()) { - resizeColumn(getLeftColumn(evt.getPoint())); - } - } - - private boolean isUsingResizeCursor() { - final Cursor cursor = table.getTableHeader().getCursor(); - return cursor.equals(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)) - || cursor.equals(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); - } - - //if near the boundary, will choose left column - private int getLeftColumn(final Point pt) { - pt.x -= EPSILON; - return table.getTableHeader().columnAtPoint(pt); - } - - private void resizeColumn(final int col) { - final TableColumn tc = table.getColumnModel().getColumn(col); - TableCellRenderer tcr = tc.getHeaderRenderer(); - - if (tcr == null) { - tcr = table.getTableHeader().getDefaultRenderer(); - } - - Object obj = tc.getHeaderValue(); - Component comp = tcr.getTableCellRendererComponent(table, obj, false, false, 0, 0); - int maxWidth = comp.getPreferredSize().width; - - for (int i = 0, ub = table.getRowCount(); i != ub; ++i) { - tcr = table.getCellRenderer(i, col); - obj = table.getValueAt(i, col); - comp = tcr.getTableCellRendererComponent(table, obj, false, false, i, col); - final int w = comp.getPreferredSize().width; - if (w > maxWidth) { - maxWidth = w; - } - } - - maxWidth += 10; //and room to grow... - tc.setPreferredWidth(maxWidth); //remembers the value - tc.setWidth(maxWidth); //forces layout, repaint - - if (listeSortKeys != null) { - if (!listeSortKeys.isEmpty()) { - table.getRowSorter().setSortKeys(listeSortKeys); - } - } - } - -} diff --git a/src/main/java/mediathek/update/DialogHinweisUpdate.java b/src/main/java/mediathek/update/DialogHinweisUpdate.java index f35f7cc925..64c1b1b502 100644 --- a/src/main/java/mediathek/update/DialogHinweisUpdate.java +++ b/src/main/java/mediathek/update/DialogHinweisUpdate.java @@ -19,28 +19,24 @@ */ package mediathek.update; -import javafx.application.Platform; -import javafx.embed.swing.JFXPanel; -import javafx.geometry.Insets; -import javafx.scene.Scene; -import javafx.scene.control.Hyperlink; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.CornerRadii; -import javafx.scene.paint.Color; import mediathek.config.Konstanten; import mediathek.gui.actions.UrlHyperlinkAction; import mediathek.tool.EscapeKeyHandler; +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jdesktop.swingx.JXHyperlink; import javax.swing.*; +import java.awt.*; import java.net.URISyntaxException; -@SuppressWarnings("serial") public class DialogHinweisUpdate extends JDialog { - private static final Logger logger = LogManager.getLogger(DialogHinweisUpdate.class); + private static final Logger logger = LogManager.getLogger(); public DialogHinweisUpdate(JFrame parent, String ttext) { super(parent, true); @@ -52,20 +48,15 @@ public DialogHinweisUpdate(JFrame parent, String ttext) { jButtonOk.addActionListener(e -> dispose()); jTextArea1.setText(ttext); - Platform.runLater(() -> { - Hyperlink link = new Hyperlink("Link zur Website"); - link.setBackground(new Background(new BackgroundFill(Color.rgb(236,236,236), CornerRadii.EMPTY, Insets.EMPTY))); - link.setOnAction(e -> SwingUtilities.invokeLater(() -> { - try { - UrlHyperlinkAction.openURL(parent, Konstanten.ADRESSE_DOWNLOAD); - } catch (URISyntaxException ex) { - logger.error(ex); - } - })); - hyperLinkPanel.setScene(new Scene(link)); + hyperLink.addActionListener(l -> { + try { + UrlHyperlinkAction.openURL(parent, Konstanten.ADRESSE_DOWNLOAD); + } catch (URISyntaxException ex) { + logger.error(ex); + } }); - - setSize(450,getHeight()); + //setSize(450,getHeight()); + pack(); } /** This method is called from within the constructor to @@ -79,12 +70,23 @@ private void initComponents() { var jScrollPane1 = new JScrollPane(); jTextArea1 = new JTextArea(); jButtonOk = new JButton(); - hyperLinkPanel = new JFXPanel(); + hyperLink = new JXHyperlink(); //======== this ======== setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); setTitle("Programminformationen"); //NON-NLS + setMinimumSize(new Dimension(480, 100)); var contentPane = getContentPane(); + contentPane.setLayout(new MigLayout( + new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .grow().fill(), + // rows + new AC() + .grow().fill().gap() + .fill().gap() + .fill())); //======== jScrollPane1 ======== { @@ -96,39 +98,18 @@ private void initComponents() { jTextArea1.setRows(5); jTextArea1.setText("\n\n"); //NON-NLS jTextArea1.setWrapStyleWord(true); + jTextArea1.setMinimumSize(new Dimension(459, 85)); jScrollPane1.setViewportView(jTextArea1); } + contentPane.add(jScrollPane1, new CC().cell(0, 0)); //---- jButtonOk ---- jButtonOk.setText("Schlie\u00dfen"); //NON-NLS + contentPane.add(jButtonOk, new CC().cell(0, 2).alignX("right").growX(0)); //NON-NLS - GroupLayout contentPaneLayout = new GroupLayout(contentPane); - contentPane.setLayout(contentPaneLayout); - contentPaneLayout.setHorizontalGroup( - contentPaneLayout.createParallelGroup() - .addGroup(GroupLayout.Alignment.TRAILING, contentPaneLayout.createSequentialGroup() - .addGroup(contentPaneLayout.createParallelGroup(GroupLayout.Alignment.TRAILING) - .addComponent(hyperLinkPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(contentPaneLayout.createSequentialGroup() - .addContainerGap() - .addGroup(contentPaneLayout.createParallelGroup(GroupLayout.Alignment.TRAILING) - .addGroup(contentPaneLayout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) - .addComponent(jButtonOk)) - .addComponent(jScrollPane1, GroupLayout.Alignment.LEADING)))) - .addGap(5, 5, 5)) - ); - contentPaneLayout.setVerticalGroup( - contentPaneLayout.createParallelGroup() - .addGroup(GroupLayout.Alignment.TRAILING, contentPaneLayout.createSequentialGroup() - .addContainerGap() - .addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 208, Short.MAX_VALUE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(hyperLinkPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addGap(2, 2, 2) - .addComponent(jButtonOk) - .addContainerGap()) - ); + //---- hyperLink ---- + hyperLink.setText("Link zur Webseite"); //NON-NLS + contentPane.add(hyperLink, new CC().cell(0, 1)); pack(); setLocationRelativeTo(getOwner()); }//
//GEN-END:initComponents @@ -136,6 +117,6 @@ private void initComponents() { // Generated using JFormDesigner non-commercial license private JTextArea jTextArea1; private JButton jButtonOk; - private JFXPanel hyperLinkPanel; + private JXHyperlink hyperLink; // End of variables declaration//GEN-END:variables } diff --git a/src/main/java/mediathek/update/DialogHinweisUpdate.jfd b/src/main/java/mediathek/update/DialogHinweisUpdate.jfd index 1d6370e226..0629419d1a 100644 --- a/src/main/java/mediathek/update/DialogHinweisUpdate.jfd +++ b/src/main/java/mediathek/update/DialogHinweisUpdate.jfd @@ -1,15 +1,17 @@ -JFDML JFormDesigner: "7.0.0.0.142" Java: "1.8.0_152-release" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.5.1.409" Java: "11.0.15" encoding: "UTF-8" new FormModel { contentType: "form/swing" root: new FormRoot { - add( new FormWindow( "javax.swing.JDialog", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { - "$horizontalGroup": "par l {seq t {par t {comp hyperLinkPanel:::::x, seq {space :::p, par t {seq t {space :0:0:x, comp jButtonOk:::p::p}, comp jScrollPane1::l:::x}}}, space :p:5:p}}" - "$verticalGroup": "par l {seq t {space :p::p, comp jScrollPane1::::208:x, space :::p, comp hyperLinkPanel:::p::p, space :p:2:p, comp jButtonOk:::p::p, space :::p}}" + add( new FormWindow( "javax.swing.JDialog", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 5,hidemode 3,gap 5 5" + "$columnConstraints": "[grow,fill]" + "$rowConstraints": "[grow,fill][fill][fill]" } ) { name: "this" "defaultCloseOperation": 2 "title": "Programminformationen" + "minimumSize": new java.awt.Dimension( 480, 100 ) add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { name: "jScrollPane1" auxiliary() { @@ -23,14 +25,22 @@ new FormModel { "rows": 5 "text": "\n\n" "wrapStyleWord": true + "minimumSize": new java.awt.Dimension( 459, 85 ) } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" } ) add( new FormComponent( "javax.swing.JButton" ) { name: "jButtonOk" "text": "Schließen" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2,alignx right,growx 0" } ) - add( new FormComponent( "javafx.embed.swing.JFXPanel" ) { - name: "hyperLinkPanel" + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperLink" + "text": "Link zur Webseite" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" } ) }, new FormLayoutConstraints( null ) { "size": new java.awt.Dimension( 475, 280 ) diff --git a/src/main/java/mediathek/update/ProgramUpdateCheck.java b/src/main/java/mediathek/update/ProgramUpdateCheck.java index f558ccaa7c..c1f94475f3 100644 --- a/src/main/java/mediathek/update/ProgramUpdateCheck.java +++ b/src/main/java/mediathek/update/ProgramUpdateCheck.java @@ -1,13 +1,13 @@ package mediathek.update; import mediathek.config.Daten; -import mediathek.config.Konstanten; import mediathek.config.MVConfig; import mediathek.daten.DatenPset; import mediathek.daten.ListePset; import mediathek.daten.ListePsetVorlagen; import mediathek.gui.dialog.DialogNewSet; import mediathek.mainwindow.MediathekGui; +import mediathek.tool.GuiFunktionen; import mediathek.tool.GuiFunktionenProgramme; import mediathek.tool.NetUtils; import mediathek.tool.TimerPool; @@ -113,13 +113,11 @@ private void performUpdateCheck() { //we have internet... SwingUtilities.invokeLater(() -> gui.enableUpdateMenuItem(false)); - var externalUpdateCheck = System.getProperty(Konstanten.EXTERNAL_UPDATE_PROPERTY); - if (externalUpdateCheck == null || !externalUpdateCheck.equalsIgnoreCase("true")) { + if (GuiFunktionen.isNotUsingExternalUpdater()) { searchForProgramUpdate(); } - else { + else logger.info("External Update Mechanism in use -> skip program update check"); - } checkForPsetUpdates(); } else diff --git a/src/main/java/mediathek/update/ProgrammUpdateSuchen.java b/src/main/java/mediathek/update/ProgrammUpdateSuchen.java index e501926a5b..dac794b2d2 100644 --- a/src/main/java/mediathek/update/ProgrammUpdateSuchen.java +++ b/src/main/java/mediathek/update/ProgrammUpdateSuchen.java @@ -1,12 +1,11 @@ package mediathek.update; -import javafx.application.Platform; import mediathek.config.Konstanten; import mediathek.config.MVConfig; import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SwingErrorDialog; import mediathek.tool.Version; import mediathek.tool.http.MVHttpClient; -import mediathek.tool.javafx.FXErrorDialog; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; @@ -49,8 +48,8 @@ public void checkVersion(boolean showAlert, boolean showProgramInformation, bool showProgramInformation(showAllInformation, silent); if (remoteProgramInfo.getVersion().isInvalid()) { - Platform.runLater(() -> FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, UPDATE_SEARCH_TITLE, - UPDATE_ERROR_MESSAGE, new RuntimeException(PI_VERSION_INVALID_MSG))); + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), + UPDATE_ERROR_MESSAGE, new RuntimeException(PI_VERSION_INVALID_MSG)); logger.warn(PI_VERSION_INVALID_MSG); } else { if (Konstanten.MVVERSION.isOlderThan(remoteProgramInfo.getVersion())) { @@ -63,8 +62,8 @@ public void checkVersion(boolean showAlert, boolean showProgramInformation, bool }); }, () -> { logger.warn(SPI_RECEPTION_ERROR_MSG); - Platform.runLater(() -> FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, - UPDATE_SEARCH_TITLE, UPDATE_ERROR_MESSAGE, new RuntimeException(SPI_RECEPTION_ERROR_MSG))); + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), + UPDATE_ERROR_MESSAGE, new RuntimeException(SPI_RECEPTION_ERROR_MSG)); }); } @@ -96,7 +95,6 @@ private void displayInfoMessages(boolean showAll, boolean silent) { } } if (!text.isEmpty()) { - //TODO add new dialog with web view here! JDialog dlg = new DialogHinweisUpdate(null, text.toString()); dlg.setVisible(true); MVConfig.add(MVConfig.Configs.SYSTEM_HINWEIS_NR_ANGEZEIGT, Integer.toString(index)); diff --git a/src/main/java/mediathek/x11/MediathekGuiX11.java b/src/main/java/mediathek/x11/MediathekGuiX11.java index 18c813a624..84655e293e 100644 --- a/src/main/java/mediathek/x11/MediathekGuiX11.java +++ b/src/main/java/mediathek/x11/MediathekGuiX11.java @@ -2,7 +2,6 @@ import mediathek.config.Konstanten; import mediathek.config.MVConfig; -import mediathek.gui.actions.ChangeGlobalFontSetting; import mediathek.mainwindow.MediathekGui; import mediathek.tool.notification.GenericNotificationCenter; import mediathek.tool.notification.INotificationCenter; @@ -36,14 +35,6 @@ private void setupX11WindowManagerClassName() { } } - @Override - protected void installChangeGlobalFontSettingMenuEntry() { - jMenuHilfe.addSeparator(); - var action = new ChangeGlobalFontSetting(); - var item = jMenuHilfe.add(action); - action.setMenuItem(item); - } - @Override protected INotificationCenter getNotificationCenter() { var notificationCenter = new LinuxNotificationCenter(); diff --git a/src/main/kotlin/mediathek/SplashScreen.kt b/src/main/kotlin/mediathek/SplashScreen.kt new file mode 100644 index 0000000000..4ce4fdd9f9 --- /dev/null +++ b/src/main/kotlin/mediathek/SplashScreen.kt @@ -0,0 +1,163 @@ +package mediathek + +import mediathek.config.Konstanten +import mediathek.tool.TimerPool.timerPool +import mediathek.tool.UIProgressState +import org.apache.commons.lang3.SystemUtils +import java.awt.Color +import java.awt.Cursor +import java.awt.Dimension +import java.awt.Font +import java.util.* +import java.util.concurrent.TimeUnit +import javax.swing.* +import kotlin.math.roundToInt + +class SplashScreen : JWindow() { + private var versionLabel: JLabel? = null + private var curSteps = 0.0 + private var appTitleLabel: JLabel? = null + private var imageLabel: JLabel? = null + private var progressBar: JProgressBar? = null + private var statusLabel: JLabel? = null + + init { + initComponents() + contentPane.background = Color.BLACK + val res = String.format("Version: %s (%s %s)", Konstanten.MVVERSION, osName, SystemUtils.OS_ARCH) + versionLabel!!.text = res + progressBar!!.value = 0 + setLocationRelativeTo(null) + + //strange behaviour on win where window will not come to front or stay there... + if (SystemUtils.IS_OS_WINDOWS) { + if (isAlwaysOnTopSupported) isAlwaysOnTop = true + } + } + + fun update(state: UIProgressState) { + curSteps++ + val pct = (100 * (curSteps / MAXIMUM_STEPS)).roundToInt() + updateStatus(state.toString(), pct) + } + + fun close() { + timerPool.schedule({ SwingUtilities.invokeLater { isVisible = false } }, 2, TimeUnit.SECONDS) + Main.splashScreen = Optional.empty() + } + + /** + * Return "modern" macOS string for mac instead of legacy "Mac OS X". + * According to apple dev docs even "old" 10.6 is now named macOS. + * + * @return "macOS" for mac otherwise the java OS name + */ + private val osName: String + get() { + return if (SystemUtils.IS_OS_MAC_OSX) "macOS" else SystemUtils.OS_NAME + } + + /** + * Updates the percent complete bar and the associated status text. + * + * @param statusText The new status text to display. + * @param percentComplete The new percentage. + */ + private fun updateStatus(statusText: String?, percentComplete: Int) { + appTitleLabel!!.paintImmediately(0, 0, appTitleLabel!!.width, appTitleLabel!!.height) + imageLabel!!.paintImmediately(0, 0, imageLabel!!.width, imageLabel!!.height) + versionLabel!!.paintImmediately(0, 0, versionLabel!!.width, versionLabel!!.height) + statusLabel!!.text = statusText + statusLabel!!.paintImmediately(0, 0, statusLabel!!.width, statusLabel!!.height) + progressBar!!.value = percentComplete + progressBar!!.paintImmediately(0, 0, progressBar!!.width, progressBar!!.height) + } + + private fun initComponents() { + appTitleLabel = JLabel() + versionLabel = JLabel() + imageLabel = JLabel() + progressBar = JProgressBar() + statusLabel = JLabel() + minimumSize = Dimension(640, 480) + background = Color.black + cursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR) + isAutoRequestFocus = false + foreground = Color.black + val contentPane = contentPane + appTitleLabel!!.text = Konstanten.PROGRAMMNAME + appTitleLabel!!.font = appTitleLabel!!.font.deriveFont( + appTitleLabel!!.font.style or Font.BOLD, + appTitleLabel!!.font.size + 45f + ) + appTitleLabel!!.foreground = Color.white + appTitleLabel!!.background = Color.black + appTitleLabel!!.isOpaque = true + versionLabel!!.text = "Version" + versionLabel!!.isOpaque = true + versionLabel!!.foreground = Color.white + versionLabel!!.background = Color.black + imageLabel!!.icon = ImageIcon(javaClass.getResource("/mediathek/res/MediathekView.png")) + imageLabel!!.background = Color.black + imageLabel!!.isOpaque = true + progressBar!!.value = 50 + progressBar!!.preferredSize = Dimension(146, 10) + statusLabel!!.text = "Status Text Message is here" + statusLabel!!.foreground = Color.white + statusLabel!!.background = Color.black + statusLabel!!.isOpaque = true + val contentPaneLayout = GroupLayout(contentPane) + contentPane.layout = contentPaneLayout + contentPaneLayout.setHorizontalGroup( + contentPaneLayout.createParallelGroup() + .addGroup( + contentPaneLayout.createSequentialGroup() + .addContainerGap() + .addGroup( + contentPaneLayout.createParallelGroup() + .addComponent(versionLabel, GroupLayout.DEFAULT_SIZE, 628, Short.MAX_VALUE.toInt()) + .addComponent(progressBar, GroupLayout.DEFAULT_SIZE, 628, Short.MAX_VALUE.toInt()) + .addGroup( + contentPaneLayout.createSequentialGroup() + .addComponent(appTitleLabel) + .addGap(0, 257, Short.MAX_VALUE.toInt()) + ) + .addComponent(statusLabel, GroupLayout.DEFAULT_SIZE, 628, Short.MAX_VALUE.toInt()) + .addGroup( + GroupLayout.Alignment.TRAILING, contentPaneLayout.createSequentialGroup() + .addGap(0, 372, Short.MAX_VALUE.toInt()) + .addComponent(imageLabel) + ) + ) + .addContainerGap() + ) + ) + contentPaneLayout.setVerticalGroup( + contentPaneLayout.createParallelGroup() + .addGroup( + contentPaneLayout.createSequentialGroup() + .addContainerGap() + .addComponent(appTitleLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(versionLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 94, Int.MAX_VALUE) + .addComponent(imageLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(statusLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent( + progressBar, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + GroupLayout.PREFERRED_SIZE + ) + .addContainerGap() + ) + ) + pack() + } + + companion object { + private val MAXIMUM_STEPS = EnumSet.allOf(UIProgressState::class.java).size - 1.0 + } +} \ No newline at end of file diff --git a/src/main/kotlin/mediathek/gui/actions/import_actions/ImportOldAbosAction.kt b/src/main/kotlin/mediathek/gui/actions/import_actions/ImportOldAbosAction.kt new file mode 100644 index 0000000000..ac0a81a3ac --- /dev/null +++ b/src/main/kotlin/mediathek/gui/actions/import_actions/ImportOldAbosAction.kt @@ -0,0 +1,41 @@ +package mediathek.gui.actions.import_actions + +import mediathek.config.Konstanten +import mediathek.mainwindow.MediathekGui +import mediathek.tool.FileDialogs.Companion.chooseLoadFileLocation +import mediathek.tool.SwingErrorDialog +import java.awt.event.ActionEvent +import javax.swing.AbstractAction +import javax.swing.JOptionPane + +class ImportOldAbosAction : AbstractAction() { + init { + putValue(NAME, "Alte Abos...") + putValue(SHORT_DESCRIPTION, "Ermöglicht den Import der Abos aus einer alten Konfigurationsdatei.") + } + + override fun actionPerformed(e: ActionEvent) { + val selectedFile = chooseLoadFileLocation(MediathekGui.ui(), " Konfigurationsdatei öffnen", "") + if (selectedFile != null) { + try { + val configReader = OldConfigFileImporter() + val (foundAbos) = configReader.importAboBlacklist(selectedFile.absolutePath, + importAbo = true, + importBlacklist = false, + importReplaceList = false) + val text = "Es wurden $foundAbos Einträge importiert." + JOptionPane.showMessageDialog(MediathekGui.ui(), text, Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE) + } + catch (ex: Exception) { + val text = """ + Es trat ein Fehler beim Import der Abos auf. + Sollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam. + """.trimIndent() + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), text, ex) + } + } else { + JOptionPane.showMessageDialog(MediathekGui.ui(), "Der Import wurde abgebrochen.", Konstanten.PROGRAMMNAME, + JOptionPane.WARNING_MESSAGE) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/mediathek/gui/actions/import_actions/ImportOldBlacklistAction.kt b/src/main/kotlin/mediathek/gui/actions/import_actions/ImportOldBlacklistAction.kt new file mode 100644 index 0000000000..27ca9e091f --- /dev/null +++ b/src/main/kotlin/mediathek/gui/actions/import_actions/ImportOldBlacklistAction.kt @@ -0,0 +1,42 @@ +package mediathek.gui.actions.import_actions + +import mediathek.config.Konstanten +import mediathek.mainwindow.MediathekGui +import mediathek.tool.FileDialogs.Companion.chooseLoadFileLocation +import mediathek.tool.SwingErrorDialog +import java.awt.event.ActionEvent +import javax.swing.AbstractAction +import javax.swing.JOptionPane + +class ImportOldBlacklistAction : AbstractAction() { + init { + putValue(NAME, "Alte Blacklist...") + putValue(SHORT_DESCRIPTION, "Ermöglicht den Import der Blacklist aus einer alten Konfigurationsdatei.") + } + + override fun actionPerformed(e: ActionEvent) { + val selectedFile = chooseLoadFileLocation(MediathekGui.ui(), " Konfigurationsdatei öffnen", "") + if (selectedFile != null) { + try { + val configReader = OldConfigFileImporter() + val (_, foundBlacklistEntries) = configReader.importAboBlacklist(selectedFile.absolutePath, + importAbo = false, + importBlacklist = true, + importReplaceList = false) + val text = "Es wurden $foundBlacklistEntries Einträge importiert." + JOptionPane.showMessageDialog(MediathekGui.ui(), text, Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE) + } + catch (ex: Exception) { + val text = """ + Es trat ein Fehler beim Import der Blacklist auf. + Sollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam. + """.trimIndent() + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), text, ex) + } + } else { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Der Import der Blacklist wurde abgebrochen.", + Konstanten.PROGRAMMNAME, JOptionPane.WARNING_MESSAGE) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/mediathek/gui/actions/import_actions/ImportOldReplacementListAction.kt b/src/main/kotlin/mediathek/gui/actions/import_actions/ImportOldReplacementListAction.kt new file mode 100644 index 0000000000..9e477f6eff --- /dev/null +++ b/src/main/kotlin/mediathek/gui/actions/import_actions/ImportOldReplacementListAction.kt @@ -0,0 +1,42 @@ +package mediathek.gui.actions.import_actions + +import mediathek.config.Konstanten +import mediathek.mainwindow.MediathekGui +import mediathek.tool.FileDialogs.Companion.chooseLoadFileLocation +import mediathek.tool.SwingErrorDialog +import java.awt.event.ActionEvent +import javax.swing.AbstractAction +import javax.swing.JOptionPane + +class ImportOldReplacementListAction : AbstractAction() { + init { + putValue(NAME, "Alte Ersetzungstabelle...") + putValue(SHORT_DESCRIPTION, "Ermöglicht den Import der Ersetzungstabelle aus einer alten Konfigurationsdatei.") + } + + override fun actionPerformed(e: ActionEvent) { + val selectedFile = chooseLoadFileLocation(MediathekGui.ui(), " Konfigurationsdatei öffnen", "") + if (selectedFile != null) { + try { + val configReader = OldConfigFileImporter() + val (_, _, foundReplaceListEntries) = configReader.importAboBlacklist(selectedFile.absolutePath, + importAbo = false, + importBlacklist = false, + importReplaceList = true) + val text = "Es wurden $foundReplaceListEntries Einträge importiert." + JOptionPane.showMessageDialog(MediathekGui.ui(), text, Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE) + } + catch (ex: Exception) { + val text = """ + Es trat ein Fehler beim Import der Ersetzungstabelle auf. + Sollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam. + """.trimIndent() + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), text, ex) + } + } else { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Der Import der Ersetzungstabelle wurde abgebrochen.", + Konstanten.PROGRAMMNAME, JOptionPane.WARNING_MESSAGE) + } + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/actions/import_actions/OldConfigFileImporter.kt b/src/main/kotlin/mediathek/gui/actions/import_actions/OldConfigFileImporter.kt similarity index 83% rename from src/main/java/mediathek/gui/actions/import_actions/OldConfigFileImporter.kt rename to src/main/kotlin/mediathek/gui/actions/import_actions/OldConfigFileImporter.kt index ab3c372a0d..6e5df5e148 100644 --- a/src/main/java/mediathek/gui/actions/import_actions/OldConfigFileImporter.kt +++ b/src/main/kotlin/mediathek/gui/actions/import_actions/OldConfigFileImporter.kt @@ -6,7 +6,6 @@ import mediathek.daten.blacklist.BlacklistRule import mediathek.gui.messages.ReplaceListChangedEvent import mediathek.tool.MessageBus import mediathek.tool.ReplaceList -import org.apache.commons.lang3.tuple.ImmutableTriple import org.apache.logging.log4j.LogManager import java.io.FileInputStream import java.io.IOException @@ -22,12 +21,10 @@ class OldConfigFileImporter { private val inFactory: XMLInputFactory = XMLInputFactory.newInstance() @Throws(IOException::class, XMLStreamException::class) - fun importAboBlacklist( - datei: String, - importAbo: Boolean, - importBlacklist: Boolean, - importReplaceList: Boolean - ): ImmutableTriple { + fun importAboBlacklist(datei: String, + importAbo: Boolean, + importBlacklist: Boolean, + importReplaceList: Boolean): ImportResult { var foundAbos = 0 var foundBlacklistEntries = 0 var foundReplaceListEntries = 0 @@ -60,11 +57,13 @@ class OldConfigFileImporter { } } } - } finally { + } + finally { if (parser != null) { try { parser!!.close() - } catch (ignored: XMLStreamException) { + } + catch (ignored: XMLStreamException) { } } } @@ -76,16 +75,19 @@ class OldConfigFileImporter { if (foundReplaceListEntries > 0) MessageBus.messageBus.publishAsync(ReplaceListChangedEvent()) - return ImmutableTriple(foundAbos, foundBlacklistEntries, foundReplaceListEntries) + return ImportResult(foundAbos, foundBlacklistEntries, foundReplaceListEntries) } + data class ImportResult(val foundAbos: Int, val foundBlacklistEntries: Int, val foundReplaceListEntries: Int) + private fun importAboEntry(parser: XMLStreamReader): Boolean { return try { val datenAbo = DatenAbo() datenAbo.readFromConfig(parser) daten.listeAbo.addAbo(datenAbo) true - } catch (e: Exception) { + } + catch (e: Exception) { logger.error("Error importing abo entry") false } @@ -93,7 +95,7 @@ class OldConfigFileImporter { private fun importReplaceList(parser: XMLStreamReader): Boolean { val sa = arrayOfNulls(ReplaceList.MAX_ELEM) - val success = get(parser, ReplaceList.REPLACELIST, ReplaceList.COLUMN_NAMES, sa) + val success = get(parser, sa) return if (success) { ReplaceList.list.add(sa) true @@ -101,10 +103,7 @@ class OldConfigFileImporter { false } - private operator fun get( - parser: XMLStreamReader, xmlElem: String, xmlNames: Array, - strRet: Array - ): Boolean { + private operator fun get(parser: XMLStreamReader, strRet: Array): Boolean { val maxElem = strRet.size for (i in 0 until maxElem) { if (strRet[i] == null) { @@ -116,13 +115,13 @@ class OldConfigFileImporter { while (parser.hasNext()) { val event = parser.next() if (event == XMLStreamConstants.END_ELEMENT) { - if (parser.localName == xmlElem) { + if (parser.localName == ReplaceList.REPLACELIST) { break } } if (event == XMLStreamConstants.START_ELEMENT) { for (i in 0 until maxElem) { - if (parser.localName == xmlNames[i]) { + if (parser.localName == ReplaceList.COLUMN_NAMES[i]) { strRet[i] = parser.elementText break } @@ -130,7 +129,8 @@ class OldConfigFileImporter { } } true - } catch (ex: Exception) { + } + catch (ex: Exception) { logger.error("get", ex) false } diff --git a/src/main/kotlin/org/jdesktop/swingx/VerticalLayout.kt b/src/main/kotlin/org/jdesktop/swingx/VerticalLayout.kt deleted file mode 100644 index b1dbf105a6..0000000000 --- a/src/main/kotlin/org/jdesktop/swingx/VerticalLayout.kt +++ /dev/null @@ -1,82 +0,0 @@ -package org.jdesktop.swingx - -import java.awt.Component -import java.awt.Container -import java.awt.Dimension -import java.awt.LayoutManager -import java.io.Serializable -import kotlin.math.max - -abstract class AbstractLayoutManager : LayoutManager, Serializable { - override fun addLayoutComponent(name: String, comp: Component) { - //do nothing - } - - override fun removeLayoutComponent(comp: Component) { - // do nothing - } - - override fun minimumLayoutSize(parent: Container): Dimension { - return preferredLayoutSize(parent) - } - - companion object { - private const val serialVersionUID = 1446292747820044161L - } -} - -/** - * SwingX VerticalLayout implementation recreated in Kotlin. - * Unfortunately SwingX is not maintained anymore :( - */ -class VerticalLayout -@JvmOverloads constructor(var gap: Int = 0) : AbstractLayoutManager() { - internal class Separator(private var next: Int, private val separator: Int) { - fun get(): Int { - val result = next - next = separator - return result - } - } - - override fun preferredLayoutSize(parent: Container): Dimension { - val pref = Dimension(0, 0) - val sep = Separator(0, gap) - var i = 0 - val c = parent.componentCount - while (i < c) { - val m = parent.getComponent(i) - if (m.isVisible) { - val componentPreferredSize = parent.getComponent(i).preferredSize - pref.height += componentPreferredSize.height + sep.get() - pref.width = max(pref.width, componentPreferredSize.width) - } - i++ - } - val insets = parent.insets - pref.width += insets.left + insets.right - pref.height += insets.top + insets.bottom - return pref - } - - override fun layoutContainer(parent: Container) { - val insets = parent.insets - val size = parent.size - val width = size.width - insets.left - insets.right - var height = insets.top - var i = 0 - val c = parent.componentCount - while (i < c) { - val m = parent.getComponent(i) - if (m.isVisible) { - m.setBounds(insets.left, height, width, m.preferredSize.height) - height += m.size.height + gap - } - i++ - } - } - - companion object { - private const val serialVersionUID = 5342270033773736441L - } -} \ No newline at end of file diff --git a/src/main/resources/icons/countries/003-austria.png b/src/main/resources/icons/countries/003-austria.png new file mode 100644 index 0000000000..26980dea5b Binary files /dev/null and b/src/main/resources/icons/countries/003-austria.png differ diff --git a/src/main/resources/icons/countries/162-germany.png b/src/main/resources/icons/countries/162-germany.png new file mode 100644 index 0000000000..e298c25bba Binary files /dev/null and b/src/main/resources/icons/countries/162-germany.png differ diff --git a/src/main/resources/icons/countries/195-france.png b/src/main/resources/icons/countries/195-france.png new file mode 100644 index 0000000000..f03106978d Binary files /dev/null and b/src/main/resources/icons/countries/195-france.png differ diff --git a/src/main/resources/icons/countries/205-switzerland.png b/src/main/resources/icons/countries/205-switzerland.png new file mode 100644 index 0000000000..ff98f6da0f Binary files /dev/null and b/src/main/resources/icons/countries/205-switzerland.png differ diff --git a/src/main/resources/icons/countries/259-european-union.png b/src/main/resources/icons/countries/259-european-union.png new file mode 100644 index 0000000000..a8d4ad5e3b Binary files /dev/null and b/src/main/resources/icons/countries/259-european-union.png differ diff --git a/src/main/resources/icons/derreisende77/high-quality.svg b/src/main/resources/icons/derreisende77/high-quality.svg new file mode 100644 index 0000000000..697bbef04d --- /dev/null +++ b/src/main/resources/icons/derreisende77/high-quality.svg @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/src/main/resources/icons/fontawesome/angles-down.svg b/src/main/resources/icons/fontawesome/angles-down.svg new file mode 100755 index 0000000000..8d771123a4 --- /dev/null +++ b/src/main/resources/icons/fontawesome/angles-down.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/arrow-down.svg b/src/main/resources/icons/fontawesome/arrow-down.svg new file mode 100755 index 0000000000..d19b8adca4 --- /dev/null +++ b/src/main/resources/icons/fontawesome/arrow-down.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/arrow-up.svg b/src/main/resources/icons/fontawesome/arrow-up.svg new file mode 100755 index 0000000000..8fa8da60c8 --- /dev/null +++ b/src/main/resources/icons/fontawesome/arrow-up.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/arrows-rotate.svg b/src/main/resources/icons/fontawesome/arrows-rotate.svg new file mode 100755 index 0000000000..ffd3595d12 --- /dev/null +++ b/src/main/resources/icons/fontawesome/arrows-rotate.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/audio-description.svg b/src/main/resources/icons/fontawesome/audio-description.svg new file mode 100755 index 0000000000..d75c64d5e6 --- /dev/null +++ b/src/main/resources/icons/fontawesome/audio-description.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/bookmark.svg b/src/main/resources/icons/fontawesome/bookmark.svg new file mode 100644 index 0000000000..2c06cb0d35 --- /dev/null +++ b/src/main/resources/icons/fontawesome/bookmark.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/broom.svg b/src/main/resources/icons/fontawesome/broom.svg new file mode 100755 index 0000000000..2692ec199d --- /dev/null +++ b/src/main/resources/icons/fontawesome/broom.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/caret-down.svg b/src/main/resources/icons/fontawesome/caret-down.svg new file mode 100755 index 0000000000..765689b8ae --- /dev/null +++ b/src/main/resources/icons/fontawesome/caret-down.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/check.svg b/src/main/resources/icons/fontawesome/check.svg new file mode 100755 index 0000000000..c3e9b881d7 --- /dev/null +++ b/src/main/resources/icons/fontawesome/check.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/circle-half-stroke.svg b/src/main/resources/icons/fontawesome/circle-half-stroke.svg new file mode 100755 index 0000000000..5c791c86da --- /dev/null +++ b/src/main/resources/icons/fontawesome/circle-half-stroke.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/circle-info.svg b/src/main/resources/icons/fontawesome/circle-info.svg new file mode 100644 index 0000000000..c8efbac9b6 --- /dev/null +++ b/src/main/resources/icons/fontawesome/circle-info.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/circle-question.svg b/src/main/resources/icons/fontawesome/circle-question.svg new file mode 100755 index 0000000000..04a60fc471 --- /dev/null +++ b/src/main/resources/icons/fontawesome/circle-question.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/clock.svg b/src/main/resources/icons/fontawesome/clock.svg new file mode 100755 index 0000000000..7d1afeacf3 --- /dev/null +++ b/src/main/resources/icons/fontawesome/clock.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/closed-captioning.svg b/src/main/resources/icons/fontawesome/closed-captioning.svg new file mode 100755 index 0000000000..055e8b36bd --- /dev/null +++ b/src/main/resources/icons/fontawesome/closed-captioning.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/cloud-arrow-down.svg b/src/main/resources/icons/fontawesome/cloud-arrow-down.svg new file mode 100644 index 0000000000..b2cd09c07f --- /dev/null +++ b/src/main/resources/icons/fontawesome/cloud-arrow-down.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/database.svg b/src/main/resources/icons/fontawesome/database.svg new file mode 100755 index 0000000000..ae479b5be9 --- /dev/null +++ b/src/main/resources/icons/fontawesome/database.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/download.svg b/src/main/resources/icons/fontawesome/download.svg new file mode 100644 index 0000000000..f2dc534f9c --- /dev/null +++ b/src/main/resources/icons/fontawesome/download.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/envelope-open-text.svg b/src/main/resources/icons/fontawesome/envelope-open-text.svg new file mode 100755 index 0000000000..78b8165439 --- /dev/null +++ b/src/main/resources/icons/fontawesome/envelope-open-text.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/eraser.svg b/src/main/resources/icons/fontawesome/eraser.svg new file mode 100755 index 0000000000..c7b1b5c69f --- /dev/null +++ b/src/main/resources/icons/fontawesome/eraser.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/file-lines.svg b/src/main/resources/icons/fontawesome/file-lines.svg new file mode 100755 index 0000000000..9b04721e39 --- /dev/null +++ b/src/main/resources/icons/fontawesome/file-lines.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/filter.svg b/src/main/resources/icons/fontawesome/filter.svg new file mode 100755 index 0000000000..5d8b626736 --- /dev/null +++ b/src/main/resources/icons/fontawesome/filter.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/folder-open.svg b/src/main/resources/icons/fontawesome/folder-open.svg new file mode 100755 index 0000000000..ffdb987f0d --- /dev/null +++ b/src/main/resources/icons/fontawesome/folder-open.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/gears.svg b/src/main/resources/icons/fontawesome/gears.svg new file mode 100755 index 0000000000..be32f2a81e --- /dev/null +++ b/src/main/resources/icons/fontawesome/gears.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/list-check.svg b/src/main/resources/icons/fontawesome/list-check.svg new file mode 100755 index 0000000000..1734cb8ab3 --- /dev/null +++ b/src/main/resources/icons/fontawesome/list-check.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/lock-open.svg b/src/main/resources/icons/fontawesome/lock-open.svg new file mode 100755 index 0000000000..97c7d7211a --- /dev/null +++ b/src/main/resources/icons/fontawesome/lock-open.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/lock.svg b/src/main/resources/icons/fontawesome/lock.svg new file mode 100755 index 0000000000..d37afda19a --- /dev/null +++ b/src/main/resources/icons/fontawesome/lock.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/minus.svg b/src/main/resources/icons/fontawesome/minus.svg new file mode 100755 index 0000000000..3d18470998 --- /dev/null +++ b/src/main/resources/icons/fontawesome/minus.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/pen-to-square.svg b/src/main/resources/icons/fontawesome/pen-to-square.svg new file mode 100755 index 0000000000..fedefc5c1a --- /dev/null +++ b/src/main/resources/icons/fontawesome/pen-to-square.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/play.svg b/src/main/resources/icons/fontawesome/play.svg new file mode 100644 index 0000000000..3cb176cb54 --- /dev/null +++ b/src/main/resources/icons/fontawesome/play.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/plus.svg b/src/main/resources/icons/fontawesome/plus.svg new file mode 100755 index 0000000000..0732f95fe7 --- /dev/null +++ b/src/main/resources/icons/fontawesome/plus.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/rectangle-list.svg b/src/main/resources/icons/fontawesome/rectangle-list.svg new file mode 100755 index 0000000000..03fecd8d87 --- /dev/null +++ b/src/main/resources/icons/fontawesome/rectangle-list.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/stop.svg b/src/main/resources/icons/fontawesome/stop.svg new file mode 100644 index 0000000000..41eb7b78e6 --- /dev/null +++ b/src/main/resources/icons/fontawesome/stop.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/tower-cell.svg b/src/main/resources/icons/fontawesome/tower-cell.svg new file mode 100755 index 0000000000..b774931fed --- /dev/null +++ b/src/main/resources/icons/fontawesome/tower-cell.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/trash-can.svg b/src/main/resources/icons/fontawesome/trash-can.svg new file mode 100755 index 0000000000..3777b1fcb1 --- /dev/null +++ b/src/main/resources/icons/fontawesome/trash-can.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/triangle-exclamation.svg b/src/main/resources/icons/fontawesome/triangle-exclamation.svg new file mode 100755 index 0000000000..ab383acb47 --- /dev/null +++ b/src/main/resources/icons/fontawesome/triangle-exclamation.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/icons/fontawesome/xmark.svg b/src/main/resources/icons/fontawesome/xmark.svg new file mode 100755 index 0000000000..c6ed4c6df8 --- /dev/null +++ b/src/main/resources/icons/fontawesome/xmark.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/main/resources/mediathek/file/hilfetext_pset.txt b/src/main/resources/mediathek/file/hilfetext_pset.txt index add85de527..bb653ea655 100644 --- a/src/main/resources/mediathek/file/hilfetext_pset.txt +++ b/src/main/resources/mediathek/file/hilfetext_pset.txt @@ -61,7 +61,7 @@ Zeit in der Form: SSMMss z.B. 152059 (15:20:59) %N Originaldateiname des Films (der kann sehr kryptisch und lang sein) %S Suffix des Originaldateinamens des Films (z.B. "mp4") -%i Filmnummer (die ändert sich beim Neuladen der Filmliste!) +%i ehemals die interne Filmnummer, nun die aktuelle Uhrzeit in Millisekunden seit 1970 %q Qualität des Films ("HD", "H", "L") %Z Hashwert der URL, z.B.: 1433245578 diff --git a/src/main/resources/mediathek/res/programm/button-blacklist-aus.png b/src/main/resources/mediathek/res/programm/button-blacklist-aus.png deleted file mode 100644 index 84c9fb85e5..0000000000 Binary files a/src/main/resources/mediathek/res/programm/button-blacklist-aus.png and /dev/null differ diff --git a/src/main/resources/mediathek/res/programm/button-blacklist-ein.png b/src/main/resources/mediathek/res/programm/button-blacklist-ein.png deleted file mode 100644 index 9591971d63..0000000000 Binary files a/src/main/resources/mediathek/res/programm/button-blacklist-ein.png and /dev/null differ diff --git a/src/main/resources/mediathek/res/programm/button-clear.png b/src/main/resources/mediathek/res/programm/button-clear.png deleted file mode 100644 index e6c8e8b9f3..0000000000 Binary files a/src/main/resources/mediathek/res/programm/button-clear.png and /dev/null differ diff --git a/src/main/resources/mediathek/res/programm/button-file-open.png b/src/main/resources/mediathek/res/programm/button-file-open.png deleted file mode 100644 index f84a0db279..0000000000 Binary files a/src/main/resources/mediathek/res/programm/button-file-open.png and /dev/null differ diff --git a/src/main/resources/mediathek/res/programm/fxml/abo/abo_information_panel.fxml b/src/main/resources/mediathek/res/programm/fxml/abo/abo_information_panel.fxml deleted file mode 100644 index f9b5a474f8..0000000000 --- a/src/main/resources/mediathek/res/programm/fxml/abo/abo_information_panel.fxml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - diff --git a/src/main/resources/mediathek/res/programm/fxml/abo/abo_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/abo/abo_toolbar.fxml deleted file mode 100644 index 43c5669bd6..0000000000 --- a/src/main/resources/mediathek/res/programm/fxml/abo/abo_toolbar.fxml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
diff --git a/src/main/resources/mediathek/res/programm/fxml/about.fxml b/src/main/resources/mediathek/res/programm/fxml/about.fxml deleted file mode 100644 index 15b48be839..0000000000 --- a/src/main/resources/mediathek/res/programm/fxml/about.fxml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -