wörk wörk (day 3)
* added License file * added changelog feature * added zsync user-agent * added application and taskbar icon * added taskbar progressbar * added social buttons * added about page with disclaimer, copyright and library license * changed gui design alot * fixed some gui errors * fixed generateRepo.sh skip empty files
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Niklas Schütrumpf (Gurkengewuerz)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
1
linux/.sync/changelog.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
write your changelog here
|
@ -92,15 +92,29 @@ while IFS= read -r folder; do
|
|||||||
FILEFOLDER=$(find "${folder}" -type f ! -path "*.zsync" | sed 's|^./||')
|
FILEFOLDER=$(find "${folder}" -type f ! -path "*.zsync" | sed 's|^./||')
|
||||||
while IFS= read -r folderfile; do
|
while IFS= read -r folderfile; do
|
||||||
filebyte=$(wc -c < "${folderfile}")
|
filebyte=$(wc -c < "${folderfile}")
|
||||||
|
|
||||||
|
if [ $filebyte -eq 0 ]; then
|
||||||
|
echo "Skipping \"${folderfile}\" because file is empty"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
foldersize=$(expr $foldersize + $filebyte)
|
foldersize=$(expr $foldersize + $filebyte)
|
||||||
name=$(echo "${folderfile}" | cut -d"/" -f2-)
|
name=$(echo "${folderfile}" | cut -d"/" -f2-)
|
||||||
x="\"${name}\":{\"size\": ${filebyte}, \"sha1\": \"${SHASUMS[$folderfile]}\"},${x}"
|
x="\"${name}\":{\"size\": ${filebyte}, \"sha1\": \"${SHASUMS[$folderfile]}\"},${x}"
|
||||||
done <<< "$FILEFOLDER"
|
done <<< "$FILEFOLDER"
|
||||||
x=$(echo ${x} | rev | cut -c2- | rev)
|
x=$(echo ${x} | rev | cut -c2- | rev)
|
||||||
|
|
||||||
|
if [ $foldersize -eq 0 ]; then
|
||||||
|
echo "Skipping complete folder \"${$folder}\" because all files are empty"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
JSONDATA+=( "\"${folder}\": {\"size\":${foldersize},\"content\":{${x}}}" )
|
JSONDATA+=( "\"${folder}\": {\"size\":${foldersize},\"content\":{${x}}}" )
|
||||||
else
|
else
|
||||||
echo "is file"
|
echo "is file"
|
||||||
filebyte=$(wc -c < "${folder}")
|
filebyte=$(wc -c < "${folder}")
|
||||||
|
if [ $filebyte -eq 0 ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
JSONDATA+=( "\"${folder}\": {\"size\":${filebyte}, \"sha1\": \"${SHASUMS[$folder]}\"}" )
|
JSONDATA+=( "\"${folder}\": {\"size\":${filebyte}, \"sha1\": \"${SHASUMS[$folder]}\"}" )
|
||||||
fi
|
fi
|
||||||
done <<< "$FILELIST"
|
done <<< "$FILELIST"
|
||||||
|
11
pom.xml
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>de.mc8051</groupId>
|
<groupId>de.mc8051</groupId>
|
||||||
<artifactId>arma3launcher</artifactId>
|
<artifactId>arma3launcher</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>0.1.1000</version>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
@ -44,7 +44,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.Gurkengewuerz</groupId>
|
<groupId>com.github.Gurkengewuerz</groupId>
|
||||||
<artifactId>zsyncer</artifactId>
|
<artifactId>zsyncer</artifactId>
|
||||||
<version>locale_fix-2f7565d392-1</version>
|
<version>1de0d3f651</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
@ -65,6 +65,13 @@
|
|||||||
<include>**/*</include>
|
<include>**/*</include>
|
||||||
</includes>
|
</includes>
|
||||||
</resource>
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources/icons</directory>
|
||||||
|
<filtering>false</filtering>
|
||||||
|
<includes>
|
||||||
|
<include>**/*</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
@ -1,9 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-2020 Niklas Schütrumpf (Gurkengewuerz)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
package de.mc8051.arma3launcher;
|
package de.mc8051.arma3launcher;
|
||||||
|
|
||||||
import com.formdev.flatlaf.FlatDarkLaf;
|
import com.formdev.flatlaf.FlatDarkLaf;
|
||||||
import com.typesafe.config.Config;
|
import com.typesafe.config.Config;
|
||||||
import com.typesafe.config.ConfigFactory;
|
import com.typesafe.config.ConfigFactory;
|
||||||
import de.mc8051.arma3launcher.steam.SteamTimer;
|
import de.mc8051.arma3launcher.steam.SteamTimer;
|
||||||
|
import de.mc8051.arma3launcher.utils.TaskBarUtils;
|
||||||
import org.ini4j.Ini;
|
import org.ini4j.Ini;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@ -28,6 +53,7 @@ public class ArmA3Launcher {
|
|||||||
public static String VERSION;
|
public static String VERSION;
|
||||||
public static String CLIENT_NAME;
|
public static String CLIENT_NAME;
|
||||||
public static String APPLICATION_PATH;
|
public static String APPLICATION_PATH;
|
||||||
|
public static String USER_AGENT;
|
||||||
|
|
||||||
public static Config config;
|
public static Config config;
|
||||||
public static Ini user_config;
|
public static Ini user_config;
|
||||||
@ -39,11 +65,12 @@ public class ArmA3Launcher {
|
|||||||
|
|
||||||
final Properties properties = new Properties();
|
final Properties properties = new Properties();
|
||||||
properties.load(ArmA3Launcher.class.getClassLoader().getResourceAsStream("project.properties"));
|
properties.load(ArmA3Launcher.class.getClassLoader().getResourceAsStream("project.properties"));
|
||||||
|
|
||||||
VERSION = properties.getProperty("version");
|
VERSION = properties.getProperty("version");
|
||||||
|
|
||||||
APPLICATION_PATH = getAppData() + CLIENT_NAME;
|
APPLICATION_PATH = getAppData() + CLIENT_NAME;
|
||||||
|
|
||||||
|
USER_AGENT = config.getString("sync.useragent") + "/" + VERSION;
|
||||||
|
|
||||||
if (new File(APPLICATION_PATH).mkdirs()) {
|
if (new File(APPLICATION_PATH).mkdirs()) {
|
||||||
Logger.getLogger(ArmA3Launcher.class.getName()).log(Level.SEVERE, "Can not create " + APPLICATION_PATH);
|
Logger.getLogger(ArmA3Launcher.class.getName()).log(Level.SEVERE, "Can not create " + APPLICATION_PATH);
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
@ -66,6 +93,8 @@ public class ArmA3Launcher {
|
|||||||
UIManager.setLookAndFeel(new FlatDarkLaf());
|
UIManager.setLookAndFeel(new FlatDarkLaf());
|
||||||
|
|
||||||
JFrame frame = new JFrame(CLIENT_NAME);
|
JFrame frame = new JFrame(CLIENT_NAME);
|
||||||
|
TaskBarUtils.getInstance().setWindow(frame);
|
||||||
|
|
||||||
LauncherGUI gui = new LauncherGUI();
|
LauncherGUI gui = new LauncherGUI();
|
||||||
frame.setContentPane(gui.mainPanel);
|
frame.setContentPane(gui.mainPanel);
|
||||||
|
|
||||||
@ -74,14 +103,18 @@ public class ArmA3Launcher {
|
|||||||
public void windowClosing(WindowEvent e) {
|
public void windowClosing(WindowEvent e) {
|
||||||
steamTimer.cancel();
|
steamTimer.cancel();
|
||||||
steamTimer.purge();
|
steamTimer.purge();
|
||||||
|
TaskBarUtils.getInstance().removeTrayIcon();
|
||||||
|
gui.exit();
|
||||||
frame.dispose();
|
frame.dispose();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frame.setMinimumSize(new Dimension(1000, 500));
|
frame.setMinimumSize(new Dimension(1000, 550));
|
||||||
|
|
||||||
frame.pack();
|
frame.pack();
|
||||||
|
frame.setIconImage(TaskBarUtils.IMAGE_ICON);
|
||||||
frame.setLocationRelativeTo(null);
|
frame.setLocationRelativeTo(null);
|
||||||
|
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||||
|
|
||||||
steamTimer.scheduleAtFixedRate(
|
steamTimer.scheduleAtFixedRate(
|
||||||
new SteamTimer(),
|
new SteamTimer(),
|
||||||
|
@ -8,10 +8,12 @@ import de.mc8051.arma3launcher.model.PresetTableModel;
|
|||||||
import de.mc8051.arma3launcher.model.RepositoryTreeNode;
|
import de.mc8051.arma3launcher.model.RepositoryTreeNode;
|
||||||
import de.mc8051.arma3launcher.model.ServerTableModel;
|
import de.mc8051.arma3launcher.model.ServerTableModel;
|
||||||
import de.mc8051.arma3launcher.objects.AbstractMod;
|
import de.mc8051.arma3launcher.objects.AbstractMod;
|
||||||
|
import de.mc8051.arma3launcher.objects.Changelog;
|
||||||
import de.mc8051.arma3launcher.objects.Mod;
|
import de.mc8051.arma3launcher.objects.Mod;
|
||||||
import de.mc8051.arma3launcher.objects.ModFile;
|
import de.mc8051.arma3launcher.objects.ModFile;
|
||||||
import de.mc8051.arma3launcher.objects.Modset;
|
import de.mc8051.arma3launcher.objects.Modset;
|
||||||
import de.mc8051.arma3launcher.objects.Server;
|
import de.mc8051.arma3launcher.objects.Server;
|
||||||
|
import de.mc8051.arma3launcher.repo.DownloadStatus;
|
||||||
import de.mc8051.arma3launcher.repo.FileChecker;
|
import de.mc8051.arma3launcher.repo.FileChecker;
|
||||||
import de.mc8051.arma3launcher.repo.RepositoryManger;
|
import de.mc8051.arma3launcher.repo.RepositoryManger;
|
||||||
import de.mc8051.arma3launcher.repo.SyncList;
|
import de.mc8051.arma3launcher.repo.SyncList;
|
||||||
@ -19,11 +21,12 @@ import de.mc8051.arma3launcher.repo.Syncer;
|
|||||||
import de.mc8051.arma3launcher.steam.SteamTimer;
|
import de.mc8051.arma3launcher.steam.SteamTimer;
|
||||||
import de.mc8051.arma3launcher.utils.Callback;
|
import de.mc8051.arma3launcher.utils.Callback;
|
||||||
import de.mc8051.arma3launcher.utils.Humanize;
|
import de.mc8051.arma3launcher.utils.Humanize;
|
||||||
|
import de.mc8051.arma3launcher.utils.ImageUtils;
|
||||||
import de.mc8051.arma3launcher.utils.LangUtils;
|
import de.mc8051.arma3launcher.utils.LangUtils;
|
||||||
|
import de.mc8051.arma3launcher.utils.TaskBarUtils;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.ListSelectionEvent;
|
import javax.swing.border.EmptyBorder;
|
||||||
import javax.swing.event.ListSelectionListener;
|
|
||||||
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
||||||
import javax.swing.text.DefaultFormatter;
|
import javax.swing.text.DefaultFormatter;
|
||||||
import javax.swing.tree.DefaultMutableTreeNode;
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
@ -34,14 +37,23 @@ import java.awt.*;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.ItemEvent;
|
import java.awt.event.ItemEvent;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.StringWriter;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.net.URL;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Scanner;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -60,7 +72,6 @@ public class LauncherGUI implements Observer {
|
|||||||
private JLabel steamStatus;
|
private JLabel steamStatus;
|
||||||
private JLabel armaStatus;
|
private JLabel armaStatus;
|
||||||
private JButton presetPanelButton;
|
private JButton presetPanelButton;
|
||||||
private JPanel logo;
|
|
||||||
private JPanel presetsTab;
|
private JPanel presetsTab;
|
||||||
private JButton playPresetButton;
|
private JButton playPresetButton;
|
||||||
private JButton clonePresetButton;
|
private JButton clonePresetButton;
|
||||||
@ -129,17 +140,32 @@ public class LauncherGUI implements Observer {
|
|||||||
private JLabel syncDeletedFilesLabel;
|
private JLabel syncDeletedFilesLabel;
|
||||||
private JLabel syncAddedFilesLabel;
|
private JLabel syncAddedFilesLabel;
|
||||||
private JLabel syncChangedFilesLabel;
|
private JLabel syncChangedFilesLabel;
|
||||||
private JLabel syncSizeLabel;
|
public JLabel syncSizeLabel;
|
||||||
private JLabel syncChangedFileSizeLabel;
|
private JLabel syncChangedFileSizeLabel;
|
||||||
private JLabel syncFileCountLabel;
|
public JLabel syncFileCountLabel;
|
||||||
public JLabel syncDownloadedLabel;
|
|
||||||
public JLabel syncDownloadSpeedLabel;
|
public JLabel syncDownloadSpeedLabel;
|
||||||
private JSplitPane splitView;
|
private JSplitPane splitView;
|
||||||
|
public JLabel syncStatusLabel;
|
||||||
|
private JLabel logo;
|
||||||
|
private JLabel aboutLabel;
|
||||||
|
private JButton changelogButton;
|
||||||
|
private JPanel changelogTab;
|
||||||
|
private JPanel aboutTab;
|
||||||
|
private JTextArea changelogPane;
|
||||||
|
private JScrollPane changelogScroll;
|
||||||
|
private JLabel twitterIcon;
|
||||||
|
private JLabel githubIcon;
|
||||||
|
private JTextPane disclaimer;
|
||||||
|
private JLabel aboutLogo;
|
||||||
|
private JLabel aboutClient;
|
||||||
|
private JLabel aboutProjectLabel;
|
||||||
|
private JLabel aboutDeveloperLabel;
|
||||||
|
private JLabel aboutCopyrightLabel;
|
||||||
|
|
||||||
private JCheckBoxTree repoTree;
|
private JCheckBoxTree repoTree;
|
||||||
private FileChecker fileChecker;
|
private FileChecker fileChecker;
|
||||||
private Syncer syncer;
|
private Syncer syncer;
|
||||||
private SyncList lastSynclist;
|
private SyncList lastSynclist = null;
|
||||||
|
|
||||||
public LauncherGUI() {
|
public LauncherGUI() {
|
||||||
fileChecker = new FileChecker(syncCheckProgress);
|
fileChecker = new FileChecker(syncCheckProgress);
|
||||||
@ -150,6 +176,16 @@ public class LauncherGUI implements Observer {
|
|||||||
fileChecker.addObserver(this);
|
fileChecker.addObserver(this);
|
||||||
syncer.addObserver(this);
|
syncer.addObserver(this);
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
RepositoryManger.getInstance().refreshMeta();
|
||||||
|
try {
|
||||||
|
Thread.sleep(750);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
RepositoryManger.getInstance().refreshModset();
|
||||||
|
}).start();
|
||||||
|
|
||||||
updateTreePanel.remove(tree1);
|
updateTreePanel.remove(tree1);
|
||||||
|
|
||||||
repoTree = new JCheckBoxTree();
|
repoTree = new JCheckBoxTree();
|
||||||
@ -184,63 +220,14 @@ public class LauncherGUI implements Observer {
|
|||||||
updatePanelButton.setMargin(x);
|
updatePanelButton.setMargin(x);
|
||||||
playPanelButton.setMargin(x);
|
playPanelButton.setMargin(x);
|
||||||
presetPanelButton.setMargin(x);
|
presetPanelButton.setMargin(x);
|
||||||
|
changelogButton.setMargin(x);
|
||||||
|
|
||||||
playPresetButton.setMargin(new Insets(10, 10, 10, 10));
|
playPresetButton.setMargin(new Insets(10, 10, 10, 10));
|
||||||
|
|
||||||
playPanelButton.addActionListener(new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
tabbedPane1.setSelectedIndex(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
updatePanelButton.addActionListener(new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
tabbedPane1.setSelectedIndex(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
presetPanelButton.addActionListener(new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
tabbedPane1.setSelectedIndex(2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
settingsPanelButton.addActionListener(new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
tabbedPane1.setSelectedIndex(3);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
serverTable.setModel(new ServerTableModel());
|
serverTable.setModel(new ServerTableModel());
|
||||||
|
|
||||||
presetList.setModel(new PresetTableModel());
|
presetList.setModel(new PresetTableModel());
|
||||||
|
|
||||||
presetList.setCellRenderer(new PresetListRenderer());
|
presetList.setCellRenderer(new PresetListRenderer());
|
||||||
presetList.addListSelectionListener(new ListSelectionListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void valueChanged(ListSelectionEvent e) {
|
|
||||||
if (!e.getValueIsAdjusting()) {
|
|
||||||
PresetTableModel m = (PresetTableModel) presetList.getModel();
|
|
||||||
Modset modset = (Modset) m.getElementAt(presetList.getSelectedIndex());
|
|
||||||
|
|
||||||
if (modset.getType() == Modset.Type.SERVER) {
|
|
||||||
renamePresetButton.setEnabled(false);
|
|
||||||
removePresetButtom.setEnabled(false);
|
|
||||||
} else {
|
|
||||||
renamePresetButton.setEnabled(true);
|
|
||||||
removePresetButtom.setEnabled(true);
|
|
||||||
}
|
|
||||||
clonePresetButton.setEnabled(true);
|
|
||||||
|
|
||||||
updateModList(modset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
modList.setCellRenderer(new ModListRenderer());
|
modList.setCellRenderer(new ModListRenderer());
|
||||||
|
|
||||||
subtitle.setText(
|
subtitle.setText(
|
||||||
@ -255,6 +242,49 @@ public class LauncherGUI implements Observer {
|
|||||||
|
|
||||||
initSettings();
|
initSettings();
|
||||||
|
|
||||||
|
logo.setIcon(new ImageIcon(ImageUtils.getScaledImage(TaskBarUtils.IMAGE_LGO, 128, 128)));
|
||||||
|
aboutLogo.setIcon(new ImageIcon(ImageUtils.getScaledImage(TaskBarUtils.IMAGE_LGO, 128, 128)));
|
||||||
|
|
||||||
|
aboutClient.setText(ArmA3Launcher.config.getString("name") + " v" + ArmA3Launcher.VERSION);
|
||||||
|
|
||||||
|
aboutDeveloperLabel.setText("<html><a href=''>https://gurkengewuerz.de</a></html>");
|
||||||
|
aboutProjectLabel.setText("<html><a href=''>"+ArmA3Launcher.config.getString("social.github")+"</a></html>");
|
||||||
|
|
||||||
|
InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("disclaimer.html");
|
||||||
|
if(resourceAsStream != null) {
|
||||||
|
Scanner s = new Scanner(resourceAsStream).useDelimiter("\\A");
|
||||||
|
String result = s.hasNext() ? s.next() : "";
|
||||||
|
disclaimer.setText(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
aboutCopyrightLabel.setText(aboutCopyrightLabel.getText().replace("{year}", "" + Calendar.getInstance().get(Calendar.YEAR)));
|
||||||
|
|
||||||
|
twitterIcon.setBorder(new EmptyBorder(2,2,2,2));
|
||||||
|
githubIcon.setBorder(new EmptyBorder(2,2,2,2));
|
||||||
|
|
||||||
|
settingScrollPane.getVerticalScrollBar().setUnitIncrement(16);
|
||||||
|
updateTreeScrolPane.getVerticalScrollBar().setUnitIncrement(16);
|
||||||
|
splitView.setDividerLocation(-1);
|
||||||
|
|
||||||
|
presetList.addListSelectionListener(e -> {
|
||||||
|
if (!e.getValueIsAdjusting()) {
|
||||||
|
PresetTableModel m = (PresetTableModel) presetList.getModel();
|
||||||
|
Modset modset = (Modset) m.getElementAt(presetList.getSelectedIndex());
|
||||||
|
|
||||||
|
if (modset.getType() == Modset.Type.SERVER) {
|
||||||
|
renamePresetButton.setEnabled(false);
|
||||||
|
removePresetButtom.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
renamePresetButton.setEnabled(true);
|
||||||
|
removePresetButtom.setEnabled(true);
|
||||||
|
}
|
||||||
|
clonePresetButton.setEnabled(true);
|
||||||
|
|
||||||
|
updateModList(modset);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
settingsResetDefault.addActionListener(new ActionListener() {
|
settingsResetDefault.addActionListener(new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
@ -268,73 +298,106 @@ public class LauncherGUI implements Observer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
settingScrollPane.getVerticalScrollBar().setUnitIncrement(16);
|
|
||||||
updateTreeScrolPane.getVerticalScrollBar().setUnitIncrement(16);
|
collapseAllButton.addActionListener(e -> repoTree.collapseAllNodes());
|
||||||
|
playPanelButton.addActionListener(e -> tabbedPane1.setSelectedIndex(0));
|
||||||
|
updatePanelButton.addActionListener(e -> tabbedPane1.setSelectedIndex(1));
|
||||||
|
changelogButton.addActionListener(e -> {
|
||||||
|
tabbedPane1.setSelectedIndex(2);
|
||||||
|
Changelog.refresh();
|
||||||
|
});
|
||||||
|
presetPanelButton.addActionListener(e -> tabbedPane1.setSelectedIndex(3));
|
||||||
|
settingsPanelButton.addActionListener(e -> tabbedPane1.setSelectedIndex(4));
|
||||||
|
|
||||||
refreshRepoButton.addActionListener(e -> RepositoryManger.getInstance().refreshModset());
|
refreshRepoButton.addActionListener(e -> RepositoryManger.getInstance().refreshModset());
|
||||||
expandAllButton.addActionListener(new ActionListener() {
|
expandAllButton.addActionListener(e -> repoTree.expandAllNodes());
|
||||||
@Override
|
syncDownloadAbortButton.addActionListener(e -> syncer.stop());
|
||||||
public void actionPerformed(ActionEvent e) {
|
syncCheckAbortButton.addActionListener(e -> fileChecker.stop());
|
||||||
repoTree.expandAllNodes();
|
|
||||||
}
|
|
||||||
|
syncCheckButton.addActionListener(e -> {
|
||||||
|
syncCheckButton.setEnabled(false);
|
||||||
|
syncCheckAbortButton.setEnabled(true);
|
||||||
|
syncCheckStatusLabel.setText("Running!");
|
||||||
|
new Thread(() -> fileChecker.check()).start();
|
||||||
|
|
||||||
|
refreshRepoButton.setEnabled(false);
|
||||||
|
|
||||||
|
repoTree.setCheckboxesEnabled(false);
|
||||||
|
repoTree.setCheckboxesChecked(false);
|
||||||
});
|
});
|
||||||
collapseAllButton.addActionListener(new ActionListener() {
|
|
||||||
|
syncDownloadButton.addActionListener(e -> {
|
||||||
|
if (!fileChecker.isChecked()) return;
|
||||||
|
syncDownloadButton.setEnabled(false);
|
||||||
|
syncDownloadAbortButton.setEnabled(true);
|
||||||
|
syncPauseButton.setEnabled(true);
|
||||||
|
syncCheckButton.setEnabled(false);
|
||||||
|
refreshRepoButton.setEnabled(false);
|
||||||
|
new Thread(() -> syncer.sync(lastSynclist.clone())).start();
|
||||||
|
});
|
||||||
|
|
||||||
|
syncPauseButton.addActionListener(e -> {
|
||||||
|
syncer.setPaused(!syncer.isPaused());
|
||||||
|
syncPauseButton.setEnabled(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
twitterIcon.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void mouseEntered(MouseEvent e) {
|
||||||
repoTree.collapseAllNodes();
|
twitterIcon.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited(MouseEvent e) {
|
||||||
|
twitterIcon.setBorder(new EmptyBorder(2,2,2,2));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
new Thread(() -> {
|
githubIcon.addMouseListener(new MouseAdapter() {
|
||||||
RepositoryManger.getInstance().refreshMeta();
|
|
||||||
try {
|
|
||||||
Thread.sleep(750);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
RepositoryManger.getInstance().refreshModset();
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
syncCheckButton.addActionListener(new ActionListener() {
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void mouseEntered(MouseEvent e) {
|
||||||
syncCheckButton.setEnabled(false);
|
githubIcon.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
|
||||||
syncCheckAbortButton.setEnabled(true);
|
}
|
||||||
syncCheckStatusLabel.setText("Running!");
|
|
||||||
new Thread(() -> fileChecker.check()).start();
|
|
||||||
|
|
||||||
repoTree.setCheckboxesEnabled(false);
|
@Override
|
||||||
repoTree.setCheckboxesChecked(false);
|
public void mouseExited(MouseEvent e) {
|
||||||
|
githubIcon.setBorder(new EmptyBorder(2,2,2,2));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
syncCheckAbortButton.addActionListener(new ActionListener() {
|
aboutLabel.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
fileChecker.stop();
|
tabbedPane1.setSelectedIndex(5);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
syncDownloadButton.addActionListener(new ActionListener() {
|
twitterIcon.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
if(lastSynclist == null) return;
|
openURL(ArmA3Launcher.config.getString("social.twitter"));
|
||||||
new Thread(() -> syncer.sync(lastSynclist.clone())).start();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
syncDownloadAbortButton.addActionListener(new ActionListener() {
|
githubIcon.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
syncer.stop();
|
openURL(ArmA3Launcher.config.getString("social.github"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
syncPauseButton.addActionListener(new ActionListener() {
|
aboutDeveloperLabel.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
syncer.setPaused(!syncer.isPaused());
|
openURL("https://gurkengewuerz.de");
|
||||||
syncPauseButton.setEnabled(false);
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
aboutProjectLabel.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
openURL(ArmA3Launcher.config.getString("social.github"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -377,6 +440,7 @@ public class LauncherGUI implements Observer {
|
|||||||
playPresetButton.setEnabled(false);
|
playPresetButton.setEnabled(false);
|
||||||
syncCheckButton.setEnabled(false);
|
syncCheckButton.setEnabled(false);
|
||||||
refreshRepoButton.setEnabled(false);
|
refreshRepoButton.setEnabled(false);
|
||||||
|
syncDownloadButton.setEnabled(false);
|
||||||
|
|
||||||
playButton.setToolTipText(LangUtils.getInstance().getString("arma_running"));
|
playButton.setToolTipText(LangUtils.getInstance().getString("arma_running"));
|
||||||
playPresetButton.setToolTipText(LangUtils.getInstance().getString("arma_running"));
|
playPresetButton.setToolTipText(LangUtils.getInstance().getString("arma_running"));
|
||||||
@ -408,11 +472,15 @@ public class LauncherGUI implements Observer {
|
|||||||
syncCheckButton.setEnabled(true);
|
syncCheckButton.setEnabled(true);
|
||||||
refreshRepoButton.setEnabled(true);
|
refreshRepoButton.setEnabled(true);
|
||||||
|
|
||||||
|
syncDownloadButton.setEnabled(fileChecker.isChecked());
|
||||||
|
|
||||||
syncCheckButton.setToolTipText(null);
|
syncCheckButton.setToolTipText(null);
|
||||||
refreshRepoButton.setToolTipText(null);
|
refreshRepoButton.setToolTipText(null);
|
||||||
} else {
|
} else {
|
||||||
syncCheckButton.setEnabled(true);
|
syncCheckButton.setEnabled(false);
|
||||||
refreshRepoButton.setEnabled(true);
|
refreshRepoButton.setEnabled(false);
|
||||||
|
|
||||||
|
syncDownloadButton.setEnabled(false);
|
||||||
|
|
||||||
syncCheckButton.setToolTipText(LangUtils.getInstance().getString("path_not_set"));
|
syncCheckButton.setToolTipText(LangUtils.getInstance().getString("path_not_set"));
|
||||||
refreshRepoButton.setToolTipText(LangUtils.getInstance().getString("path_not_set"));
|
refreshRepoButton.setToolTipText(LangUtils.getInstance().getString("path_not_set"));
|
||||||
@ -655,7 +723,7 @@ public class LauncherGUI implements Observer {
|
|||||||
RepositoryTreeNode lastNode = modFolder;
|
RepositoryTreeNode lastNode = modFolder;
|
||||||
ArrayList<String> path = modfile.getPath();
|
ArrayList<String> path = modfile.getPath();
|
||||||
|
|
||||||
for (int i = 0; i < path.size() -1; i++) {
|
for (int i = 0; i < path.size() - 1; i++) {
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
|
|
||||||
for (int j = 0; j < lastNode.getChildCount(); j++) {
|
for (int j = 0; j < lastNode.getChildCount(); j++) {
|
||||||
@ -703,11 +771,11 @@ public class LauncherGUI implements Observer {
|
|||||||
public void checkStateChanged(JCheckBoxTree.CheckChangeEvent event) {
|
public void checkStateChanged(JCheckBoxTree.CheckChangeEvent event) {
|
||||||
lastSynclist = getSyncList();
|
lastSynclist = getSyncList();
|
||||||
if (lastSynclist.getSize() != 0)
|
if (lastSynclist.getSize() != 0)
|
||||||
syncSizeLabel.setText(Humanize.binaryPrefix(lastSynclist.getSize()));
|
syncSizeLabel.setText("0.0 B/" + Humanize.binaryPrefix(lastSynclist.getSize()));
|
||||||
else syncSizeLabel.setText("0.0 B");
|
else syncSizeLabel.setText("0.0 B/0.0 B");
|
||||||
if (lastSynclist.getCount() != 0) {
|
if (lastSynclist.getCount() != 0) {
|
||||||
syncDownloadButton.setEnabled(true);
|
syncDownloadButton.setEnabled(true);
|
||||||
syncFileCountLabel.setText("" + lastSynclist.getCount());
|
syncFileCountLabel.setText("0/" + lastSynclist.getCount());
|
||||||
} else {
|
} else {
|
||||||
syncDownloadButton.setEnabled(false);
|
syncDownloadButton.setEnabled(false);
|
||||||
syncFileCountLabel.setText("");
|
syncFileCountLabel.setText("");
|
||||||
@ -838,6 +906,17 @@ public class LauncherGUI implements Observer {
|
|||||||
refreshRepoButton.setEnabled(false);
|
refreshRepoButton.setEnabled(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (s.equals(RepositoryManger.Type.CHANGELOG.toString())) {
|
||||||
|
if (RepositoryManger.getInstance().getStatus(RepositoryManger.Type.CHANGELOG) == DownloadStatus.FINNISHED) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
changelogPane.setText(Changelog.get());
|
||||||
|
changelogPane.setCaretPosition(0);
|
||||||
|
changelogPane.setLineWrap(true);
|
||||||
|
changelogPane.setWrapStyleWord(true);
|
||||||
|
changelogPane.revalidate();
|
||||||
|
changelogPane.repaint();
|
||||||
|
});
|
||||||
|
}
|
||||||
} else if (s.equals("fileChecker")) {
|
} else if (s.equals("fileChecker")) {
|
||||||
syncCheckButton.setEnabled(true);
|
syncCheckButton.setEnabled(true);
|
||||||
syncCheckAbortButton.setEnabled(false);
|
syncCheckAbortButton.setEnabled(false);
|
||||||
@ -867,26 +946,70 @@ public class LauncherGUI implements Observer {
|
|||||||
syncPauseButton.setEnabled(false);
|
syncPauseButton.setEnabled(false);
|
||||||
|
|
||||||
repoTree.setCheckboxesChecked(false);
|
repoTree.setCheckboxesChecked(false);
|
||||||
|
refreshRepoButton.setEnabled(true);
|
||||||
|
|
||||||
syncAddedFilesLabel.setText("" + 0);
|
syncAddedFilesLabel.setText("" + 0);
|
||||||
syncChangedFilesLabel.setText("" + 0);
|
syncChangedFilesLabel.setText("" + 0);
|
||||||
syncDeletedFilesLabel.setText("" + 0);
|
syncDeletedFilesLabel.setText("" + 0);
|
||||||
|
|
||||||
syncChangedFileSizeLabel.setText("0.0 B");
|
syncChangedFileSizeLabel.setText("0.0 B");
|
||||||
|
|
||||||
} else if (s.equals("syncStopped")) {
|
} else if (s.equals("syncStopped")) {
|
||||||
new Thread(() -> fileChecker.check()).start();
|
new Thread(() -> fileChecker.check()).start();
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
syncDownloadButton.setEnabled(false);
|
||||||
|
syncDownloadAbortButton.setEnabled(false);
|
||||||
|
syncPauseButton.setEnabled(false);
|
||||||
|
|
||||||
|
syncStatusLabel.setText("Sync stopped");
|
||||||
|
syncFileProgress.setValue(0);
|
||||||
|
TaskBarUtils.getInstance().setValue(0);
|
||||||
|
TaskBarUtils.getInstance().off();
|
||||||
|
});
|
||||||
} else if (s.equals("syncComplete")) {
|
} else if (s.equals("syncComplete")) {
|
||||||
new Thread(() -> fileChecker.check()).start();
|
new Thread(() -> fileChecker.check()).start();
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
syncDownloadButton.setEnabled(false);
|
||||||
|
syncDownloadAbortButton.setEnabled(false);
|
||||||
|
syncPauseButton.setEnabled(false);
|
||||||
|
|
||||||
|
syncStatusLabel.setText("Sync finished");
|
||||||
|
syncFileProgress.setValue(0);
|
||||||
|
syncFileProgress.setString("");
|
||||||
|
TaskBarUtils.getInstance().setValue(0);
|
||||||
|
TaskBarUtils.getInstance().off();
|
||||||
|
TaskBarUtils.getInstance().attention();
|
||||||
|
TaskBarUtils.getInstance().notification("Sync complete", "", TrayIcon.MessageType.INFO);
|
||||||
|
});
|
||||||
} else if (s.equals("syncContinue")) {
|
} else if (s.equals("syncContinue")) {
|
||||||
syncDownloadAbortButton.setEnabled(true);
|
SwingUtilities.invokeLater(() -> {
|
||||||
syncPauseButton.setEnabled(true);
|
syncDownloadAbortButton.setEnabled(true);
|
||||||
syncPauseButton.setText(LangUtils.getInstance().getString("pause"));
|
syncPauseButton.setEnabled(true);
|
||||||
syncDownloadButton.setEnabled(false);
|
syncPauseButton.setText(LangUtils.getInstance().getString("pause"));
|
||||||
|
syncDownloadButton.setEnabled(false);
|
||||||
|
TaskBarUtils.getInstance().normal();
|
||||||
|
});
|
||||||
} else if (s.equals("syncPaused")) {
|
} else if (s.equals("syncPaused")) {
|
||||||
syncDownloadAbortButton.setEnabled(true);
|
SwingUtilities.invokeLater(() -> {
|
||||||
syncPauseButton.setEnabled(true);
|
syncDownloadAbortButton.setEnabled(true);
|
||||||
syncPauseButton.setText(LangUtils.getInstance().getString("resume"));
|
syncPauseButton.setEnabled(true);
|
||||||
syncDownloadButton.setEnabled(false);
|
syncPauseButton.setText(LangUtils.getInstance().getString("resume"));
|
||||||
|
syncDownloadButton.setEnabled(false);
|
||||||
|
syncFileProgress.setValue(0);
|
||||||
|
TaskBarUtils.getInstance().paused();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exit() {
|
||||||
|
fileChecker.stop();
|
||||||
|
syncer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void openURL(String url) {
|
||||||
|
try {
|
||||||
|
Desktop.getDesktop().browse(new URL(url).toURI());
|
||||||
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
29
src/main/java/de/mc8051/arma3launcher/objects/Changelog.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package de.mc8051.arma3launcher.objects;
|
||||||
|
|
||||||
|
import de.mc8051.arma3launcher.repo.RepositoryManger;
|
||||||
|
import de.mc8051.arma3launcher.utils.Callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by gurkengewuerz.de on 27.03.2020.
|
||||||
|
*/
|
||||||
|
public class Changelog {
|
||||||
|
|
||||||
|
private static long lastUpdate = 0;
|
||||||
|
private static String cache = "";
|
||||||
|
|
||||||
|
|
||||||
|
public static void refresh() {
|
||||||
|
if(cache.isEmpty() || System.currentTimeMillis() - lastUpdate > 5 * 60 * 1000) { // 5 Minuten
|
||||||
|
RepositoryManger.getInstance().refreshChangelog();
|
||||||
|
lastUpdate = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String get() {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setChangelog(String changelog) {
|
||||||
|
cache = changelog;
|
||||||
|
}
|
||||||
|
}
|
@ -79,6 +79,10 @@ public class ModFile implements AbstractMod {
|
|||||||
return modfileString;
|
return modfileString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getModPath() {
|
||||||
|
return (parent == null ? "" : parent + "/") + modfileString;
|
||||||
|
}
|
||||||
|
|
||||||
public File getLocaleFile() {
|
public File getLocaleFile() {
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ public class FileChecker implements Observable {
|
|||||||
private JProgressBar pb;
|
private JProgressBar pb;
|
||||||
private boolean stop = false;
|
private boolean stop = false;
|
||||||
|
|
||||||
|
private boolean checked = false;
|
||||||
|
|
||||||
private ArrayList<Path> deleted = new ArrayList<>();
|
private ArrayList<Path> deleted = new ArrayList<>();
|
||||||
private HashMap<String, ArrayList<ModFile>> changed = new HashMap<>();
|
private HashMap<String, ArrayList<ModFile>> changed = new HashMap<>();
|
||||||
int changedCount = 0;
|
int changedCount = 0;
|
||||||
@ -93,6 +95,11 @@ public class FileChecker implements Observable {
|
|||||||
|
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
notifyObservers("fileChecker");
|
notifyObservers("fileChecker");
|
||||||
|
checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChecked() {
|
||||||
|
return checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
|
@ -4,6 +4,7 @@ import de.mc8051.arma3launcher.ArmA3Launcher;
|
|||||||
import de.mc8051.arma3launcher.interfaces.Observable;
|
import de.mc8051.arma3launcher.interfaces.Observable;
|
||||||
import de.mc8051.arma3launcher.interfaces.Observer;
|
import de.mc8051.arma3launcher.interfaces.Observer;
|
||||||
import de.mc8051.arma3launcher.objects.AbstractMod;
|
import de.mc8051.arma3launcher.objects.AbstractMod;
|
||||||
|
import de.mc8051.arma3launcher.objects.Changelog;
|
||||||
import de.mc8051.arma3launcher.objects.Mod;
|
import de.mc8051.arma3launcher.objects.Mod;
|
||||||
import de.mc8051.arma3launcher.objects.ModFile;
|
import de.mc8051.arma3launcher.objects.ModFile;
|
||||||
import de.mc8051.arma3launcher.objects.Modset;
|
import de.mc8051.arma3launcher.objects.Modset;
|
||||||
@ -45,6 +46,7 @@ public class RepositoryManger implements Observable {
|
|||||||
private RepositoryManger() {
|
private RepositoryManger() {
|
||||||
statusMap.put(Type.METADATA, DownloadStatus.FINNISHED);
|
statusMap.put(Type.METADATA, DownloadStatus.FINNISHED);
|
||||||
statusMap.put(Type.MODSET, DownloadStatus.FINNISHED);
|
statusMap.put(Type.MODSET, DownloadStatus.FINNISHED);
|
||||||
|
statusMap.put(Type.CHANGELOG, DownloadStatus.FINNISHED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getAsync(String urlS, Callback.HttpCallback callback) {
|
private void getAsync(String urlS, Callback.HttpCallback callback) {
|
||||||
@ -55,7 +57,7 @@ public class RepositoryManger implements Observable {
|
|||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
.uri(url)
|
.uri(url)
|
||||||
.GET()
|
.GET()
|
||||||
.headers("Content-Type", "text/plain;charset=UTF-8")
|
.headers("User-Agent", ArmA3Launcher.USER_AGENT)
|
||||||
.timeout(Duration.of(3, SECONDS))
|
.timeout(Duration.of(3, SECONDS))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -191,6 +193,26 @@ public class RepositoryManger implements Observable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void refreshChangelog() {
|
||||||
|
statusMap.replace(Type.CHANGELOG, DownloadStatus.RUNNING);
|
||||||
|
RepositoryManger.getInstance().notifyObservers(Type.CHANGELOG.toString());
|
||||||
|
|
||||||
|
getAsync(ArmA3Launcher.config.getString("sync.url") + "/.sync/changelog.txt", new Callback.HttpCallback() {
|
||||||
|
@Override
|
||||||
|
public void response(Response r) {
|
||||||
|
if (!r.isSuccessful()) {
|
||||||
|
statusMap.replace(Type.CHANGELOG, DownloadStatus.ERROR);
|
||||||
|
RepositoryManger.getInstance().notifyObservers(Type.CHANGELOG.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
statusMap.replace(Type.CHANGELOG, DownloadStatus.FINNISHED);
|
||||||
|
RepositoryManger.getInstance().notifyObservers(Type.CHANGELOG.toString());
|
||||||
|
Changelog.setChangelog(r.getBody());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static RepositoryManger getInstance() {
|
public static RepositoryManger getInstance() {
|
||||||
if (instance == null) instance = new RepositoryManger();
|
if (instance == null) instance = new RepositoryManger();
|
||||||
return instance;
|
return instance;
|
||||||
@ -218,7 +240,8 @@ public class RepositoryManger implements Observable {
|
|||||||
public enum Type {
|
public enum Type {
|
||||||
|
|
||||||
METADATA("metadata"),
|
METADATA("metadata"),
|
||||||
MODSET("modset");
|
MODSET("modset"),
|
||||||
|
CHANGELOG("changelog");
|
||||||
|
|
||||||
private String modset;
|
private String modset;
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import de.mc8051.arma3launcher.interfaces.Observer;
|
|||||||
import de.mc8051.arma3launcher.objects.AbstractMod;
|
import de.mc8051.arma3launcher.objects.AbstractMod;
|
||||||
import de.mc8051.arma3launcher.objects.ModFile;
|
import de.mc8051.arma3launcher.objects.ModFile;
|
||||||
import de.mc8051.arma3launcher.utils.Humanize;
|
import de.mc8051.arma3launcher.utils.Humanize;
|
||||||
|
import de.mc8051.arma3launcher.utils.TaskBarUtils;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -36,17 +37,19 @@ public class Syncer extends ZsyncObserver implements Observable {
|
|||||||
private SyncList modlist;
|
private SyncList modlist;
|
||||||
|
|
||||||
private boolean currentDownload_failed = false;
|
private boolean currentDownload_failed = false;
|
||||||
|
private String currentDownload_sizeS;
|
||||||
private boolean controlfile_downloaded = false;
|
private boolean controlfile_downloaded = false;
|
||||||
private int failed = 0;
|
private int failed = 0;
|
||||||
private int success = 0;
|
private int success = 0;
|
||||||
|
|
||||||
long syncSize;
|
private long syncSize;
|
||||||
int syncCount;
|
private String syncSizeString;
|
||||||
|
private int syncCount;
|
||||||
|
|
||||||
long downloadStarted;
|
private long downloadStarted;
|
||||||
long downloadEnded;
|
private long downloadEnded;
|
||||||
long downloadSize;
|
private long downloadSize;
|
||||||
long downloadDownloaded;
|
private long downloadDownloaded;
|
||||||
|
|
||||||
private Zsync zsync;
|
private Zsync zsync;
|
||||||
private LauncherGUI gui;
|
private LauncherGUI gui;
|
||||||
@ -69,10 +72,12 @@ public class Syncer extends ZsyncObserver implements Observable {
|
|||||||
success = 0;
|
success = 0;
|
||||||
|
|
||||||
syncSize = ml.getSize();
|
syncSize = ml.getSize();
|
||||||
|
syncSizeString = Humanize.binaryPrefix(syncSize);
|
||||||
syncCount = ml.getCount();
|
syncCount = ml.getCount();
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
gui.syncDownloadProgress.setMaximum(syncCount);
|
gui.syncDownloadProgress.setMaximum(syncCount);
|
||||||
gui.syncDownloadProgress.setValue(0);
|
gui.syncDownloadProgress.setValue(0);
|
||||||
|
TaskBarUtils.getInstance().normal();
|
||||||
});
|
});
|
||||||
|
|
||||||
boolean lastPause = false;
|
boolean lastPause = false;
|
||||||
@ -120,11 +125,13 @@ public class Syncer extends ZsyncObserver implements Observable {
|
|||||||
if (mf != null) {
|
if (mf != null) {
|
||||||
Zsync.Options o = new Zsync.Options();
|
Zsync.Options o = new Zsync.Options();
|
||||||
o.setOutputFile(Paths.get(mf.getLocaleFile().getAbsolutePath()));
|
o.setOutputFile(Paths.get(mf.getLocaleFile().getAbsolutePath()));
|
||||||
|
o.setUseragent(ArmA3Launcher.USER_AGENT);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
currentDownload = mf;
|
currentDownload = mf;
|
||||||
currentDownload_failed = false;
|
currentDownload_failed = false;
|
||||||
controlfile_downloaded = false;
|
controlfile_downloaded = false;
|
||||||
|
currentDownload_sizeS = Humanize.binaryPrefix(currentDownload.getSize());
|
||||||
|
|
||||||
zsync.zsync(URI.create(mf.getRemoteFile() + ".zsync"), o, this);
|
zsync.zsync(URI.create(mf.getRemoteFile() + ".zsync"), o, this);
|
||||||
} catch (ZsyncException | IllegalArgumentException e) {
|
} catch (ZsyncException | IllegalArgumentException e) {
|
||||||
@ -178,14 +185,14 @@ public class Syncer extends ZsyncObserver implements Observable {
|
|||||||
@Override
|
@Override
|
||||||
public void zsyncStarted(URI requestedZsyncUri, Zsync.Options options) {
|
public void zsyncStarted(URI requestedZsyncUri, Zsync.Options options) {
|
||||||
super.zsyncStarted(requestedZsyncUri, options);
|
super.zsyncStarted(requestedZsyncUri, options);
|
||||||
System.out.println("ZSync started " + options.getOutputFile());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
SwingUtilities.invokeLater(() -> {
|
||||||
public void controlFileDownloadingComplete() {
|
gui.syncFileProgress.setValue(0);
|
||||||
super.controlFileDownloadingComplete();
|
gui.syncFileProgress.setString("0 %");
|
||||||
System.out.println("controlFileDownloadingComplete");
|
});
|
||||||
controlfile_downloaded = true;
|
|
||||||
|
System.out.println("ZSync started " + options.getOutputFile());
|
||||||
|
SwingUtilities.invokeLater(() -> gui.syncStatusLabel.setText(currentDownload.getModPath() + ": Sync started"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -193,6 +200,7 @@ public class Syncer extends ZsyncObserver implements Observable {
|
|||||||
super.zsyncFailed(exception);
|
super.zsyncFailed(exception);
|
||||||
currentDownload_failed = true;
|
currentDownload_failed = true;
|
||||||
System.out.println("Zsync failed " + exception.getMessage());
|
System.out.println("Zsync failed " + exception.getMessage());
|
||||||
|
SwingUtilities.invokeLater(() -> gui.syncStatusLabel.setText(currentDownload.getModPath() + ": Sync failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -211,13 +219,22 @@ public class Syncer extends ZsyncObserver implements Observable {
|
|||||||
else success++;
|
else success++;
|
||||||
|
|
||||||
final long finalSize = syncSize - modlist.getSize();
|
final long finalSize = syncSize - modlist.getSize();
|
||||||
int i = success + failed;
|
final int i = success + failed;
|
||||||
int percentage = (int) ((double)i / (double)Long.valueOf(syncCount).intValue() * 100);
|
final int percentage = (int) ((double) i / (double) Long.valueOf(syncCount).intValue() * 100);
|
||||||
|
final String modPath = currentDownload.getModPath();
|
||||||
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
gui.syncDownloadProgress.setValue(i);
|
gui.syncDownloadProgress.setValue(i);
|
||||||
gui.syncDownloadedLabel.setText(Humanize.binaryPrefix(finalSize) + " " + " (" + failed + " failed)");
|
gui.syncFileCountLabel.setText(i + "/" + syncCount + " (" + failed + " failed)");
|
||||||
gui.syncDownloadProgress.setString(percentage + "%");
|
gui.syncSizeLabel.setText(Humanize.binaryPrefix(finalSize) + "/" + syncSizeString);
|
||||||
|
|
||||||
|
if (currentDownload_failed)
|
||||||
|
gui.syncStatusLabel.setText(modPath + ": Sync failed");
|
||||||
|
else
|
||||||
|
gui.syncStatusLabel.setText(modPath + ": Sync finished");
|
||||||
|
|
||||||
|
gui.syncDownloadProgress.setString(percentage + " %");
|
||||||
|
TaskBarUtils.getInstance().setValue(percentage);
|
||||||
});
|
});
|
||||||
|
|
||||||
finnishCurrent();
|
finnishCurrent();
|
||||||
@ -227,6 +244,16 @@ public class Syncer extends ZsyncObserver implements Observable {
|
|||||||
public void controlFileDownloadingStarted(URI uri, long length) {
|
public void controlFileDownloadingStarted(URI uri, long length) {
|
||||||
super.controlFileDownloadingStarted(uri, length);
|
super.controlFileDownloadingStarted(uri, length);
|
||||||
System.out.println("controlFileDownloadingStarted " + length);
|
System.out.println("controlFileDownloadingStarted " + length);
|
||||||
|
SwingUtilities.invokeLater(() -> gui.syncStatusLabel.setText(currentDownload.getModPath() + ": Get Header"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void controlFileDownloadingComplete() {
|
||||||
|
super.controlFileDownloadingComplete();
|
||||||
|
System.out.println("controlFileDownloadingComplete");
|
||||||
|
controlfile_downloaded = true;
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(() -> gui.syncStatusLabel.setText(currentDownload.getModPath() + ": Hashing"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -234,26 +261,23 @@ public class Syncer extends ZsyncObserver implements Observable {
|
|||||||
super.remoteFileDownloadingStarted(uri, length);
|
super.remoteFileDownloadingStarted(uri, length);
|
||||||
System.out.println("remoteFileDownloadingStarted " + length);
|
System.out.println("remoteFileDownloadingStarted " + length);
|
||||||
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
gui.syncFileProgress.setMaximum(Long.valueOf(length).intValue());
|
|
||||||
gui.syncFileProgress.setValue(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
downloadSize = length;
|
downloadSize = length;
|
||||||
downloadDownloaded = 0;
|
downloadDownloaded = 0;
|
||||||
downloadStarted = System.nanoTime();
|
downloadStarted = System.nanoTime();
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(() -> gui.syncStatusLabel.setText(currentDownload.getModPath() + ": Downloading"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bytesDownloaded(long bytes) {
|
public void bytesDownloaded(long bytes) {
|
||||||
super.bytesDownloaded(bytes);
|
super.bytesDownloaded(bytes);
|
||||||
// System.out.println("Downloaded " + bytes);
|
|
||||||
downloadDownloaded += bytes;
|
downloadDownloaded += bytes;
|
||||||
|
|
||||||
// TODO: Fix file Download Progress
|
|
||||||
if (controlfile_downloaded) {
|
if (controlfile_downloaded) {
|
||||||
|
final int percentage = (int) (((double) downloadDownloaded / (double) downloadSize) * 100);
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
gui.syncFileProgress.setValue(Long.valueOf(downloadDownloaded).intValue());
|
gui.syncFileProgress.setValue(percentage);
|
||||||
|
gui.syncFileProgress.setString(percentage + " % " + Humanize.binaryPrefix(downloadDownloaded) + "/" + currentDownload_sizeS);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,15 @@ import java.io.File;
|
|||||||
*/
|
*/
|
||||||
public class Callback {
|
public class Callback {
|
||||||
|
|
||||||
public static interface JFileSelectCallback { //declare an interface with the callback methods, so you can use on more than one class and just refer to the interface
|
public interface JFileSelectCallback {
|
||||||
boolean allowSelection(File path);
|
boolean allowSelection(File path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface HttpCallback {
|
public interface HttpCallback {
|
||||||
void response(Response r);
|
void response(Response r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface ChangelogCallback {
|
||||||
|
void response(String changelog);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ public class Humanize {
|
|||||||
final long[] dividers = new long[] { T, G, M, K, 1 };
|
final long[] dividers = new long[] { T, G, M, K, 1 };
|
||||||
final String[] units = new String[] { "TB", "GB", "MB", "KB", "B" };
|
final String[] units = new String[] { "TB", "GB", "MB", "KB", "B" };
|
||||||
if(value < 1)
|
if(value < 1)
|
||||||
throw new IllegalArgumentException("Invalid file size: " + value);
|
return "0.0 B";
|
||||||
String result = null;
|
String result = null;
|
||||||
for(int i = 0; i < dividers.length; i++){
|
for(int i = 0; i < dividers.length; i++){
|
||||||
final long divider = dividers[i];
|
final long divider = dividers[i];
|
||||||
|
21
src/main/java/de/mc8051/arma3launcher/utils/ImageUtils.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package de.mc8051.arma3launcher.utils;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by gurkengewuerz.de on 27.03.2020.
|
||||||
|
*/
|
||||||
|
public class ImageUtils {
|
||||||
|
|
||||||
|
public static Image getScaledImage(Image srcImg, int w, int h){
|
||||||
|
BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
Graphics2D g2 = resizedImg.createGraphics();
|
||||||
|
|
||||||
|
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||||
|
g2.drawImage(srcImg, 0, 0, w, h, null);
|
||||||
|
g2.dispose();
|
||||||
|
|
||||||
|
return resizedImg;
|
||||||
|
}
|
||||||
|
}
|
143
src/main/java/de/mc8051/arma3launcher/utils/TaskBarUtils.java
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package de.mc8051.arma3launcher.utils;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by gurkengewuerz.de on 27.03.2020.
|
||||||
|
*/
|
||||||
|
public class TaskBarUtils {
|
||||||
|
|
||||||
|
public static BufferedImage IMAGE_ICON = createIcon();
|
||||||
|
public static BufferedImage IMAGE_LGO = createLogo();
|
||||||
|
|
||||||
|
private static TaskBarUtils instance;
|
||||||
|
|
||||||
|
private final boolean isTaskbarSupported;
|
||||||
|
private final boolean isSystemtraySupported;
|
||||||
|
private Taskbar taskbar;
|
||||||
|
private SystemTray tray;
|
||||||
|
private TrayIcon trayIcon;
|
||||||
|
private Window w;
|
||||||
|
|
||||||
|
private TaskBarUtils() {
|
||||||
|
isTaskbarSupported = Taskbar.isTaskbarSupported();
|
||||||
|
if (isTaskbarSupported) {
|
||||||
|
taskbar = Taskbar.getTaskbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
isSystemtraySupported = SystemTray.isSupported();
|
||||||
|
if (isSystemtraySupported) {
|
||||||
|
tray = SystemTray.getSystemTray();
|
||||||
|
|
||||||
|
try {
|
||||||
|
trayIcon = new TrayIcon(IMAGE_ICON);
|
||||||
|
trayIcon.setImageAutoSize(true);
|
||||||
|
tray.add(trayIcon);
|
||||||
|
trayIcon.addActionListener(e -> {
|
||||||
|
if (w == null) return;
|
||||||
|
if(!(w instanceof JFrame)) return;
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
JFrame frame = (JFrame) w;
|
||||||
|
if(frame.getState()!=Frame.NORMAL) { frame.setState(Frame.NORMAL); }
|
||||||
|
frame.setVisible(true);
|
||||||
|
frame.setAlwaysOnTop(true);
|
||||||
|
frame.toFront();
|
||||||
|
frame.requestFocus();
|
||||||
|
frame.setAlwaysOnTop(false);
|
||||||
|
frame.repaint();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (AWTException e) {
|
||||||
|
Logger.getLogger(TaskBarUtils.class.getName()).log(Level.SEVERE, null, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TaskBarUtils getInstance() {
|
||||||
|
if (instance == null) instance = new TaskBarUtils();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(Window w) {
|
||||||
|
if (w == null) return;
|
||||||
|
if (!isTaskbarSupported) return;
|
||||||
|
if (!taskbar.isSupported(Taskbar.Feature.PROGRESS_STATE_WINDOW)) return;
|
||||||
|
taskbar.setWindowProgressState(w, Taskbar.State.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void normal() {
|
||||||
|
if (w == null) return;
|
||||||
|
if (!isTaskbarSupported) return;
|
||||||
|
if (!taskbar.isSupported(Taskbar.Feature.PROGRESS_STATE_WINDOW)) return;
|
||||||
|
taskbar.setWindowProgressState(w, Taskbar.State.NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void off() {
|
||||||
|
if (w == null) return;
|
||||||
|
if (!isTaskbarSupported) return;
|
||||||
|
if (!taskbar.isSupported(Taskbar.Feature.PROGRESS_STATE_WINDOW)) return;
|
||||||
|
taskbar.setWindowProgressState(w, Taskbar.State.OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void paused() {
|
||||||
|
if (w == null) return;
|
||||||
|
if (!isTaskbarSupported) return;
|
||||||
|
if (!taskbar.isSupported(Taskbar.Feature.PROGRESS_STATE_WINDOW)) return;
|
||||||
|
taskbar.setWindowProgressState(w, Taskbar.State.PAUSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(int val) {
|
||||||
|
if (w == null) return;
|
||||||
|
if (!isTaskbarSupported) return;
|
||||||
|
if (!taskbar.isSupported(Taskbar.Feature.PROGRESS_VALUE_WINDOW)) return;
|
||||||
|
taskbar.setWindowProgressValue(w, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attention() {
|
||||||
|
if (w == null) return;
|
||||||
|
if (!isTaskbarSupported) return;
|
||||||
|
if (!taskbar.isSupported(Taskbar.Feature.USER_ATTENTION_WINDOW)) return;
|
||||||
|
taskbar.requestWindowUserAttention(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notification(String caption, String text, TrayIcon.MessageType type) {
|
||||||
|
if (!isSystemtraySupported) return;
|
||||||
|
if (trayIcon == null) return;
|
||||||
|
|
||||||
|
trayIcon.displayMessage(caption, text, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWindow(Window w) {
|
||||||
|
this.w = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTrayIcon() {
|
||||||
|
if (!isSystemtraySupported) return;
|
||||||
|
if (trayIcon == null) return;
|
||||||
|
tray.remove(trayIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BufferedImage createIcon() {
|
||||||
|
try {
|
||||||
|
return ImageIO.read(TaskBarUtils.class.getResourceAsStream("/icons/logo_32.png"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.getLogger(TaskBarUtils.class.getName()).log(Level.SEVERE, null, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BufferedImage createLogo() {
|
||||||
|
try {
|
||||||
|
return ImageIO.read(TaskBarUtils.class.getResourceAsStream("/icons/logo_256.png"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.getLogger(TaskBarUtils.class.getName()).log(Level.SEVERE, null, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,13 @@
|
|||||||
"title": "Welcome! :P",
|
"title": "Welcome! :P",
|
||||||
"subtitle": "${name} v${version}",
|
"subtitle": "${name} v${version}",
|
||||||
"sync": {
|
"sync": {
|
||||||
|
"useragent": "TheTownSyncer",
|
||||||
"url": "http://46.4.195.36"
|
"url": "http://46.4.195.36"
|
||||||
},
|
},
|
||||||
|
"social": {
|
||||||
|
"twitter": "https://twitter.com/TheTownServer",
|
||||||
|
"github": "https://github.com/Gurkengewuerz/arma3launcher"
|
||||||
|
},
|
||||||
"client": {
|
"client": {
|
||||||
"armaPath": "",
|
"armaPath": "",
|
||||||
"modPath": "",
|
"modPath": "",
|
||||||
|
189
src/main/resources/disclaimer.html
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Haftungsausschluss</h1>
|
||||||
|
Gemäß <i>§1 Absatz 2 Satz 3 ProdHaftG</i> haftet das Entwickler-Team nicht für Schäden,
|
||||||
|
die aus der Nutzung des Clients entstehen, da dieser unentgeltlich angeboten und ehrenamtlich entwickelt wird. Das
|
||||||
|
Entwickler-Team übernimmt darüber hinaus keine Garantie für die ordnungsgemäße Funktion des Clients.<br/><br/>
|
||||||
|
Darausfolgt auch, dass es keinen Anspruch darauf gibt, dass der Client auf jedem System funktioniert oder Programmfehler
|
||||||
|
durch
|
||||||
|
Updates behoben werden. Wir bemühen uns zwar, ein fehlerfreies Programm anzubieten, bitten aber um Verständnis, dass
|
||||||
|
nicht jeder Fehler unsererseits behoben werden kann.
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<h1>Icons</h1>
|
||||||
|
<div>
|
||||||
|
Icons made by <a href="https://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a> from
|
||||||
|
<a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Icons made by <a href="https://www.flaticon.com/authors/pixel-perfect" title="Pixel perfect">Pixel perfect</a> from
|
||||||
|
<a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Icons made by <a href="https://www.flaticon.com/authors/roundicons" title="Roundicons">Roundicons</a> from
|
||||||
|
<a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<h1>Licenses</h1>
|
||||||
|
<h3>de.mc8051.arma3launcher</h3>
|
||||||
|
<tt>
|
||||||
|
MIT License<br/>
|
||||||
|
<br/>
|
||||||
|
Copyright (c) 2020 Niklas Schütrumpf (Gurkengewuerz)<br/>
|
||||||
|
<br/>
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy<br/>
|
||||||
|
of this software and associated documentation files (the "Software"), to deal<br/>
|
||||||
|
in the Software without restriction, including without limitation the rights<br/>
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell<br/>
|
||||||
|
copies of the Software, and to permit persons to whom the Software is<br/>
|
||||||
|
furnished to do so, subject to the following conditions:<br/>
|
||||||
|
<br/>
|
||||||
|
The above copyright notice and this permission notice shall be included in all<br/>
|
||||||
|
copies or substantial portions of the Software.<br/>
|
||||||
|
<br/>
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br/>
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br/>
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE<br/>
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br/>
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,<br/>
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE<br/>
|
||||||
|
SOFTWARE.<br/>
|
||||||
|
</tt>
|
||||||
|
<br/>
|
||||||
|
<h3>com.formdev.flatlaf</h3>
|
||||||
|
<a href="">https://github.com/JFormDesigner/FlatLaf</a><br/>
|
||||||
|
<tt>
|
||||||
|
Copyright 2019 FormDev Software GmbH<br/>
|
||||||
|
<br/>
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");<br/>
|
||||||
|
you may not use this file except in compliance with the License.<br/>
|
||||||
|
You may obtain a copy of the License at<br/>
|
||||||
|
<br/>
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0<br/>
|
||||||
|
<br/>
|
||||||
|
Unless required by applicable law or agreed to in writing, software<br/>
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,<br/>
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.<br/>
|
||||||
|
See the License for the specific language governing permissions and<br/>
|
||||||
|
limitations under the License.<br/>
|
||||||
|
</tt>
|
||||||
|
<br/>
|
||||||
|
<h3>co.bitshfted.xapps.zsync</h3>
|
||||||
|
<a href="">https://github.com/bitshifted/zsyncer</a><br/>
|
||||||
|
<tt>
|
||||||
|
Copyright (c) 2015, Salesforce.com, Inc. All rights reserved.<br/>
|
||||||
|
Copyright (c) 2020, Bitshift (bitshifted.co), Inc. All rights reserved.<br/>
|
||||||
|
<br/>
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the<br/>
|
||||||
|
following conditions are met:<br/>
|
||||||
|
<br/>
|
||||||
|
Redistributions of source code must retain the above copyright notice, this list of conditions and the following<br/>
|
||||||
|
disclaimer.<br/>
|
||||||
|
<br/>
|
||||||
|
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following<br/>
|
||||||
|
disclaimer in the documentation and/or other materials provided with the distribution.<br/>
|
||||||
|
<br/>
|
||||||
|
Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products<br/>
|
||||||
|
derived from this software without specific prior written permission.<br/>
|
||||||
|
<br/>
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,<br/>
|
||||||
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE<br/>
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,<br/>
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br/>
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,<br/>
|
||||||
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE<br/>
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.<br/>
|
||||||
|
</tt>
|
||||||
|
<br/>
|
||||||
|
<h3>com.github.RalleYTN.SimpleRegistry</h3>
|
||||||
|
<a href="">https://github.com/RalleYTN/SimpleRegistry</a><br/>
|
||||||
|
<tt>
|
||||||
|
MIT License<br/>
|
||||||
|
<br/>
|
||||||
|
Copyright (c) 2017 Ralph Niemitz<br/>
|
||||||
|
<br/>
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy<br/>
|
||||||
|
of this software and associated documentation files (the "Software"), to deal<br/>
|
||||||
|
in the Software without restriction, including without limitation the rights<br/>
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell<br/>
|
||||||
|
copies of the Software, and to permit persons to whom the Software is<br/>
|
||||||
|
furnished to do so, subject to the following conditions:<br/>
|
||||||
|
<br/>
|
||||||
|
The above copyright notice and this permission notice shall be included in all<br/>
|
||||||
|
copies or substantial portions of the Software.<br/>
|
||||||
|
<br/>
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br/>
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br/>
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE<br/>
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br/>
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,<br/>
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE<br/>
|
||||||
|
SOFTWARE.<br/>
|
||||||
|
</tt>
|
||||||
|
<br/>
|
||||||
|
<h3>com.typesafe.config</h3>
|
||||||
|
<a href="">https://github.com/lightbend/config</a><br/>
|
||||||
|
<tt>
|
||||||
|
Copyright (C) 2011-2012 Typesafe Inc. http://typesafe.com<br/>
|
||||||
|
<br/>
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");<br/>
|
||||||
|
you may not use this file except in compliance with the License.<br/>
|
||||||
|
You may obtain a copy of the License at<br/>
|
||||||
|
<br/>
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0<br/>
|
||||||
|
<br/>
|
||||||
|
Unless required by applicable law or agreed to in writing, software<br/>
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,<br/>
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.<br/>
|
||||||
|
See the License for the specific language governing permissions and<br/>
|
||||||
|
limitations under the License.<br/>
|
||||||
|
</tt>
|
||||||
|
<br/>
|
||||||
|
<h3>org.ini4j.ini4j</h3>
|
||||||
|
<a href="">https://github.com/facebookarchive/ini4j</a><br/>
|
||||||
|
<tt>
|
||||||
|
Copyright 2005,2009 Ivan SZKIBA<br/>
|
||||||
|
<br/>
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");<br/>
|
||||||
|
you may not use this file except in compliance with the License.<br/>
|
||||||
|
You may obtain a copy of the License at<br/>
|
||||||
|
<br/>
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0<br/>
|
||||||
|
<br/>
|
||||||
|
Unless required by applicable law or agreed to in writing, software<br/>
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,<br/>
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.<br/>
|
||||||
|
See the License for the specific language governing permissions and<br/>
|
||||||
|
limitations under the License.<br/>
|
||||||
|
</tt>
|
||||||
|
<h3>org.json.json</h3>
|
||||||
|
<a href="">https://json.org</a><br/>
|
||||||
|
<tt>
|
||||||
|
Copyright (c) 2018 JSON.org<br/>
|
||||||
|
<br/>
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy<br/>
|
||||||
|
of this software and associated documentation files (the "Software"), to deal<br/>
|
||||||
|
in the Software without restriction, including without limitation the rights<br/>
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell<br/>
|
||||||
|
copies of the Software, and to permit persons to whom the Software is<br/>
|
||||||
|
furnished to do so, subject to the following conditions:<br/>
|
||||||
|
<br/>
|
||||||
|
The above copyright notice and this permission notice shall be included in all<br/>
|
||||||
|
copies or substantial portions of the Software.<br/>
|
||||||
|
<br/>
|
||||||
|
The Software shall be used for Good, not Evil.<br/>
|
||||||
|
<br/>
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br/>
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br/>
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE<br/>
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br/>
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,<br/>
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE<br/>
|
||||||
|
SOFTWARE.<br/>
|
||||||
|
</tt>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
src/main/resources/icons/changelog_16.png
Normal file
After Width: | Height: | Size: 536 B |
BIN
src/main/resources/icons/download_16.png
Normal file
After Width: | Height: | Size: 476 B |
BIN
src/main/resources/icons/github_32.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/main/resources/icons/logo_256.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/main/resources/icons/logo_32.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
src/main/resources/icons/play_16.png
Normal file
After Width: | Height: | Size: 511 B |
BIN
src/main/resources/icons/preset_16.png
Normal file
After Width: | Height: | Size: 237 B |
BIN
src/main/resources/icons/settings_16.png
Normal file
After Width: | Height: | Size: 566 B |
BIN
src/main/resources/icons/twitter_32.png
Normal file
After Width: | Height: | Size: 912 B |
@ -10,7 +10,6 @@ check_modset=Modset automatisch
|
|||||||
client_settings=Client Einstellungen
|
client_settings=Client Einstellungen
|
||||||
clone=Klonen
|
clone=Klonen
|
||||||
closed=geschlossen
|
closed=geschlossen
|
||||||
common=Allgemein
|
|
||||||
cpucount_desc=Gibt an, wie viele Kerne der CPU von Arma 3 genutzt werden sollen.
|
cpucount_desc=Gibt an, wie viele Kerne der CPU von Arma 3 genutzt werden sollen.
|
||||||
crashdiag_desc=Zusätzlich zur RPT wird beim Arma-Absturz ein Log vom Crash in einer binarisierten Datei abgelegt.
|
crashdiag_desc=Zusätzlich zur RPT wird beim Arma-Absturz ein Log vom Crash in einer binarisierten Datei abgelegt.
|
||||||
description=Bezeichnung
|
description=Bezeichnung
|
||||||
@ -56,7 +55,6 @@ rename=Umbennen
|
|||||||
repository_content=Repository Inhalt
|
repository_content=Repository Inhalt
|
||||||
reset_default=Auf Standard zurücksetzten
|
reset_default=Auf Standard zurücksetzten
|
||||||
running=gestartet
|
running=gestartet
|
||||||
select_all=Alles auswählen
|
|
||||||
select_folder=Ordner auswählen
|
select_folder=Ordner auswählen
|
||||||
select_mods=Mods auswählen
|
select_mods=Mods auswählen
|
||||||
select_server=Server-Auswahl
|
select_server=Server-Auswahl
|
||||||
@ -96,4 +94,11 @@ changed_filesize_tooltip=Dies ist die ungef
|
|||||||
resume=Fortsetzen
|
resume=Fortsetzen
|
||||||
changed_filesize=Veränderte Dateien
|
changed_filesize=Veränderte Dateien
|
||||||
changed_files=Geänderte Dateien
|
changed_files=Geänderte Dateien
|
||||||
file_count=Dateianzahl
|
file_count=Dateianzahl
|
||||||
|
changelog=Changelog
|
||||||
|
about=Über
|
||||||
|
follow_on_twitter=Folgt und auf Twitter
|
||||||
|
star_on_github="Star us" auf GitHub
|
||||||
|
client_up_to_date=Client ist aktuell
|
||||||
|
developer_page=Entwickler-Seite
|
||||||
|
project_page=Projektseite
|
@ -10,7 +10,6 @@ check_modset=Check Modset automatically
|
|||||||
client_settings=Client Settings
|
client_settings=Client Settings
|
||||||
clone=Clone
|
clone=Clone
|
||||||
closed=closed
|
closed=closed
|
||||||
common=Common
|
|
||||||
cpucount_desc=Specifies how many cores of the CPU should be used by Arma 3.
|
cpucount_desc=Specifies how many cores of the CPU should be used by Arma 3.
|
||||||
crashdiag_desc=In addition to the RPT, a log of the crash is stored in a binarized file.
|
crashdiag_desc=In addition to the RPT, a log of the crash is stored in a binarized file.
|
||||||
description=Description
|
description=Description
|
||||||
@ -55,7 +54,6 @@ remove=Remove
|
|||||||
rename=Rename
|
rename=Rename
|
||||||
reset_default=Reset to default
|
reset_default=Reset to default
|
||||||
running=running
|
running=running
|
||||||
select_all=Select All
|
|
||||||
select_folder=Choose folder
|
select_folder=Choose folder
|
||||||
select_mods=Select mods
|
select_mods=Select mods
|
||||||
select_server=Select Server
|
select_server=Select Server
|
||||||
@ -94,4 +92,11 @@ check_local_addons=Check local files
|
|||||||
changed_filesize=Changed size
|
changed_filesize=Changed size
|
||||||
changed_files=Changed files
|
changed_files=Changed files
|
||||||
changed_filesize_tooltip=This is the approximate maximum file size that will be downloaded
|
changed_filesize_tooltip=This is the approximate maximum file size that will be downloaded
|
||||||
file_count=Number of files
|
file_count=Number of files
|
||||||
|
changelog=Changelog
|
||||||
|
about=About
|
||||||
|
follow_on_twitter=Follow us on Twitter
|
||||||
|
star_on_github=Star us on GitHub
|
||||||
|
client_up_to_date=Client is up to date
|
||||||
|
developer_page=Developer page
|
||||||
|
project_page=Project page
|