diff --git a/src/com/gpl/rpg/atcontentstudio/model/WorkspaceSettings.java b/src/com/gpl/rpg/atcontentstudio/model/WorkspaceSettings.java index 3ca4dda..397804e 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/WorkspaceSettings.java +++ b/src/com/gpl/rpg/atcontentstudio/model/WorkspaceSettings.java @@ -28,16 +28,16 @@ public class WorkspaceSettings { public File file; public static Boolean DEFAULT_USE_SYS_MAP_EDITOR = true; - public Setting useSystemDefaultMapEditor = new Setting("useSystemDefaultMapEditor", DEFAULT_USE_SYS_MAP_EDITOR); + public Setting useSystemDefaultMapEditor = new PrimitiveSetting("useSystemDefaultMapEditor", DEFAULT_USE_SYS_MAP_EDITOR); public static String DEFAULT_MAP_EDITOR_COMMAND = "tiled"; - public Setting mapEditorCommand = new Setting("mapEditorCommand", DEFAULT_MAP_EDITOR_COMMAND); + public Setting mapEditorCommand = new PrimitiveSetting("mapEditorCommand", DEFAULT_MAP_EDITOR_COMMAND); public static Boolean DEFAULT_USE_SYS_IMG_VIEWER = true; - public Setting useSystemDefaultImageViewer = new Setting("useSystemDefaultImageViewer", DEFAULT_USE_SYS_MAP_EDITOR); - public static Boolean DEFAULT_USE_SYS_IMG_EDITOR = true; - public Setting useSystemDefaultImageEditor = new Setting("useSystemDefaultImageEditor", DEFAULT_USE_SYS_MAP_EDITOR); + public Setting useSystemDefaultImageViewer = new PrimitiveSetting("useSystemDefaultImageViewer", DEFAULT_USE_SYS_IMG_VIEWER); + public static Boolean DEFAULT_USE_SYS_IMG_EDITOR = false; + public Setting useSystemDefaultImageEditor = new PrimitiveSetting("useSystemDefaultImageEditor", DEFAULT_USE_SYS_IMG_EDITOR); public static String DEFAULT_IMG_EDITOR_COMMAND = "gimp"; - public Setting imageEditorCommand = new Setting("imageEditorCommand", DEFAULT_MAP_EDITOR_COMMAND); + public Setting imageEditorCommand = new PrimitiveSetting("imageEditorCommand", DEFAULT_IMG_EDITOR_COMMAND); public List> settings = new ArrayList>(); @@ -133,14 +133,13 @@ public class WorkspaceSettings { } } - class Setting { + public abstract class Setting { + + public String id; + public X value, defaultValue; - X value, defaultValue; - String id; - - public Setting(String id, X defaultValue) { - this.id = id; - this.value = this.defaultValue = defaultValue; + public void setCurrentValue(X value) { + this.value = value; } public X getCurrentValue() { @@ -155,15 +154,49 @@ public class WorkspaceSettings { value = defaultValue; } - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void readFromJson(Map json) { - value = (X)json.get(id); - } + public abstract void readFromJson(Map json); @SuppressWarnings({ "rawtypes", "unchecked" }) public void saveToJson(Map json) { if (!defaultValue.equals(value)) json.put(id, value); } } + + public class PrimitiveSetting extends Setting { + + + public PrimitiveSetting(String id, X defaultValue) { + this.id = id; + this.value = this.defaultValue = defaultValue; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void readFromJson(Map json) { + if (json.get(id) != null) value = (X)json.get(id); + } + + + } + + public class ListSetting extends Setting> { + + public ListSetting(String id, List defaultValue) { + this.id = id; + this.value = this.defaultValue = defaultValue; + } + + @Override + public void readFromJson(Map json) { + value = new ArrayList(); + if (json.get(id) != null) { + for (Object o : ((List)json.get(id))) { + value.add((X)o); + } + } + } + + } + + } diff --git a/src/com/gpl/rpg/atcontentstudio/model/maps/TMXMapSet.java b/src/com/gpl/rpg/atcontentstudio/model/maps/TMXMapSet.java index f852f86..6ade66a 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/maps/TMXMapSet.java +++ b/src/com/gpl/rpg/atcontentstudio/model/maps/TMXMapSet.java @@ -23,6 +23,7 @@ import com.gpl.rpg.atcontentstudio.model.ProjectTreeNode; import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataSet; import com.gpl.rpg.atcontentstudio.model.sprites.SpriteSheetSet; import com.gpl.rpg.atcontentstudio.ui.DefaultIcons; +import com.gpl.rpg.atcontentstudio.utils.FileUtils; public class TMXMapSet implements ProjectTreeNode { @@ -50,15 +51,7 @@ public class TMXMapSet implements ProjectTreeNode { if (!this.mapFolder.exists()) { this.mapFolder.mkdirs(); } - Path target = Paths.get(getProject().baseContent.gameSprites.drawableFolder.getAbsolutePath()); - Path link = Paths.get(new File(mapFolder.getAbsolutePath()+File.separator+DEFAULT_REL_PATH_TO_DRAWABLE).getAbsolutePath()); - if (!Files.exists(link)) { - try { - Files.createSymbolicLink(link, target); - } catch (IOException e) { - e.printStackTrace(); - } - } + FileUtils.makeSymlink(getProject().baseContent.gameSprites.drawableFolder, new File(mapFolder.getAbsolutePath()+File.separator+DEFAULT_REL_PATH_TO_DRAWABLE)); } this.tmxMaps = new ArrayList(); diff --git a/src/com/gpl/rpg/atcontentstudio/ui/StudioFrame.java b/src/com/gpl/rpg/atcontentstudio/ui/StudioFrame.java index 6f7318a..8522a78 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/StudioFrame.java +++ b/src/com/gpl/rpg/atcontentstudio/ui/StudioFrame.java @@ -129,6 +129,8 @@ public class StudioFrame extends JFrame { fileMenu.add(new JMenuItem(actions.closeProject)); fileMenu.add(new JMenuItem(actions.deleteProject)); fileMenu.add(new JSeparator()); + fileMenu.add(new JMenuItem(actions.editWorkspaceSettings)); + fileMenu.add(new JSeparator()); fileMenu.add(new JMenuItem(actions.exitATCS)); getJMenuBar().add(fileMenu); diff --git a/src/com/gpl/rpg/atcontentstudio/ui/WorkspaceActions.java b/src/com/gpl/rpg/atcontentstudio/ui/WorkspaceActions.java index 26db950..c2bbca2 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/WorkspaceActions.java +++ b/src/com/gpl/rpg/atcontentstudio/ui/WorkspaceActions.java @@ -370,6 +370,15 @@ public class WorkspaceActions { } }; + public ATCSAction editWorkspaceSettings = new ATCSAction("Edit Workspace Settings", "Change the preferences of this workspace.") { + public void actionPerformed(ActionEvent e) { + new WorkspaceSettingsEditor(Workspace.activeWorkspace.settings); + }; + public void selectionChanged(ProjectTreeNode selectedNode, TreePath[] selectedPaths) { + setEnabled(true); + }; + }; + List actions = new ArrayList(); public WorkspaceActions() { @@ -390,6 +399,7 @@ public class WorkspaceActions { actions.add(testWriter); // actions.add(testCommitWriter); actions.add(createWriter); + actions.add(editWorkspaceSettings); selectionChanged(null, null); } diff --git a/src/com/gpl/rpg/atcontentstudio/ui/WorkspaceSettingsEditor.java b/src/com/gpl/rpg/atcontentstudio/ui/WorkspaceSettingsEditor.java new file mode 100644 index 0000000..48e57da --- /dev/null +++ b/src/com/gpl/rpg/atcontentstudio/ui/WorkspaceSettingsEditor.java @@ -0,0 +1,180 @@ +package com.gpl.rpg.atcontentstudio.ui; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTextField; + +import com.gpl.rpg.atcontentstudio.ATContentStudio; +import com.gpl.rpg.atcontentstudio.model.WorkspaceSettings; +import com.jidesoft.swing.JideBoxLayout; + +public class WorkspaceSettingsEditor extends JDialog { + + private static final long serialVersionUID = -1326158719217162879L; + + WorkspaceSettings settings; + + JRadioButton useSystemDefaultMapEditorButton, useCustomMapEditorButton; + JTextField mapEditorCommandField; + + JRadioButton useSystemDefaultImageViewerButton, useSystemDefaultImageEditorButton, useCustomImageEditorButton; + JTextField imageEditorCommandField; + + + + public WorkspaceSettingsEditor(WorkspaceSettings settings) { + super(ATContentStudio.frame, "Workspace settings", true); + setIconImage(DefaultIcons.getMainIconImage()); + + this.settings = settings; + + JPanel pane = new JPanel(); + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(new JScrollPane(pane), BorderLayout.CENTER); + pane.setLayout(new JideBoxLayout(pane, JideBoxLayout.PAGE_AXIS)); + JPanel buttonPane = new JPanel(); + buttonPane.setLayout(new JideBoxLayout(buttonPane, JideBoxLayout.LINE_AXIS)); + getContentPane().add(buttonPane, BorderLayout.SOUTH); + + + pane.add(getExternalToolsPane(), JideBoxLayout.FIX); + pane.add(new JPanel(), JideBoxLayout.VARY); + + buttonPane.add(new JPanel(), JideBoxLayout.VARY); + JButton ok = new JButton("Ok"); + ok.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + pushToModel(); + dispose(); + } + }); + buttonPane.add(ok, JideBoxLayout.FIX); + JButton reset = new JButton("Reset to defaults"); + reset.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + resetDefaults(); + } + }); + buttonPane.add(reset, JideBoxLayout.FIX); + JButton cancel = new JButton("Cancel"); + cancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + }); + buttonPane.add(cancel, JideBoxLayout.FIX); + + loadFromModel(); + pack(); + setVisible(true); + + } + + public JPanel getExternalToolsPane() { + CollapsiblePanel pane = new CollapsiblePanel("External tools"); + pane.setLayout(new JideBoxLayout(pane, JideBoxLayout.PAGE_AXIS)); + + //Tiled + CollapsiblePanel tiledPane = new CollapsiblePanel("TMX Map viewer/editor"); + tiledPane.setLayout(new JideBoxLayout(tiledPane, JideBoxLayout.PAGE_AXIS)); + ButtonGroup tiledRadioGroup = new ButtonGroup(); + useSystemDefaultMapEditorButton = new JRadioButton("Use system-default TMX Map editor"); + tiledRadioGroup.add(useSystemDefaultMapEditorButton); + tiledPane.add(useSystemDefaultMapEditorButton, JideBoxLayout.FIX); + useCustomMapEditorButton = new JRadioButton("Use custom command to open TMX Map files"); + tiledRadioGroup.add(useCustomMapEditorButton); + tiledPane.add(useCustomMapEditorButton, JideBoxLayout.FIX); + mapEditorCommandField = new JTextField(); + tiledPane.add(mapEditorCommandField, JideBoxLayout.FIX); + ActionListener tiledRadioListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (useSystemDefaultMapEditorButton.equals(e.getSource())) { + mapEditorCommandField.setEnabled(false); + } else if (useCustomMapEditorButton.equals(e.getSource())) { + mapEditorCommandField.setEnabled(true); + } + } + }; + useSystemDefaultMapEditorButton.addActionListener(tiledRadioListener); + useCustomMapEditorButton.addActionListener(tiledRadioListener); + pane.add(tiledPane, JideBoxLayout.FIX); + + //Images + CollapsiblePanel imgPane = new CollapsiblePanel("Image viewer/editor"); + imgPane.setLayout(new JideBoxLayout(imgPane, JideBoxLayout.PAGE_AXIS)); + ButtonGroup imgRadioGroup = new ButtonGroup(); + useSystemDefaultImageViewerButton = new JRadioButton("Use system-default image viewer"); + imgRadioGroup.add(useSystemDefaultImageViewerButton); + imgPane.add(useSystemDefaultImageViewerButton, JideBoxLayout.FIX); + useSystemDefaultImageEditorButton = new JRadioButton("Use system-default image editor"); + imgRadioGroup.add(useSystemDefaultImageEditorButton); + imgPane.add(useSystemDefaultImageEditorButton, JideBoxLayout.FIX); + useCustomImageEditorButton = new JRadioButton("Use custom command to open images"); + imgRadioGroup.add(useCustomImageEditorButton); + imgPane.add(useCustomImageEditorButton, JideBoxLayout.FIX); + imageEditorCommandField = new JTextField(); + imgPane.add(imageEditorCommandField, JideBoxLayout.FIX); + ActionListener imgRadioListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (useSystemDefaultMapEditorButton.equals(e.getSource())) { + imageEditorCommandField.setEnabled(false); + } else if (useSystemDefaultImageViewerButton.equals(e.getSource())) { + imageEditorCommandField.setEnabled(false); + } else if (useCustomImageEditorButton.equals(e.getSource())) { + imageEditorCommandField.setEnabled(true); + } + } + }; + useSystemDefaultImageViewerButton.addActionListener(imgRadioListener); + useSystemDefaultImageEditorButton.addActionListener(imgRadioListener); + useCustomImageEditorButton.addActionListener(imgRadioListener); + pane.add(imgPane, JideBoxLayout.FIX); + + pane.expand(); + return pane; + } + + public void loadFromModel() { + //Tiled + useSystemDefaultMapEditorButton.setSelected(settings.useSystemDefaultMapEditor.getCurrentValue()); + useCustomMapEditorButton.setSelected(!settings.useSystemDefaultMapEditor.getCurrentValue()); + mapEditorCommandField.setText(settings.mapEditorCommand.getCurrentValue()); + //Images + useSystemDefaultImageViewerButton.setSelected(settings.useSystemDefaultImageViewer.getCurrentValue()); + useSystemDefaultImageEditorButton.setSelected(settings.useSystemDefaultImageEditor.getCurrentValue()); + useCustomImageEditorButton.setSelected(!(settings.useSystemDefaultImageViewer.getCurrentValue() || settings.useSystemDefaultImageEditor.getCurrentValue())); + imageEditorCommandField.setText(settings.imageEditorCommand.getCurrentValue()); + } + + public void pushToModel() { + //Tiled + settings.useSystemDefaultMapEditor.setCurrentValue(useSystemDefaultMapEditorButton.isSelected()); + settings.mapEditorCommand.setCurrentValue(mapEditorCommandField.getText()); + //Images + settings.useSystemDefaultImageViewer.setCurrentValue(useSystemDefaultImageViewerButton.isSelected()); + settings.useSystemDefaultImageEditor.setCurrentValue(useSystemDefaultImageEditorButton.isSelected()); + settings.imageEditorCommand.setCurrentValue(imageEditorCommandField.getText()); + + settings.save(); + } + + public void resetDefaults() { + settings.resetDefault(); + settings.save(); + loadFromModel(); + } + +} diff --git a/src/com/gpl/rpg/atcontentstudio/ui/map/TMXMapEditor.java b/src/com/gpl/rpg/atcontentstudio/ui/map/TMXMapEditor.java index 850afd8..769cff0 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/map/TMXMapEditor.java +++ b/src/com/gpl/rpg/atcontentstudio/ui/map/TMXMapEditor.java @@ -99,6 +99,7 @@ import com.gpl.rpg.atcontentstudio.ui.Editor; import com.gpl.rpg.atcontentstudio.ui.FieldUpdateListener; import com.gpl.rpg.atcontentstudio.ui.IntegerBasedCheckBox; import com.gpl.rpg.atcontentstudio.ui.ScrollablePanel; +import com.gpl.rpg.atcontentstudio.utils.DesktopIntegration; import com.jidesoft.swing.JideBoxLayout; import com.jidesoft.swing.JideTabbedPane; @@ -1608,11 +1609,7 @@ public class TMXMapEditor extends Editor { gdeIcon.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - try { - Runtime.getRuntime().exec(new String[]{"tiled",map.tmxFile.getAbsolutePath()}); - } catch (IOException e1) { - e1.printStackTrace(); - } + DesktopIntegration.openTmxMap(map.tmxFile); } }); diff --git a/src/com/gpl/rpg/atcontentstudio/ui/sprites/SpritesheetEditor.java b/src/com/gpl/rpg/atcontentstudio/ui/sprites/SpritesheetEditor.java index 10e9314..f20f779 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/sprites/SpritesheetEditor.java +++ b/src/com/gpl/rpg/atcontentstudio/ui/sprites/SpritesheetEditor.java @@ -4,6 +4,8 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; @@ -17,6 +19,7 @@ import java.util.Map; import javax.swing.BorderFactory; import javax.swing.DefaultListCellRenderer; import javax.swing.ImageIcon; +import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; @@ -41,8 +44,10 @@ import com.gpl.rpg.atcontentstudio.model.GameDataElement; import com.gpl.rpg.atcontentstudio.model.ProjectTreeNode; import com.gpl.rpg.atcontentstudio.model.maps.TMXMap; import com.gpl.rpg.atcontentstudio.model.sprites.Spritesheet; +import com.gpl.rpg.atcontentstudio.ui.DefaultIcons; import com.gpl.rpg.atcontentstudio.ui.Editor; import com.gpl.rpg.atcontentstudio.ui.FieldUpdateListener; +import com.gpl.rpg.atcontentstudio.utils.DesktopIntegration; import com.jidesoft.swing.JideBoxLayout; import com.jidesoft.swing.JideTabbedPane; @@ -84,6 +89,14 @@ public class SpritesheetEditor extends Editor { pane.setLayout(new JideBoxLayout(pane, JideBoxLayout.PAGE_AXIS, 6)); add(getWarningLabel(), JideBoxLayout.FIX); + JButton openImage = new JButton(new ImageIcon(DefaultIcons.getTileLayerImage())); + openImage.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + DesktopIntegration.openImage(((Spritesheet)target).spritesheetFile); + } + }); + pane.add(openImage, JideBoxLayout.FIX); addLabelField(pane, "Spritesheet ID: ", sheet.id); addLabelField(pane, "File: ", sheet.spritesheetFile.getAbsolutePath()); widthField = addIntegerField(pane, "Sprite width (px): ", sheet.spriteWidth, false, true, listener); diff --git a/src/com/gpl/rpg/atcontentstudio/utils/DesktopIntegration.java b/src/com/gpl/rpg/atcontentstudio/utils/DesktopIntegration.java new file mode 100644 index 0000000..8505652 --- /dev/null +++ b/src/com/gpl/rpg/atcontentstudio/utils/DesktopIntegration.java @@ -0,0 +1,67 @@ +package com.gpl.rpg.atcontentstudio.utils; + +import java.awt.Desktop; +import java.io.File; +import java.io.IOException; +import java.util.Locale; + +import com.gpl.rpg.atcontentstudio.model.Workspace; + +public class DesktopIntegration { + + public static void openTmxMap(File f) { + if (Workspace.activeWorkspace.settings.useSystemDefaultMapEditor.getCurrentValue()) { + try { + Desktop.getDesktop().open(f); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + try { + Runtime.getRuntime().exec(Workspace.activeWorkspace.settings.mapEditorCommand.getCurrentValue()+" "+f.getAbsolutePath()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public static void openImage(File f) { + if (Workspace.activeWorkspace.settings.useSystemDefaultImageViewer.getCurrentValue()) { + try { + Desktop.getDesktop().open(f); + } catch (IOException e) { + e.printStackTrace(); + } + } else if (Workspace.activeWorkspace.settings.useSystemDefaultImageEditor.getCurrentValue()) { + try { + Desktop.getDesktop().edit(f); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + try { + Runtime.getRuntime().exec(Workspace.activeWorkspace.settings.imageEditorCommand.getCurrentValue()+" "+f.getAbsolutePath()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + + + public static enum OSType { + Windows, MacOS, NIX, Other + } + + public static OSType detectedOS = detectOS(); + + private static OSType detectOS() { + String os = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); + if ((os.indexOf("mac") >= 0) || (os.indexOf("darwin") >= 0)) return OSType.MacOS; + if (os.indexOf("win") >= 0) return OSType.Windows; + if ((os.indexOf("nux") >= 0) || (os.indexOf("nix") >= 0) || (os.indexOf("aix") >= 0) || (os.indexOf("sunos") >= 0) || (os.indexOf("solaris") >= 0)) return OSType.NIX; + return OSType.Other; + } + + +} diff --git a/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java b/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java index d217f2f..1a4e76e 100644 --- a/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java +++ b/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java @@ -9,6 +9,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -108,8 +111,48 @@ public class FileUtils { } } } - - + } + + public static boolean makeSymlink(File targetFile, File linkFile) { + Path target = Paths.get(targetFile.getAbsolutePath()); + Path link = Paths.get(linkFile.getAbsolutePath()); + if (!Files.exists(link)) { + try { + Files.createSymbolicLink(link, target); + } catch (Exception e) { + System.err.println("Failed to create symbolic link to target \""+targetFile.getAbsolutePath()+"\" as \""+linkFile.getAbsolutePath()+"\" the java.nio way:"); + e.printStackTrace(); + switch (DesktopIntegration.detectedOS) { + case Windows: + System.err.println("Trying the Windows way with mklink"); + try { + Runtime.getRuntime().exec("mklink "+(targetFile.isDirectory() ? "/J " : "")+linkFile.getAbsolutePath()+" "+targetFile.getAbsolutePath()); + } catch (IOException e1) { + e1.printStackTrace(); + } + break; + case MacOS: + case NIX: + case Other: + System.err.println("Trying the unix way with ln -s"); + try { + Runtime.getRuntime().exec("ln -s "+targetFile.getAbsolutePath()+" "+linkFile.getAbsolutePath()); + } catch (IOException e1) { + e1.printStackTrace(); + } + break; + default: + System.out.println("Unrecognized OS. Please contact ATCS dev."); + break; + + } + } + } + if (!Files.exists(link)) { + System.err.println("Failed to create link \""+linkFile.getAbsolutePath()+"\" targetting \""+targetFile.getAbsolutePath()+"\""); + System.err.println("You can try running ATCS with administrative privileges once, or create the symbolic link manually."); + } + return true; } }