diff --git a/pom.xml b/pom.xml
index 3e2b0ea..d9e89d3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,11 @@
commons-io
2.6
+
+ commons-codec
+ commons-codec
+ 1.14
+
com.formdev
flatlaf
@@ -47,15 +52,14 @@
0.5.4
- io.takari.zsync
- zsync-parent
- 0.1.0
- pom
+ com.github.mfornos
+ humanize-slim
+ 1.2.2
- com.github.bitshifted
+ com.github.Gurkengewuerz
zsyncer
- -f69d844481-1
+ locale_fix-2f7565d392-1
com.squareup.okhttp3
diff --git a/src/main/java/de/mc8051/arma3launcher/LauncherGUI.form b/src/main/java/de/mc8051/arma3launcher/LauncherGUI.form
index 07ce0d0..d061ed7 100644
--- a/src/main/java/de/mc8051/arma3launcher/LauncherGUI.form
+++ b/src/main/java/de/mc8051/arma3launcher/LauncherGUI.form
@@ -3,7 +3,7 @@
-
+
@@ -407,7 +407,7 @@
-
+
@@ -423,7 +423,7 @@
-
+
@@ -437,7 +437,7 @@
-
+
@@ -448,7 +448,7 @@
-
+
@@ -526,6 +526,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -573,22 +590,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -596,7 +600,7 @@
-
+
@@ -610,17 +614,9 @@
-
+
-
-
-
-
-
-
-
-
-
+
@@ -628,7 +624,39 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -671,24 +699,24 @@
-
+
-
+
-
+
-
+
diff --git a/src/main/java/de/mc8051/arma3launcher/LauncherGUI.java b/src/main/java/de/mc8051/arma3launcher/LauncherGUI.java
index ce730ee..06b2768 100644
--- a/src/main/java/de/mc8051/arma3launcher/LauncherGUI.java
+++ b/src/main/java/de/mc8051/arma3launcher/LauncherGUI.java
@@ -5,6 +5,7 @@ import de.mc8051.arma3launcher.model.JCheckBoxTree;
import de.mc8051.arma3launcher.model.ModListRenderer;
import de.mc8051.arma3launcher.model.PresetListRenderer;
import de.mc8051.arma3launcher.model.PresetTableModel;
+import de.mc8051.arma3launcher.model.RepositoryTreeNode;
import de.mc8051.arma3launcher.model.ServerTableModel;
import de.mc8051.arma3launcher.objects.AbstractMod;
import de.mc8051.arma3launcher.objects.Mod;
@@ -13,9 +14,12 @@ import de.mc8051.arma3launcher.objects.Modset;
import de.mc8051.arma3launcher.objects.Server;
import de.mc8051.arma3launcher.repo.FileChecker;
import de.mc8051.arma3launcher.repo.RepositoryManger;
+import de.mc8051.arma3launcher.repo.SyncList;
+import de.mc8051.arma3launcher.repo.Syncer;
import de.mc8051.arma3launcher.steam.SteamTimer;
import de.mc8051.arma3launcher.utils.Callback;
import de.mc8051.arma3launcher.utils.LangUtils;
+import humanize.Humanize;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
@@ -24,6 +28,7 @@ import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.DefaultFormatter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ActionEvent;
@@ -37,11 +42,6 @@ import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
@@ -115,8 +115,8 @@ public class LauncherGUI implements Observer {
private JProgressBar syncCheckProgress;
private JButton syncCheckAbortButton;
private JButton syncCheckButton;
- private JProgressBar progressBar2;
- private JProgressBar progressBar3;
+ public JProgressBar syncDownloadProgress;
+ public JProgressBar syncFileProgress;
private JButton syncDownloadButton;
private JButton syncDownloadAbortButton;
private JButton syncPauseButton;
@@ -130,29 +130,33 @@ public class LauncherGUI implements Observer {
private JLabel syncAddedFilesLabel;
private JLabel syncChangedFilesLabel;
private JLabel syncSizeLabel;
+ private JLabel syncChangedFileSizeLabel;
+ private JLabel syncFileCountLabel;
+ public JLabel syncDownloadedLabel;
+ public JLabel syncDownloadSpeedLabel;
private JCheckBoxTree repoTree;
private FileChecker fileChecker;
-
- // TODO: Updater
- /*
- Prüfung
- In eine Liste hinzufügen wenn Datei in modset.json (Neu runterladen), nicht in modset.json (zum Löschen) oder die Größe unterschiedlich ist (Geändert)
- Checkboxen beim Syncronisieren deaktivieren
- */
+ private Syncer syncer;
+ private SyncList lastSynclist;
public LauncherGUI() {
fileChecker = new FileChecker(syncCheckProgress);
+ syncer = new Syncer(this);
RepositoryManger.getInstance().addObserver(this);
SteamTimer.addObserver(this);
fileChecker.addObserver(this);
+ syncer.addObserver(this);
updateTreePanel.remove(tree1);
repoTree = new JCheckBoxTree();
updateTreePanel.add(repoTree, BorderLayout.CENTER);
+ DefaultTreeModel model = (DefaultTreeModel) repoTree.getModel();
+ model.setRoot(new RepositoryTreeNode("Repository"));
+
updateTreePanel.revalidate();
updateTreePanel.repaint();
@@ -221,7 +225,6 @@ public class LauncherGUI implements Observer {
if (!e.getValueIsAdjusting()) {
PresetTableModel m = (PresetTableModel) presetList.getModel();
Modset modset = (Modset) m.getElementAt(presetList.getSelectedIndex());
- System.out.println(modset.getName());
if (modset.getType() == Modset.Type.SERVER) {
renamePresetButton.setEnabled(false);
@@ -283,6 +286,11 @@ public class LauncherGUI implements Observer {
new Thread(() -> {
RepositoryManger.getInstance().refreshMeta();
+ try {
+ Thread.sleep(750);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
RepositoryManger.getInstance().refreshModset();
}).start();
@@ -294,7 +302,8 @@ public class LauncherGUI implements Observer {
syncCheckStatusLabel.setText("Running!");
new Thread(() -> fileChecker.check()).start();
- // TODO: disable JTree Checkboxes
+ repoTree.setCheckboxesEnabled(false);
+ repoTree.setCheckboxesChecked(false);
}
});
@@ -304,6 +313,28 @@ public class LauncherGUI implements Observer {
fileChecker.stop();
}
});
+
+ syncDownloadButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ new Thread(() -> syncer.sync(lastSynclist.clone())).start();
+ }
+ });
+
+ syncDownloadAbortButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ syncer.stop();
+ }
+ });
+
+ syncPauseButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ syncer.setPaused(!syncer.isPaused());
+ syncPauseButton.setEnabled(false);
+ }
+ });
}
public static void infoBox(String infoMessage, String titleBar) {
@@ -440,7 +471,7 @@ public class LauncherGUI implements Observer {
}
String modPath = ArmA3Launcher.user_config.get("client", "modPath");
- if(sPath.equalsIgnoreCase(modPath)) {
+ if (sPath.equalsIgnoreCase(modPath)) {
SwingUtilities.invokeLater(() -> errorBox(LangUtils.getInstance().getString("same_mod_arma_dir_msg"), LangUtils.getInstance().getString("same_mod_arma_dir")));
return false;
}
@@ -457,7 +488,7 @@ public class LauncherGUI implements Observer {
String sPath = path.getAbsolutePath();
String armaPath = ArmA3Launcher.user_config.get("client", "armaPath");
- if(sPath.equalsIgnoreCase(armaPath)) {
+ if (sPath.equalsIgnoreCase(armaPath)) {
SwingUtilities.invokeLater(() -> errorBox(LangUtils.getInstance().getString("same_mod_arma_dir_msg"), LangUtils.getInstance().getString("same_mod_arma_dir")));
return false;
}
@@ -550,78 +581,54 @@ public class LauncherGUI implements Observer {
spinner.addChangeListener(new SettingsHandler.SpinnerListener(paraObj));
}
- public ArrayList getSyncList() {
- ArrayList modList = new ArrayList<>();
+ public SyncList getSyncList() {
+ SyncList synclist = new SyncList();
- HashMap> tempMap = new HashMap<>();
- for (TreePath checkedPath : repoTree.getCheckedPaths()) {
- DefaultMutableTreeNode tn = (DefaultMutableTreeNode)checkedPath.getLastPathComponent();
+ DefaultTreeModel model = (DefaultTreeModel) repoTree.getModel();
+ RepositoryTreeNode root = (RepositoryTreeNode) model.getRoot();
+ for (TreeNode leaf : root.getAllLeafNodes()) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) leaf;
+ TreeNode[] path = node.getPath();
+ boolean isSelected = repoTree.isSelected(new TreePath(path));
+ if (!isSelected) continue;
- if(tn.getChildCount() > 0) continue;
- Object[] path = checkedPath.getPath();
- DefaultMutableTreeNode[] modifiedArray = Arrays.stream(Arrays.copyOfRange(path, 1, path.length)).toArray(DefaultMutableTreeNode[]::new);
-
- ArrayList strings = new ArrayList<>();
- if(tempMap.containsKey(String.valueOf(modifiedArray[0].getUserObject()))) {
- strings = tempMap.get(String.valueOf(modifiedArray[0].getUserObject()));
+ ArrayList treePathList = new ArrayList<>();
+ for (int i = 2; i < path.length; i++) {
+ treePathList.add(String.valueOf(((DefaultMutableTreeNode) path[i]).getUserObject()));
}
+ String treePath = String.join("/", treePathList);
+ String modname = String.valueOf(((DefaultMutableTreeNode) path[1]).getUserObject());
- String modPath = "";
- for (int i = 1; i < modifiedArray.length; i++) {
- modPath += String.valueOf(modifiedArray[i].getUserObject()) + "/";
- }
- modPath = modPath.isEmpty() ? "" : modPath.substring(0, modPath.length() - 1);
- strings.add(modPath);
-
- tempMap.put((String) modifiedArray[0].getUserObject(), strings);
- }
-
- for (Map.Entry> entry : tempMap.entrySet()) {
- String modS = entry.getKey();
- ArrayList modlistS = entry.getValue();
-
- if(modlistS.isEmpty()) {
- for (AbstractMod abstractMod : RepositoryManger.MOD_LIST) {
- if (abstractMod.getName().equals(modS)) {
- modList.add(abstractMod);
+ if (fileChecker.getChanged().containsKey(modname)) {
+ for (ModFile modFile : fileChecker.getChanged().get(modname)) {
+ if (String.join("/", modFile.getPath()).equals(treePath)) {
+ synclist.add(modFile);
break;
}
}
- } else {
- for (AbstractMod abstractMod : RepositoryManger.MOD_LIST) {
- if (abstractMod.getName().equals(modS)) {
- if(!(abstractMod instanceof Mod)) continue;
- Mod m = ((Mod) abstractMod).clone();
+ }
- for (int i = 0; i < m.getFiles().size(); i++) {
- boolean found = false;
- for (String pathS : modlistS) {
- if(m.getFiles().get(i).getModfileString().equals(pathS)) {
- found = true;
- }
- }
-
- if(!found) {
- m.getFiles().remove(i);
- }
- }
-
- modList.add(m);
+ if (fileChecker.getAdded().containsKey(modname)) {
+ for (ModFile modFile : fileChecker.getAdded().get(modname)) {
+ if (String.join("/", modFile.getPath()).equals(treePath)) {
+ synclist.add(modFile);
+ break;
}
}
}
}
+ synclist.setDeleted(fileChecker.getDeleted());
- return modList;
+ return synclist;
}
public void updateModList(Modset modset) {
ListModel model = (ListModel) modList.getModel();
// TODO: Show All Mods (keyname)
- // TODO: Show not installed Mods with red font
- // TODO: Select Mod if in modset.Mods
- // TODO: Custom Checkbox Render
- // TODO: Wenn modset.type == Server alle Checkboxen deaktivieren!
+ // Show not installed Mods with red font
+ // Select Mod if in modset.Mods
+ // Custom Checkbox Render
+ // Wenn modset.type == Server alle Checkboxen deaktivieren!
}
public void updateRepoTree() {
@@ -629,28 +636,26 @@ public class LauncherGUI implements Observer {
collapseAllButton.setEnabled(false);
DefaultTreeModel model = (DefaultTreeModel) repoTree.getModel();
- DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
- root.setUserObject("Repository");
+ RepositoryTreeNode root = (RepositoryTreeNode) model.getRoot();
root.removeAllChildren();
for (AbstractMod abstractMod : RepositoryManger.MOD_LIST) {
if (abstractMod instanceof Mod) {
// Whole Folder
- // TODO: Recursives Ordner Parsen und einzelne Treenodes erstellen
Mod m = (Mod) abstractMod;
- DefaultMutableTreeNode modFolder = new DefaultMutableTreeNode(m.getName(), true);
+ RepositoryTreeNode modFolder = new RepositoryTreeNode(m.getName(), true);
model.insertNodeInto(modFolder, root, root.getChildCount());
for (ModFile modfile : m.getFiles()) {
- DefaultMutableTreeNode lastNode = modFolder;
+ RepositoryTreeNode lastNode = modFolder;
ArrayList path = modfile.getPath();
- for (int i = 0; i < path.size(); i++) {
+ for (int i = 0; i < path.size() -1; i++) {
boolean found = false;
for (int j = 0; j < lastNode.getChildCount(); j++) {
- DefaultMutableTreeNode temp = (DefaultMutableTreeNode) lastNode.getChildAt(j);
+ RepositoryTreeNode temp = (RepositoryTreeNode) lastNode.getChildAt(j);
if (temp.getUserObject().equals(path.get(i))) {
found = true;
lastNode = temp;
@@ -659,26 +664,25 @@ public class LauncherGUI implements Observer {
}
if (!found) {
- DefaultMutableTreeNode temp = new DefaultMutableTreeNode(path.get(i));
+ RepositoryTreeNode temp = new RepositoryTreeNode(path.get(i));
model.insertNodeInto(temp, lastNode, lastNode.getChildCount());
lastNode = temp;
}
}
-
- model.insertNodeInto(new DefaultMutableTreeNode(modfile.getName()), lastNode, lastNode.getChildCount());
+ model.insertNodeInto(new RepositoryTreeNode(modfile.getName(), getNodeColor(m.getName(), modfile)), lastNode, lastNode.getChildCount());
}
sort(modFolder);
} else if (abstractMod instanceof ModFile) {
// Just a Single FIle
ModFile m = (ModFile) abstractMod;
- model.insertNodeInto(new DefaultMutableTreeNode(m.getName(), false), root, root.getChildCount());
+ model.insertNodeInto(new RepositoryTreeNode(m.getName(), getNodeColor(m.getName(), m), false), root, root.getChildCount());
}
}
sort(root);
+ setParentColor(root);
repoTree.clearCheckChangeEventListeners();
-
repoTree.resetCheckingState();
SwingUtilities.invokeLater(() -> {
@@ -690,38 +694,90 @@ public class LauncherGUI implements Observer {
updateTreePanel.repaint();
});
+ repoTree.addCheckChangeEventListener(new JCheckBoxTree.CheckChangeEventListener() {
+ @Override
+ public void checkStateChanged(JCheckBoxTree.CheckChangeEvent event) {
+ lastSynclist = getSyncList();
+ if (lastSynclist.getSize() != 0)
+ syncSizeLabel.setText(Humanize.binaryPrefix(lastSynclist.getSize()));
+ else syncSizeLabel.setText("0.0 B");
+ if (lastSynclist.getCount() != 0) {
+ syncDownloadButton.setEnabled(true);
+ syncFileCountLabel.setText("" + lastSynclist.getCount());
+ } else {
+ syncDownloadButton.setEnabled(false);
+ syncFileCountLabel.setText("");
+ }
+ }
+ });
+
expandAllButton.setEnabled(true);
collapseAllButton.setEnabled(true);
}
- public DefaultMutableTreeNode sort(DefaultMutableTreeNode node) {
+ public Color getNodeColor(String mod, ModFile mf) {
+ if (fileChecker.getAdded().containsKey(mod)) {
+ ArrayList mfList = fileChecker.getAdded().get(mod);
+ for (ModFile modFile : mfList) {
+ if (modFile.getLocaleFile().getPath().equals(mf.getLocaleFile().getPath())) return Color.RED;
+ }
+ }
+
+ if (fileChecker.getChanged().containsKey(mod)) {
+ ArrayList mfList = fileChecker.getChanged().get(mod);
+ for (ModFile modFile : mfList) {
+ if (modFile.getLocaleFile().getPath().equals(mf.getLocaleFile().getPath())) return Color.ORANGE;
+ }
+ }
+
+ return null;
+ }
+
+ public void setParentColor(RepositoryTreeNode node) {
+ for (TreeNode leaf : node.getAllLeafNodes()) {
+ if (!(leaf instanceof RepositoryTreeNode)) continue;
+ RepositoryTreeNode mLeaf = (RepositoryTreeNode) leaf;
+ TreeNode[] path = mLeaf.getPath();
+
+ if (mLeaf.getLabelColor() == null) continue;
+ for (int i = 0; i < path.length - 1; i++) {
+ if (!(path[i] instanceof RepositoryTreeNode)) continue;
+ RepositoryTreeNode parent = (RepositoryTreeNode) path[i];
+ if (parent.getLabelColor() == mLeaf.getLabelColor()) continue;
+ if (parent.getLabelColor() == Color.RED) continue;
+ parent.setLabelColor(mLeaf.getLabelColor());
+ }
+ }
+ }
+
+ public RepositoryTreeNode sort(RepositoryTreeNode node) {
//sort alphabetically
- for(int i = 0; i < node.getChildCount() - 1; i++) {
- DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
+ for (int i = 0; i < node.getChildCount() - 1; i++) {
+ RepositoryTreeNode child = (RepositoryTreeNode) node.getChildAt(i);
String nt = child.getUserObject().toString();
- for(int j = i + 1; j <= node.getChildCount() - 1; j++) {
- DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j);
+ for (int j = i + 1; j <= node.getChildCount() - 1; j++) {
+ RepositoryTreeNode prevNode = (RepositoryTreeNode) node.getChildAt(j);
String np = prevNode.getUserObject().toString();
- if(nt.compareToIgnoreCase(np) > 0) {
+ if (nt.compareToIgnoreCase(np) > 0) {
node.insert(child, j);
node.insert(prevNode, i);
}
}
- if(child.getChildCount() > 0) {
+ if (child.getChildCount() > 0) {
sort(child);
}
}
//put folders first - normal on Windows and some flavors of Linux but not on Mac OS X.
- for(int i = 0; i < node.getChildCount() - 1; i++) {
- DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
- for(int j = i + 1; j <= node.getChildCount() - 1; j++) {
- DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j);
+ for (int i = 0; i < node.getChildCount() - 1; i++) {
+ RepositoryTreeNode child = (RepositoryTreeNode) node.getChildAt(i);
+ for (int j = i + 1; j <= node.getChildCount() - 1; j++) {
+ RepositoryTreeNode prevNode = (RepositoryTreeNode) node.getChildAt(j);
- if(!prevNode.isLeaf() && child.isLeaf()) {
+ if (!prevNode.isLeaf() && child.isLeaf()) {
node.insert(child, j);
node.insert(prevNode, i);
}
@@ -778,30 +834,55 @@ public class LauncherGUI implements Observer {
refreshRepoButton.setEnabled(false);
break;
}
- } else if(s.equals("fileChecker")) {
+ } else if (s.equals("fileChecker")) {
syncCheckButton.setEnabled(true);
syncCheckAbortButton.setEnabled(false);
syncCheckStatusLabel.setText("Finished!");
updateRepoTree();
- // TODO: Label einfärben
- // TODO: Enable Tree Checkboxes
+
+ repoTree.setCheckboxesEnabled(true);
syncDownloadButton.setEnabled(true);
syncAddedFilesLabel.setText(String.valueOf(fileChecker.getAddedCount()));
syncChangedFilesLabel.setText(String.valueOf(fileChecker.getChangedCount()));
syncDeletedFilesLabel.setText(String.valueOf(fileChecker.getDeletedCount()));
- syncSizeLabel.setText(String.valueOf(fileChecker.getSize())); // TODO: Make Humanreadable
+ syncDownloadAbortButton.setEnabled(false);
+ syncDownloadButton.setEnabled(true);
+ syncPauseButton.setEnabled(false);
+
+ syncChangedFileSizeLabel.setText(Humanize.binaryPrefix(fileChecker.getSize()));
} else if (s.equals("fileCheckerStopped")) {
syncCheckButton.setEnabled(true);
syncCheckAbortButton.setEnabled(false);
syncCheckProgress.setValue(0);
syncCheckStatusLabel.setText("Failed!");
+ repoTree.setCheckboxesEnabled(false);
+
+ syncDownloadAbortButton.setEnabled(false);
+ syncDownloadButton.setEnabled(false);
+ syncPauseButton.setEnabled(false);
+
+ repoTree.setCheckboxesChecked(false);
syncAddedFilesLabel.setText("" + 0);
syncChangedFilesLabel.setText("" + 0);
syncDeletedFilesLabel.setText("" + 0);
- syncSizeLabel.setText("0.0 B");
+ syncChangedFileSizeLabel.setText("0.0 B");
+ } else if (s.equals("syncStopped")) {
+ new Thread(() -> fileChecker.check()).start();
+ } else if (s.equals("syncComplete")) {
+ new Thread(() -> fileChecker.check()).start();
+ } else if (s.equals("syncContinue")) {
+ syncDownloadAbortButton.setEnabled(true);
+ syncPauseButton.setEnabled(true);
+ syncPauseButton.setText(LangUtils.getInstance().getString("pause"));
+ syncDownloadButton.setEnabled(false);
+ } else if (s.equals("syncPaused")) {
+ syncDownloadAbortButton.setEnabled(true);
+ syncPauseButton.setEnabled(true);
+ syncPauseButton.setText(LangUtils.getInstance().getString("resume"));
+ syncDownloadButton.setEnabled(false);
}
}
}
diff --git a/src/main/java/de/mc8051/arma3launcher/model/JCheckBoxTree.java b/src/main/java/de/mc8051/arma3launcher/model/JCheckBoxTree.java
index a45eafc..6fd9d19 100644
--- a/src/main/java/de/mc8051/arma3launcher/model/JCheckBoxTree.java
+++ b/src/main/java/de/mc8051/arma3launcher/model/JCheckBoxTree.java
@@ -17,6 +17,7 @@ import java.util.EventListener;
import java.util.EventObject;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Set;
/**
* Created by SomethingSomething https://stackoverflow.com/a/21851201/5605489
@@ -31,6 +32,7 @@ public class JCheckBoxTree extends JTree {
// Defining data structure that will enable to fast check-indicate the state of each node
// It totally replaces the "selection" mechanism of the JTree
private class CheckedNode {
+ boolean isEnabled;
boolean isSelected;
boolean hasChildren;
boolean allChildrenSelected;
@@ -38,6 +40,7 @@ public class JCheckBoxTree extends JTree {
public CheckedNode(boolean isSelected_, boolean hasChildren_, boolean allChildrenSelected_) {
isSelected = isSelected_;
hasChildren = hasChildren_;
+ isEnabled = true;
allChildrenSelected = allChildrenSelected_;
}
}
@@ -107,6 +110,11 @@ public class JCheckBoxTree extends JTree {
return cn.isSelected && cn.hasChildren && !cn.allChildrenSelected;
}
+ public boolean isSelected(TreePath path) {
+ CheckedNode cn = nodesCheckingState.get(path);
+ return cn.isSelected;
+ }
+
public void resetCheckingState() {
nodesCheckingState = new HashMap();
checkedPaths = new HashSet();
@@ -148,12 +156,21 @@ public class JCheckBoxTree extends JTree {
boolean hasFocus) {
checkBox.setText(value.toString());
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
+
+ if (node instanceof RepositoryTreeNode && ((RepositoryTreeNode) node).getLabelColor() != null) {
+ checkBox.setForeground(((RepositoryTreeNode) node).getLabelColor());
+ } else {
+ checkBox.setForeground(UIManager.getColor("CheckBox.foreground"));
+ }
+
TreePath tp = new TreePath(node.getPath());
CheckedNode cn = nodesCheckingState.get(tp);
if (cn == null) {
return this;
}
+
checkBox.setSelected(cn.isSelected);
+ checkBox.setEnabled(cn.isEnabled);
checkBox.setOpaque(cn.isSelected && cn.hasChildren && !cn.allChildrenSelected);
return this;
}
@@ -264,6 +281,26 @@ public class JCheckBoxTree extends JTree {
}
}
+ public void setCheckboxesChecked(boolean state) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) getModel().getRoot();
+ checkSubTree(new TreePath(node.getPath()), state);
+ }
+
+ public void setCheckboxesEnabled(TreePath tp, boolean state) {
+ CheckedNode cn = nodesCheckingState.get(tp);
+ cn.isEnabled = state;
+
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) tp.getLastPathComponent();
+ for (int i = 0; i < node.getChildCount(); i++) {
+ setCheckboxesEnabled(tp.pathByAddingChild(node.getChildAt(i)), state);
+ }
+ }
+
+ public void setCheckboxesEnabled(boolean state) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) getModel().getRoot();
+ setCheckboxesEnabled(new TreePath(node.getPath()), state);
+ }
+
public void expandAllNodes() {
setTreeExpandedState(true);
}
@@ -274,13 +311,13 @@ public class JCheckBoxTree extends JTree {
private void setTreeExpandedState(boolean expanded) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) getModel().getRoot();
- setNodeExpandedState( node, expanded);
+ setNodeExpandedState(node, expanded);
}
private void setNodeExpandedState(DefaultMutableTreeNode node, boolean expanded) {
ArrayList list = Collections.list(node.children());
for (TreeNode treeNode : list) {
- setNodeExpandedState((DefaultMutableTreeNode)treeNode, expanded);
+ setNodeExpandedState((DefaultMutableTreeNode) treeNode, expanded);
}
if (!expanded && node.isRoot()) {
return;
diff --git a/src/main/java/de/mc8051/arma3launcher/model/RepositoryTreeNode.java b/src/main/java/de/mc8051/arma3launcher/model/RepositoryTreeNode.java
new file mode 100644
index 0000000..324e76a
--- /dev/null
+++ b/src/main/java/de/mc8051/arma3launcher/model/RepositoryTreeNode.java
@@ -0,0 +1,54 @@
+package de.mc8051.arma3launcher.model;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+import java.awt.*;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by gurkengewuerz.de on 26.03.2020.
+ */
+public class RepositoryTreeNode extends DefaultMutableTreeNode {
+
+ private Color labelColor = null;
+
+ public RepositoryTreeNode(String userObject, boolean allowChildren) {
+ super(userObject, allowChildren);
+ }
+
+ public RepositoryTreeNode(String userObject, Color labelColor, boolean allowChildren) {
+ super(userObject, allowChildren);
+ this.labelColor = labelColor;
+ }
+
+ public RepositoryTreeNode(String userObject, Color labelColor) {
+ super(userObject);
+ this.labelColor = labelColor;
+ }
+
+ public RepositoryTreeNode(String userObject) {
+ super(userObject);
+ }
+
+ public Color getLabelColor() {
+ return labelColor;
+ }
+
+ public void setLabelColor(Color labelColor) {
+ this.labelColor = labelColor;
+ }
+
+ public Set getAllLeafNodes() {
+ Set leafNodes = new HashSet<>();
+ if (this.children == null) {
+ leafNodes.add(this);
+ } else {
+ for (Object child : this.children) {
+ if (child instanceof RepositoryTreeNode)
+ leafNodes.addAll(((RepositoryTreeNode) child).getAllLeafNodes());
+ }
+ }
+ return leafNodes;
+ }
+}
diff --git a/src/main/java/de/mc8051/arma3launcher/objects/ModFile.java b/src/main/java/de/mc8051/arma3launcher/objects/ModFile.java
index 8d0176c..c5b357c 100644
--- a/src/main/java/de/mc8051/arma3launcher/objects/ModFile.java
+++ b/src/main/java/de/mc8051/arma3launcher/objects/ModFile.java
@@ -1,10 +1,19 @@
package de.mc8051.arma3launcher.objects;
+import de.mc8051.arma3launcher.ArmA3Launcher;
+import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FilenameUtils;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
/**
* Created by gurkengewuerz.de on 25.03.2020.
@@ -17,8 +26,11 @@ public class ModFile implements AbstractMod {
private String filename;
private String extension;
private String modfileString;
+ private String sha1sum;
+ private String parent;
+ private String localGeneratedSHA1sum = "";
- public ModFile(File f, String modfile, long size) {
+ public ModFile(File f, String modfile, String parent, long size, String sha1sum) {
// File: Abosolut Path
// modfile: addons/config/something.pbo
// size: size as in metafile on server
@@ -28,6 +40,12 @@ public class ModFile implements AbstractMod {
this.filename = FilenameUtils.getBaseName(modfile);
this.extension = FilenameUtils.getExtension(modfile);
this.modfileString = modfile;
+ this.sha1sum = sha1sum.toLowerCase();
+ this.parent = parent;
+ }
+
+ public ModFile(File f, String modfile, long size, String sha1sum) {
+ this(f, modfile, null, size, sha1sum);
}
public long getSize() {
@@ -48,23 +66,23 @@ public class ModFile implements AbstractMod {
public ArrayList getPath() {
ArrayList list = new ArrayList<>();
- File relativePath = new File("./"+ modfileString);
+ File relativePath = new File("./" + modfileString);
+
do {
list.add(relativePath.getName());
relativePath = relativePath.getParentFile();
} while (relativePath.getParentFile() != null);
- list.remove(0);
Collections.reverse(list);
return list;
}
public long getLocalSize() {
- if(!f.exists() || !f.isFile()) return -1;
+ if (!f.exists() || !f.isFile()) return -1;
return f.length();
}
public boolean exists() {
- if(!f.exists() || !f.isFile()) return false;
+ if (!f.exists() || !f.isFile()) return false;
return true;
}
@@ -79,4 +97,45 @@ public class ModFile implements AbstractMod {
public File getLocaleFile() {
return f;
}
+
+ public String getSHA1Sum() {
+ return sha1sum;
+ }
+
+ public String getLocalGeneratedSHA1Sum() {
+ try {
+ if (localGeneratedSHA1sum.isEmpty() && exists()) {
+ localGeneratedSHA1sum = DigestUtils.sha1Hex(new FileInputStream(f.getAbsolutePath())).toLowerCase();
+ }
+ } catch (IOException e) {
+ Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
+ }
+ return localGeneratedSHA1sum;
+ }
+
+ public String getRemoteFile() {
+ String s = ArmA3Launcher.config.getString("sync.url");
+ if (parent == null || parent.isEmpty()) {
+ return s + "/" + encodeToURL(getName());
+ }
+
+ s += "/" + encodeToURL(parent);
+ for (String seg : getPath()) {
+ s += "/" + encodeToURL(seg);
+ }
+ return s;
+ }
+
+ public String getParent() {
+ return parent;
+ }
+
+ private String encodeToURL(String s) {
+ try {
+ return URLEncoder.encode(s, StandardCharsets.UTF_8.name()).replace("+", "%20").replace("@", "%40");
+ } catch (UnsupportedEncodingException e) {
+ Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
+ }
+ return "";
+ }
}
diff --git a/src/main/java/de/mc8051/arma3launcher/repo/DownloadThread.java b/src/main/java/de/mc8051/arma3launcher/repo/DownloadThread.java
deleted file mode 100644
index 3163b02..0000000
--- a/src/main/java/de/mc8051/arma3launcher/repo/DownloadThread.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package de.mc8051.arma3launcher.repo;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Created by gurkengewuerz.de on 24.03.2020.
- */
-public class DownloadThread implements Runnable {
-
- private ProcessBuilder processBuilder;
- private Process process;
- private Thread thread;
-
- private Status status = Status.PENDING;
-
- public DownloadThread(ProcessBuilder processBuilder) {
- this.processBuilder = processBuilder;
- this.processBuilder.redirectErrorStream(true);
-
- thread = new Thread(this);
- thread.start();
- }
-
- public void stop() {
- process.destroy();
- thread.interrupt();
- }
-
- public Status getStatus() {
- return status;
- }
-
- @Override
- public void run() {
- try {
- process = processBuilder.start();
- status = Status.RUNNING;
-
- BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
- String line;
- while ( (line = reader.readLine()) != null && !thread.isInterrupted()) {
- System.out.println(line);
- }
-
- int exitVal = process.waitFor();
- if(exitVal == 0) status = Status.FINNISHED;
- else status = Status.ERROR;
-
- System.out.println(exitVal);
- } catch (IOException | InterruptedException ex) {
- Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
- status = Status.ERROR;
- }
- }
-
- private enum Status {
- PENDING(0),
- RUNNING(1),
- FINNISHED(2),
- ERROR(3);
-
- Status(int i) {
-
- }
- }
-}
diff --git a/src/main/java/de/mc8051/arma3launcher/repo/FileChecker.java b/src/main/java/de/mc8051/arma3launcher/repo/FileChecker.java
index a030744..34c3118 100644
--- a/src/main/java/de/mc8051/arma3launcher/repo/FileChecker.java
+++ b/src/main/java/de/mc8051/arma3launcher/repo/FileChecker.java
@@ -30,10 +30,10 @@ public class FileChecker implements Observable {
private JProgressBar pb;
private boolean stop = false;
- ArrayList deleted = new ArrayList<>();
- HashMap> changed = new HashMap<>();
+ private ArrayList deleted = new ArrayList<>();
+ private HashMap> changed = new HashMap<>();
int changedCount = 0;
- HashMap> added = new HashMap<>();
+ private HashMap> added = new HashMap<>();
int addedCount = 0;
long size = 0;
@@ -66,7 +66,8 @@ public class FileChecker implements Observable {
Mod m = (Mod) abstractMod;
for (ModFile mf : m.getFiles()) {
- checkFile(mf.getName(), mf);
+ checkFile(m.getName(), mf);
+
i++;
int finalI = i;
SwingUtilities.invokeLater(() -> {
@@ -99,16 +100,21 @@ public class FileChecker implements Observable {
}
private void checkFile(String mod, ModFile mf) {
- // TODO: Add mf to Array if Array already exists
+ ArrayList temp = new ArrayList<>();
+
if(!mf.exists()) {
- added.put(mod, mf);
+ if(added.containsKey(mod)) temp =added.get(mod);
+ temp.add(mf);
+ added.put(mod, temp);
addedCount++;
size += mf.getSize();
return;
}
- if(mf.getLocalSize() != mf.getSize()) {
- changed.put(mod, mf);
+ if(mf.getLocalSize() != mf.getSize() || !mf.getSHA1Sum().equalsIgnoreCase(mf.getLocalGeneratedSHA1Sum())) {
+ if(changed.containsKey(mod)) temp =changed.get(mod);
+ temp.add(mf);
+ changed.put(mod, temp);
changedCount++;
size += mf.getSize();
return;
diff --git a/src/main/java/de/mc8051/arma3launcher/repo/RepositoryManger.java b/src/main/java/de/mc8051/arma3launcher/repo/RepositoryManger.java
index 68f09bb..51f7240 100644
--- a/src/main/java/de/mc8051/arma3launcher/repo/RepositoryManger.java
+++ b/src/main/java/de/mc8051/arma3launcher/repo/RepositoryManger.java
@@ -151,16 +151,18 @@ public class RepositoryManger implements Observable {
Iterator keys = content.keys();
while (keys.hasNext()) {
String modfile = keys.next();
- long modfilesize = content.getLong(modfile);
+ JSONObject jo = content.getJSONObject(modfile);
+ long modfilesize = jo.getLong("size");
+ String sha1 = jo.getString("sha1");
- modFiles.add(new ModFile(new File(finalModPath + File.separator + modname + File.separator + modfile), modfile, modfilesize));
+ modFiles.add(new ModFile(new File(finalModPath + File.separator + modname + File.separator + modfile), modfile, modname, modfilesize, sha1));
RepositoryManger.MOD_LIST_SIZE++;
}
MOD_LIST.add(new Mod(modname, modsize, modFiles));
} else {
// Single File
- MOD_LIST.add(new ModFile(new File(finalModPath + File.separator + modname), modname, modsize));
+ MOD_LIST.add(new ModFile(new File(finalModPath + File.separator + modname), modname, modsize, jsonMod.getString("sha1")));
RepositoryManger.MOD_LIST_SIZE++;
}
diff --git a/src/main/java/de/mc8051/arma3launcher/repo/SyncList.java b/src/main/java/de/mc8051/arma3launcher/repo/SyncList.java
new file mode 100644
index 0000000..900cbfb
--- /dev/null
+++ b/src/main/java/de/mc8051/arma3launcher/repo/SyncList.java
@@ -0,0 +1,108 @@
+package de.mc8051.arma3launcher.repo;
+
+import de.mc8051.arma3launcher.objects.AbstractMod;
+import de.mc8051.arma3launcher.objects.Mod;
+import de.mc8051.arma3launcher.objects.ModFile;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Created by gurkengewuerz.de on 26.03.2020.
+ */
+public class SyncList extends ArrayList {
+
+ private long size = 0;
+ private int count = 0;
+
+ private ArrayList deleted = new ArrayList<>();
+
+ private SyncList(long size, int count) {
+ this.size = size;
+ this.count = count;
+ }
+
+ public SyncList() {
+
+ }
+
+ public void setDeleted(ArrayList deleted) {
+ this.deleted = deleted;
+ }
+
+ @Override
+ public boolean add(AbstractMod abstractMod) {
+ addedMod(abstractMod);
+ return super.add(abstractMod);
+ }
+
+ @Override
+ public boolean addAll(Collection extends AbstractMod> c) {
+ for (AbstractMod abstractMod : c) {
+ addedMod(abstractMod);
+ }
+ return super.addAll(c);
+ }
+
+ private void addedMod(AbstractMod abstractMod) {
+ if (abstractMod instanceof Mod) {
+ Mod mod = (Mod) abstractMod;
+ for (ModFile mf : mod.getFiles()) {
+ size += mf.getSize();
+ count++;
+ }
+ } else if (abstractMod instanceof ModFile) {
+ ModFile mf = (ModFile) abstractMod;
+ size += mf.getSize();
+ count++;
+ }
+ }
+
+ @Override
+ public AbstractMod remove(int index) {
+ AbstractMod abstractMod = get(index);
+
+ if (abstractMod instanceof Mod) {
+ Mod mod = (Mod) abstractMod;
+ for (ModFile mf : mod.getFiles()) {
+ size -= mf.getSize();
+ }
+ } else if (abstractMod instanceof ModFile) {
+ ModFile mf = (ModFile) abstractMod;
+ size -= mf.getSize();
+ }
+
+ return super.remove(index);
+ }
+
+ public void setSize(long size) {
+ this.size = size;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public SyncList clone() {
+ SyncList clone = new SyncList();
+ clone.addAll(this);
+ clone.getDeleted().addAll(deleted);
+ clone.setSize(size);
+ clone.setDeleted(deleted);
+ return clone;
+ }
+
+ public ArrayList getDeleted() {
+ return deleted;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public int getCount() {
+ return count;
+ }
+}
diff --git a/src/main/java/de/mc8051/arma3launcher/repo/Syncer.java b/src/main/java/de/mc8051/arma3launcher/repo/Syncer.java
index 0b058f4..59a09db 100644
--- a/src/main/java/de/mc8051/arma3launcher/repo/Syncer.java
+++ b/src/main/java/de/mc8051/arma3launcher/repo/Syncer.java
@@ -1,8 +1,304 @@
package de.mc8051.arma3launcher.repo;
+import co.bitshfted.xapps.zsync.Zsync;
+import co.bitshfted.xapps.zsync.ZsyncException;
+import co.bitshfted.xapps.zsync.ZsyncObserver;
+import de.mc8051.arma3launcher.ArmA3Launcher;
+import de.mc8051.arma3launcher.LauncherGUI;
+import de.mc8051.arma3launcher.interfaces.Observable;
+import de.mc8051.arma3launcher.interfaces.Observer;
+import de.mc8051.arma3launcher.objects.AbstractMod;
+import de.mc8051.arma3launcher.objects.ModFile;
+import humanize.Humanize;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
/**
* Created by gurkengewuerz.de on 25.03.2020.
*/
-public class Syncer {
-// FilenameUtils.directoryContains
+public class Syncer extends ZsyncObserver implements Observable {
+
+ private List observerList = new ArrayList<>();
+
+ private boolean stopped = false;
+ private boolean paused = false;
+ private boolean running = false;
+
+ private ModFile currentDownload = null;
+ private SyncList modlist;
+
+ private boolean currentDownload_failed = false;
+ private boolean controlfile_downloaded = false;
+ private int failed = 0;
+ private int success = 0;
+
+ long syncSize;
+ int syncCount;
+
+ long downloadStarted;
+ long downloadEnded;
+ long downloadSize;
+ long downloadDownloaded;
+
+ private Zsync zsync;
+ private LauncherGUI gui;
+
+ public Syncer(LauncherGUI gui) {
+ zsync = new Zsync();
+ this.gui = gui;
+ }
+
+ public void sync(SyncList ml) {
+ modlist = ml;
+
+ stopped = false;
+ paused = false;
+ running = true;
+
+ currentDownload = null;
+
+ failed = 0;
+ success = 0;
+
+ syncSize = ml.getSize();
+ syncCount = ml.getCount();
+ SwingUtilities.invokeLater(() -> {
+ gui.syncDownloadProgress.setMaximum(syncCount);
+ gui.syncDownloadProgress.setValue(0);
+ });
+
+ boolean lastPause = false;
+ while (running) {
+ if (stopped) {
+ running = false;
+ break;
+ }
+ if (modlist.isEmpty()) {
+ running = false;
+ break;
+ }
+
+ if (paused) {
+ if (!lastPause) {
+ lastPause = true;
+ notifyObservers("syncPaused");
+ }
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
+ }
+ continue;
+ } else if (lastPause) {
+ lastPause = false;
+ notifyObservers("syncContinue");
+ }
+
+ if (currentDownload != null) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
+ }
+ }
+
+ AbstractMod abstractMod = modlist.get(0);
+
+ ModFile mf = null;
+ if (abstractMod instanceof ModFile) {
+ mf = (ModFile) abstractMod;
+ }
+
+ if (mf != null) {
+ Zsync.Options o = new Zsync.Options();
+ o.setOutputFile(Paths.get(mf.getLocaleFile().getAbsolutePath()));
+
+ try {
+ currentDownload = mf;
+ currentDownload_failed = false;
+ controlfile_downloaded = false;
+
+ zsync.zsync(URI.create(mf.getRemoteFile() + ".zsync"), o, this);
+ } catch (ZsyncException | IllegalArgumentException e) {
+ Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
+ }
+ } else {
+ modlist.remove(0);
+ }
+ }
+
+ deleteFiles();
+ cleanUpEmptyFolders();
+
+ if (stopped) {
+ notifyObservers("syncStopped");
+ } else {
+ notifyObservers("syncComplete");
+ }
+ }
+
+ public void finnishCurrent() {
+ modlist.remove(0);
+ currentDownload = null;
+ }
+
+ public void deleteFiles() {
+ modlist.getDeleted().stream()
+ .filter((p) -> p.toFile().exists())
+ .filter((p) -> p.toFile().canRead())
+ .filter((p) -> p.toFile().canWrite())
+ .forEach((p) -> p.toFile().delete());
+ }
+
+ public void cleanUpEmptyFolders() {
+ try {
+ String modPath = ArmA3Launcher.user_config.get("client", "modPath");
+ if (modPath == null) modPath = "";
+ if (modPath.isEmpty()) return;
+ Files.find(Paths.get(modPath),
+ Integer.MAX_VALUE,
+ (filePath, fileAttr) -> fileAttr.isDirectory())
+ .filter((p) -> p.toFile().canRead())
+ .filter((p) -> p.toFile().canWrite())
+ .filter((p) -> p.toFile().list().length == 0)
+ .forEach((p) -> p.toFile().delete());
+ } catch (IOException e) {
+ Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
+ }
+ }
+
+ @Override
+ public void zsyncStarted(URI requestedZsyncUri, Zsync.Options options) {
+ super.zsyncStarted(requestedZsyncUri, options);
+ System.out.println("ZSync started " + options.getOutputFile());
+ }
+
+ @Override
+ public void controlFileDownloadingComplete() {
+ super.controlFileDownloadingComplete();
+ System.out.println("controlFileDownloadingComplete");
+ controlfile_downloaded = true;
+ }
+
+ @Override
+ public void zsyncFailed(Exception exception) {
+ super.zsyncFailed(exception);
+ currentDownload_failed = true;
+ System.out.println("Zsync failed " + exception.getMessage());
+ }
+
+ @Override
+ public void zsyncComplete() {
+ super.zsyncComplete();
+
+ downloadEnded = System.nanoTime();
+ System.out.println(downloadSize);
+ System.out.println(downloadEnded - downloadStarted);
+ System.out.println((downloadSize / (downloadEnded - downloadStarted)) / 1000);
+
+ System.out.println("Zsync complete");
+
+ if (currentDownload_failed)
+ failed++;
+ else success++;
+
+ final long finalSize = syncSize - modlist.getSize();
+ int i = success + failed;
+ int percentage = (int) ((double)i / (double)Long.valueOf(syncCount).intValue() * 100);
+
+ SwingUtilities.invokeLater(() -> {
+ gui.syncDownloadProgress.setValue(i);
+ gui.syncDownloadedLabel.setText(Humanize.binaryPrefix(finalSize) + " " + " (" + failed + " failed)");
+ gui.syncDownloadProgress.setString(percentage + "%");
+ });
+
+ finnishCurrent();
+ }
+
+ @Override
+ public void controlFileDownloadingStarted(URI uri, long length) {
+ super.controlFileDownloadingStarted(uri, length);
+ System.out.println("controlFileDownloadingStarted " + length);
+ }
+
+ @Override
+ public void remoteFileDownloadingStarted(URI uri, long length) {
+ super.remoteFileDownloadingStarted(uri, length);
+ System.out.println("remoteFileDownloadingStarted " + length);
+
+ SwingUtilities.invokeLater(() -> {
+ gui.syncFileProgress.setMaximum(Long.valueOf(length).intValue());
+ gui.syncFileProgress.setValue(0);
+ });
+
+ downloadSize = length;
+ downloadDownloaded = 0;
+ downloadStarted = System.nanoTime();
+ }
+
+ @Override
+ public void bytesDownloaded(long bytes) {
+ super.bytesDownloaded(bytes);
+// System.out.println("Downloaded " + bytes);
+ downloadDownloaded += bytes;
+
+ // TODO: Fix file Download Progress
+ if (controlfile_downloaded) {
+ SwingUtilities.invokeLater(() -> {
+ gui.syncFileProgress.setValue(Long.valueOf(downloadDownloaded).intValue());
+ });
+ }
+ }
+
+
+ public boolean isStopped() {
+ return stopped;
+ }
+
+ public boolean isPaused() {
+ return paused;
+ }
+
+ public boolean isRunning() {
+ return running;
+ }
+
+ public void stop() {
+ this.stopped = true;
+ }
+
+ public void setPaused(boolean paused) {
+ this.paused = paused;
+ }
+
+ public int getCountFailed() {
+ return failed;
+ }
+
+ public int getCountSuccess() {
+ return success;
+ }
+
+ @Override
+ public void addObserver(Observer observer) {
+ observerList.add(observer);
+ }
+
+ @Override
+ public void removeObserver(Observer observer) {
+ observerList.remove(observer);
+ }
+
+ @Override
+ public void notifyObservers(String obj) {
+ for (Observer obs : observerList) obs.update(obj);
+ }
}
diff --git a/src/main/resources/lang_de_DE.properties b/src/main/resources/lang_de_DE.properties
index 88ef780..3795b5c 100644
--- a/src/main/resources/lang_de_DE.properties
+++ b/src/main/resources/lang_de_DE.properties
@@ -88,6 +88,12 @@ collapse_all=Alles einklappen
same_mod_arma_dir_msg=Das ArmA sowie Mod Verzeichnis dürfen nicht identisch sein.
same_mod_arma_dir=Gleiches Verzeichnis
check_local_addons=Lokale Dateien überprüfen
-changed_files=Veränderte Datien
-added_files=Hinzugefügte Datien
-deleted_files=Gelöschte Dateien
\ No newline at end of file
+changed_filesize=Veränderte Größe
+added_files=Hinzugefügte Dateien
+deleted_files=Gelöschte Dateien
+changed_filesize=Veränderte Größe
+changed_filesize_tooltip=Dies ist die ungefähre Dateigröße die Maximal runtergeladen wird
+resume=Fortsetzen
+changed_filesize=Veränderte Dateien
+changed_files=Geänderte Dateien
+file_count=Dateianzahl
\ No newline at end of file
diff --git a/src/main/resources/lang_en_US.properties b/src/main/resources/lang_en_US.properties
index 52d559a..294762d 100644
--- a/src/main/resources/lang_en_US.properties
+++ b/src/main/resources/lang_en_US.properties
@@ -85,4 +85,13 @@ steam_not_running=Steam not running. Please start Steam.
repository_content=Repository Content
collapse_all=Collapse All
same_mod_arma_dir_msg=The ArmA and Mod directory must not be identical.
-same_mod_arma_dir=Same directory
\ No newline at end of file
+same_mod_arma_dir=Same directory
+resume=Resume
+added_files=Added files
+deleted_files=Deleted files
+update_repository=Update repository
+check_local_addons=Check local files
+changed_filesize=Changed size
+changed_files=Changed files
+changed_filesize_tooltip=This is the approximate maximum file size that will be downloaded
+file_count=Number of files
\ No newline at end of file