From 7ea3d8d5c8b1a5159987434e35b7e9d78ecbacf9 Mon Sep 17 00:00:00 2001 From: Philipp Spohr <spohr.philipp@web.de> Date: Thu, 7 Sep 2017 09:56:52 +0200 Subject: [PATCH] Added "About" panel and various small fixes Bumped up version number to 0.1 --- pom.xml | 2 +- .../de/hhu/ba/yoshikoWrapper/CyActivator.java | 4 ++ .../ba/yoshikoWrapper/core/AlgorithmTask.java | 10 ++- .../yoshikoWrapper/core/GraphicsLoader.java | 14 +++++ .../core/{Util.java => YoshUtil.java} | 5 +- .../ba/yoshikoWrapper/gui/AboutDialog.java | 56 +++++++++++++++++ .../hhu/ba/yoshikoWrapper/gui/MainPanel.java | 58 +++++++++++++----- .../gui/ReductionRulesChooser.java | 2 +- .../hhu/ba/yoshikoWrapper/gui/SwingUtil.java | 7 ++- src/main/resources/YoshikoStrings.properties | 6 +- src/main/resources/graphics/Info.png | Bin 0 -> 4115 bytes 11 files changed, 139 insertions(+), 25 deletions(-) rename src/main/java/de/hhu/ba/yoshikoWrapper/core/{Util.java => YoshUtil.java} (60%) create mode 100644 src/main/java/de/hhu/ba/yoshikoWrapper/gui/AboutDialog.java create mode 100644 src/main/resources/graphics/Info.png diff --git a/pom.xml b/pom.xml index 8cee314..385081b 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ <groupId>de.hhu.ba</groupId> <artifactId>yoshikoWrapper</artifactId> - <version>0.0.3</version> + <version>0.1.0</version> <name>YoshikoWrapper</name> diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/CyActivator.java b/src/main/java/de/hhu/ba/yoshikoWrapper/CyActivator.java index 8e19125..8cb417e 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/CyActivator.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/CyActivator.java @@ -55,6 +55,7 @@ import de.hhu.ba.yoshikoWrapper.core.CyCore; import de.hhu.ba.yoshikoWrapper.core.Hint; import de.hhu.ba.yoshikoWrapper.core.LocalizationManager; import de.hhu.ba.yoshikoWrapper.core.NetChangeListener; +import de.hhu.ba.yoshikoWrapper.core.YoshUtil; import de.hhu.ba.yoshikoWrapper.core.YoshikoLoader; import de.hhu.ba.yoshikoWrapper.gui.HintManager; import de.hhu.ba.yoshikoWrapper.gui.MainPanel; @@ -92,6 +93,9 @@ public class CyActivator extends AbstractCyActivator { CyCore.renderingEngineFactory = getService(context,RenderingEngineFactory.class); CyCore.cloneNetworkTaskFactory = getService(context,CloneNetworkTaskFactory.class); + //Store a reference to the Version + YoshUtil.version = context.getBundle().getVersion(); + //Set language according to settings LocalizationManager.switchLanguage(cm.getProperties().getProperty("locale", "enUS")); diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/core/AlgorithmTask.java b/src/main/java/de/hhu/ba/yoshikoWrapper/core/AlgorithmTask.java index a79b9ad..0cdd0cd 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/core/AlgorithmTask.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/core/AlgorithmTask.java @@ -152,7 +152,7 @@ public class AlgorithmTask extends AbstractTask { @Override public void updateGap(double gap) { - taskMonitor.setStatusMessage(LocalizationManager.get("currentGap")+": "+Util.twoDecimals.format(gap)+"%"); + taskMonitor.setStatusMessage(LocalizationManager.get("currentGap")+": "+YoshUtil.twoDecimals.format(gap)+"%"); } } @@ -161,8 +161,14 @@ public class AlgorithmTask extends AbstractTask { result = ca.run(); taskMonitor.setProgress(0.9); - + if (result == null) { + throw new Exception(LocalizationManager.get("noFeasible")); + } long numberOfSolutions = result.getNumberOfSolutions(); + + if (numberOfSolutions == 0) { + throw new Exception(LocalizationManager.get("noFeasible")); + } taskMonitor.setStatusMessage("Found: "+numberOfSolutions+" solutions!"); //TODO localize YoshikoResult yoshikoResult = new YoshikoResult(); diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/core/GraphicsLoader.java b/src/main/java/de/hhu/ba/yoshikoWrapper/core/GraphicsLoader.java index c747a16..fe123b2 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/core/GraphicsLoader.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/core/GraphicsLoader.java @@ -35,6 +35,7 @@ public class GraphicsLoader { private static BufferedImage yoshikoLogo; private static BufferedImage yoshikoLogo_solved; private static BufferedImage yoshikoText; + private static BufferedImage infoIcon; public final static Color yoshikoGreen = new Color(0,128,0); @@ -57,6 +58,19 @@ public class GraphicsLoader { return new ImageIcon(flags.get(lcl).getScaledInstance(width, height, Image.SCALE_SMOOTH)); } + public static ImageIcon getInfoIcon(int size) { + if (infoIcon == null) { + try { + infoIcon = ImageIO.read( + classLoader.getResource("graphics/Info.png") + ); + } catch (IOException e) { + e.printStackTrace(); + } + } + return new ImageIcon(infoIcon.getScaledInstance(size, size, Image.SCALE_SMOOTH)); + } + public static ImageIcon getLogo(int size) { if (yoshikoLogo == null) { try { diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/core/Util.java b/src/main/java/de/hhu/ba/yoshikoWrapper/core/YoshUtil.java similarity index 60% rename from src/main/java/de/hhu/ba/yoshikoWrapper/core/Util.java rename to src/main/java/de/hhu/ba/yoshikoWrapper/core/YoshUtil.java index 99fed80..9f195d0 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/core/Util.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/core/YoshUtil.java @@ -2,6 +2,9 @@ package de.hhu.ba.yoshikoWrapper.core; import java.text.DecimalFormat; -public class Util { +import org.osgi.framework.Version; + +public class YoshUtil { final static DecimalFormat twoDecimals =new DecimalFormat("0.00"); + public static Version version; } diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/gui/AboutDialog.java b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/AboutDialog.java new file mode 100644 index 0000000..4c3c3be --- /dev/null +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/AboutDialog.java @@ -0,0 +1,56 @@ +package de.hhu.ba.yoshikoWrapper.gui; + +import javax.swing.GroupLayout; +import javax.swing.GroupLayout.Alignment; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import de.hhu.ba.yoshikoWrapper.core.LocalizationManager; +import de.hhu.ba.yoshikoWrapper.core.YoshUtil; + +/**Factory for a simple About-Dialog that provides some basic info about the software + * + */ +public class AboutDialog{ + + private static JPanel getDialogPanel() { + + JPanel ret = new JPanel(); + + final YoshikoHeader header; + final JLabel version; + final JLabel text; + + header = new YoshikoHeader(); + version = new JLabel(LocalizationManager.get("wrapperVersion")+": "+YoshUtil.version); + text = new JLabel(LocalizationManager.get("about")); + + SwingUtil.addAll(ret, header,version,text); + + GroupLayout layout = new GroupLayout(ret); + layout.setAutoCreateGaps(true); + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.CENTER) + .addComponent(header) + .addComponent(version) + .addComponent(text) + ); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(header) + .addComponent(version) + .addComponent(text) + ); + + ret.setLayout(layout); + + return ret; + } + + public static void showDialog() { + JOptionPane.showMessageDialog( + null, + getDialogPanel(), + LocalizationManager.get("aboutTitle"), + JOptionPane.PLAIN_MESSAGE); + } +} diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/gui/MainPanel.java b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/MainPanel.java index 350150c..b6c4d40 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/gui/MainPanel.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/MainPanel.java @@ -42,6 +42,7 @@ import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.LayoutStyle; import org.cytoscape.application.swing.CytoPanelComponent; import org.cytoscape.application.swing.CytoPanelName; @@ -67,6 +68,8 @@ public class MainPanel extends JPanel implements CytoPanelComponent { private final YoshikoHeader header; + private final JButton about; + private final JCheckBox showAdvancedOptions; private final ArrayList<JComponent> advancedOptions; @@ -93,6 +96,16 @@ public class MainPanel extends JPanel implements CytoPanelComponent { //Initialize Swing components header = new YoshikoHeader(); + about = new JButton(GraphicsLoader.getInfoIcon(16)); + about.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + AboutDialog.showDialog(); + } + + }); + showAdvancedOptions = new JCheckBox(LocalizationManager.get("showAdvanced")); showAdvancedOptions.addActionListener(toggleAdvancedOptionsListener); @@ -123,6 +136,7 @@ public class MainPanel extends JPanel implements CytoPanelComponent { //Add components to main panel SwingUtil.addAll(this, header, + about, showAdvancedOptions, langPanel, libraryPanel, @@ -145,28 +159,40 @@ public class MainPanel extends JPanel implements CytoPanelComponent { //Layout GroupLayout layout = new GroupLayout(this); + layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING,true) - .addComponent(header,DEFAULT_SIZE, PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(showAdvancedOptions,DEFAULT_SIZE, PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(langPanel,DEFAULT_SIZE, PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(libraryPanel,DEFAULT_SIZE, PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(ecPanelWrapper,DEFAULT_SIZE, PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(reductionWrapper,DEFAULT_SIZE, PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(opWrapper,DEFAULT_SIZE, PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(runButton,DEFAULT_SIZE, PREFERRED_SIZE,PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(header,DEFAULT_SIZE, DEFAULT_SIZE,DEFAULT_SIZE) + //"SPRING" Functionality ; eats all the space that is available + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(about,DEFAULT_SIZE, DEFAULT_SIZE,DEFAULT_SIZE) + ) + .addGap(4) + .addComponent(showAdvancedOptions,DEFAULT_SIZE, DEFAULT_SIZE,DEFAULT_SIZE) + .addComponent(langPanel,DEFAULT_SIZE, DEFAULT_SIZE,DEFAULT_SIZE) + .addComponent(libraryPanel,DEFAULT_SIZE, DEFAULT_SIZE,DEFAULT_SIZE) + .addComponent(ecPanelWrapper,DEFAULT_SIZE, DEFAULT_SIZE,DEFAULT_SIZE) + .addComponent(reductionWrapper,DEFAULT_SIZE, DEFAULT_SIZE,DEFAULT_SIZE) + .addComponent(opWrapper,DEFAULT_SIZE, DEFAULT_SIZE,DEFAULT_SIZE) + .addComponent(runButton,DEFAULT_SIZE, DEFAULT_SIZE,DEFAULT_SIZE) ); layout.setVerticalGroup(layout.createSequentialGroup() - .addComponent(header,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(showAdvancedOptions,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(langPanel,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(libraryPanel,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(ecPanelWrapper,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(reductionWrapper,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(opWrapper,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE) - .addComponent(runButton,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE) + .addGroup(layout.createParallelGroup() + .addComponent(header,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) + .addComponent(about,Alignment.TRAILING,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) + ) + .addGap(4) + .addComponent(showAdvancedOptions,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) + .addComponent(langPanel,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) + .addComponent(libraryPanel,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) + .addComponent(ecPanelWrapper,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) + .addComponent(reductionWrapper,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) + .addComponent(opWrapper,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) + .addComponent(runButton,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) ); this.setLayout(layout); diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/gui/ReductionRulesChooser.java b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/ReductionRulesChooser.java index ef62a8b..c24ead3 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/gui/ReductionRulesChooser.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/ReductionRulesChooser.java @@ -117,7 +117,7 @@ public class ReductionRulesChooser extends JPanel{ .addComponent(useHERule, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(usePDRRule, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(useSNRule, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(SNPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(SNPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE) ); this.setLayout(layout); diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/gui/SwingUtil.java b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/SwingUtil.java index 2761282..e2d3fa8 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/gui/SwingUtil.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/SwingUtil.java @@ -21,13 +21,14 @@ ******************************************************************************/ package de.hhu.ba.yoshikoWrapper.gui; +import java.awt.Container; + import javax.swing.JComponent; -import javax.swing.JPanel; public class SwingUtil { - static void addAll(JPanel panel, JComponent ... components) { + static void addAll(Container container, JComponent ... components) { for (JComponent c: components) { - panel.add(c); + container.add(c); } } } diff --git a/src/main/resources/YoshikoStrings.properties b/src/main/resources/YoshikoStrings.properties index 463f1f8..70c004b 100644 --- a/src/main/resources/YoshikoStrings.properties +++ b/src/main/resources/YoshikoStrings.properties @@ -21,6 +21,7 @@ #------------------------------------------------------------------------------- resultsPanelTitle = Yoshiko Results yoshVersion = Yoshiko Version +wrapperVersion = Yoshiko Plugin for Cytoscape Version resolveLibPath = Resolve Yoshiko Library Path yoshTask = Performing Yoshiko Algorithm solution = Solution @@ -65,6 +66,9 @@ gap = Gap currentGap = [ILP] Current Instance Gap disableMultiThreading = Disable Multithreading yoshikoHint = Yoshiko Hint +aboutTitle = Yoshiko Plugin Info getYoshiko = Get Yoshiko Library +noFeasible = No feasible solution found! noMappingHint = You haven't mapped the cost value to a column in your edge table.\nYoshiko runs significantly faster and generates better solutions if you map values. -firstStart = Thanks for using the Yoshiko Clustering Tool!\nIt appears, that this is your first time using this tool.\nIf you want an in-depth explanation of the algorithm please refer to: [INSERT COOL LINK HERE] \ No newline at end of file +firstStart = Thanks for using the Yoshiko Clustering Tool!\nIt appears, that this is your first time using this tool.\nIf you want an in-depth explanation of the algorithm please refer to: [INSERT COOL LINK HERE] +about = INSERT SOME AMAZING INFO ABOUT THIS APPLICATION,\n THE AUTHOR AND WHERE TO FIND MORE INFO HERE\n <a href=\"www.hhu.de\" >LINK</a> \ No newline at end of file diff --git a/src/main/resources/graphics/Info.png b/src/main/resources/graphics/Info.png new file mode 100644 index 0000000000000000000000000000000000000000..80c5ba866d94baf4db4ff95f6fc7e511dc6fd2ef GIT binary patch literal 4115 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 literal 0 HcmV?d00001 -- GitLab