v0.5.4 released! Keyword is data-protection. Many small bugfixes, but

main changes are:
- Modified marker (* character before name) goes up the project tree by
marking all parents of a modified object as modified.
- Impact management when changing an object's ID. Allows for some
refactoring to occur, while marking the impacted elements as modified,
or aven creating "altered" versions if imapcted element was only in game
source.
This commit is contained in:
Zukero
2017-04-07 10:01:31 +02:00
parent 49f19abb91
commit bd8576df0c
43 changed files with 462 additions and 91 deletions

View File

@@ -0,0 +1,100 @@
package com.gpl.rpg.atcontentstudio.ui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Vector;
import javax.swing.DefaultListCellRenderer;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import com.gpl.rpg.atcontentstudio.ATContentStudio;
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
import com.jidesoft.swing.JideBoxLayout;
public class IdChangeImpactWizard extends JDialog {
private static final long serialVersionUID = 8532169707953315739L;
public static enum Result {
ok, cancel
}
Result result = null;
private IdChangeImpactWizard(GameDataElement changing, List<GameDataElement> toModify, List<GameDataElement> toAlter) {
super(ATContentStudio.frame, true);
JPanel pane = new JPanel();
pane.setLayout(new JideBoxLayout(pane, JideBoxLayout.PAGE_AXIS));
pane.add(new JLabel("Changing the id for \""+changing.getDesc()+"\" has impacts on your project:"), JideBoxLayout.FIX);
pane.add(new JLabel("The following elements from your project will be modified:"), JideBoxLayout.FIX);
JList<GameDataElement> modifList = new JList<GameDataElement>(new Vector<GameDataElement>(toModify));
modifList.setCellRenderer(new ChangeImpactListCellRenderer());
pane.add(new JScrollPane(modifList), JideBoxLayout.FIX);
pane.add(new JLabel("The following elements from the game source will be altered:"), JideBoxLayout.FIX);
JList<GameDataElement> alterList = new JList<GameDataElement>(new Vector<GameDataElement>(toAlter));
alterList.setCellRenderer(new ChangeImpactListCellRenderer());
pane.add(new JScrollPane(alterList), JideBoxLayout.FIX);
pane.add(new JLabel("Press Ok to apply the changes, or Cancel to cancel your edition of the object's ID"), JideBoxLayout.FIX);
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new JideBoxLayout(buttonPane, JideBoxLayout.LINE_AXIS));
buttonPane.add(new JPanel(), JideBoxLayout.VARY);
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
result = Result.cancel;
dispose();
}
});
buttonPane.add(cancelButton, JideBoxLayout.FIX);
JButton okButton = new JButton("Ok");
okButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
result = Result.ok;
dispose();
}
});
buttonPane.add(okButton, JideBoxLayout.FIX);
pane.add(buttonPane, JideBoxLayout.FIX);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(pane, BorderLayout.CENTER);
pack();
}
public static Result showIdChangeImapctWizard(GameDataElement changing, List<GameDataElement> toModify, List<GameDataElement> toAlter) {
IdChangeImpactWizard wizard = new IdChangeImpactWizard(changing, toModify, toAlter);
wizard.setVisible(true);
return wizard.result;
}
public class ChangeImpactListCellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = 5764079243906396333L;
@Override
public Component getListCellRendererComponent(@SuppressWarnings("rawtypes") JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (c instanceof JLabel) {
JLabel label = (JLabel) c;
GameDataElement target = ((GameDataElement)value);
label.setIcon(new ImageIcon(target.getIcon()));
label.setText(target.getDataType().toString()+"/"+target.getDesc());
}
return c;
}
}
}

View File

@@ -118,6 +118,7 @@ public class StudioFrame extends JFrame {
@Override
public void windowClosing(WindowEvent e) {
Workspace.saveActive();
actions.exitATCS.actionPerformed(null);
}
});
}

View File

@@ -95,14 +95,14 @@ public class WorkspaceActions {
public void actionPerformed(ActionEvent e) {
if (!(selectedNode instanceof GameDataElement)) return;
final GameDataElement node = ((GameDataElement)selectedNode);
if (node.state == GameDataElement.State.modified){
if (node.needsSaving()){
node.save();
ATContentStudio.frame.nodeChanged(node);
}
};
public void selectionChanged(ProjectTreeNode selectedNode, TreePath[] selectedPaths) {
if (selectedNode instanceof GameDataElement) {
setEnabled(((GameDataElement)selectedNode).state == GameDataElement.State.modified);
setEnabled(((GameDataElement)selectedNode).needsSaving());
} else {
setEnabled(false);
}
@@ -330,8 +330,14 @@ public class WorkspaceActions {
public ATCSAction exitATCS = new ATCSAction("Exit", "Closes the program"){
public void actionPerformed(ActionEvent e) {
//TODO ouch.
System.exit(0);
if (Workspace.activeWorkspace.needsSaving()) {
int answer = JOptionPane.showConfirmDialog(ATContentStudio.frame, "There are unsaved changes in your workspace.\nExiting ATCS will discard these changes.\nDo you really want to exit?", "Unsaved changes. Confirm exit.", JOptionPane.YES_NO_OPTION);
if (answer == JOptionPane.YES_OPTION) {
System.exit(0);
}
} else {
System.exit(0);
}
};
};

View File

@@ -147,10 +147,22 @@ public class ActorConditionEditor extends JSONElementEditor {
public void valueChanged(JComponent source, Object value) {
ActorCondition aCond = (ActorCondition)target;
if (source == idField) {
aCond.id = (String) value;
ActorConditionEditor.this.name = aCond.getDesc();
aCond.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(ActorConditionEditor.this);
//Events caused by cancel an ID edition. Dismiss.
if (skipNext) {
skipNext = false;
return;
}
if (target.id.equals((String) value)) return;
if (idChanging()) {
aCond.id = (String) value;
ActorConditionEditor.this.name = aCond.getDesc();
aCond.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(ActorConditionEditor.this);
} else {
cancelIdEdit(idField);
return;
}
} else if (source == nameField) {
aCond.display_name = (String) value;
ActorConditionEditor.this.name = aCond.getDesc();

View File

@@ -1075,10 +1075,22 @@ public class DialogueEditor extends JSONElementEditor {
public void valueChanged(JComponent source, Object value) {
Dialogue dialogue = (Dialogue) target;
if (source == idField) {
dialogue.id = (String) value;
DialogueEditor.this.name = dialogue.getDesc();
dialogue.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(DialogueEditor.this);
//Events caused by cancel an ID edition. Dismiss.
if (skipNext) {
skipNext = false;
return;
}
if (target.id.equals((String) value)) return;
if (idChanging()) {
dialogue.id = (String) value;
DialogueEditor.this.name = dialogue.getDesc();
dialogue.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(DialogueEditor.this);
} else {
cancelIdEdit(idField);
return;
}
} else if (source == messageField) {
dialogue.message = (String) value;
} else if (source == switchToNpcBox) {

View File

@@ -248,10 +248,22 @@ public class DroplistEditor extends JSONElementEditor {
public void valueChanged(JComponent source, Object value) {
Droplist droplist = ((Droplist)target);
if (source == idField) {
droplist.id = (String) value;
DroplistEditor.this.name = droplist.getDesc();
droplist.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(DroplistEditor.this);
//Events caused by cancel an ID edition. Dismiss.
if (skipNext) {
skipNext = false;
return;
}
if (target.id.equals((String) value)) return;
if (idChanging()) {
droplist.id = (String) value;
DroplistEditor.this.name = droplist.getDesc();
droplist.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(DroplistEditor.this);
} else {
cancelIdEdit(idField);
return;
}
} else if (source == itemCombo) {
if (selectedItem.item != null) {
selectedItem.item.removeBacklink(droplist);

View File

@@ -87,10 +87,22 @@ public class ItemCategoryEditor extends JSONElementEditor {
public void valueChanged(JComponent source, Object value) {
ItemCategory ic = (ItemCategory)target;
if (source == idField) {
ic.id = (String) value;
ItemCategoryEditor.this.name = ic.getDesc();
ic.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(ItemCategoryEditor.this);
//Events caused by cancel an ID edition. Dismiss.
if (skipNext) {
skipNext = false;
return;
}
if (target.id.equals((String) value)) return;
if (idChanging()) {
ic.id = (String) value;
ItemCategoryEditor.this.name = ic.getDesc();
ic.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(ItemCategoryEditor.this);
} else {
cancelIdEdit(idField);
return;
}
} else if (source == nameField) {
ic.name = (String) value;
ItemCategoryEditor.this.name = ic.getDesc();

View File

@@ -936,10 +936,22 @@ public class ItemEditor extends JSONElementEditor {
boolean updatePrice, updateEquip, updateHit, updateKill;
updatePrice = updateEquip = updateHit = updateKill = false;
if (source == idField) {
item.id = (String) value;
ItemEditor.this.name = item.getDesc();
item.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(ItemEditor.this);
//Events caused by cancel an ID edition. Dismiss.
if (skipNext) {
skipNext = false;
return;
}
if (target.id.equals((String) value)) return;
if (idChanging()) {
item.id = (String) value;
ItemEditor.this.name = item.getDesc();
item.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(ItemEditor.this);
} else {
cancelIdEdit(idField);
return;
}
} else if (source == nameField) {
item.name = (String) value;
ItemEditor.this.name = item.getDesc();

View File

@@ -6,6 +6,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -14,6 +15,8 @@ import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
@@ -26,10 +29,13 @@ import com.gpl.rpg.atcontentstudio.model.ProjectTreeNode;
import com.gpl.rpg.atcontentstudio.model.SaveEvent;
import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataCategory;
import com.gpl.rpg.atcontentstudio.model.gamedata.JSONElement;
import com.gpl.rpg.atcontentstudio.model.maps.TMXMap;
import com.gpl.rpg.atcontentstudio.model.maps.WorldmapSegment;
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.ui.IdChangeImpactWizard;
import com.gpl.rpg.atcontentstudio.ui.SaveItemsWizard;
import com.gpl.rpg.atcontentstudio.ui.sprites.SpriteChooser;
import com.jidesoft.swing.JideBoxLayout;
@@ -275,4 +281,64 @@ public abstract class JSONElementEditor extends Editor {
message.revalidate();
message.repaint();
}
public boolean idChanging() {
JSONElement node = (JSONElement) target;
List<GameDataElement> toModify = new LinkedList<GameDataElement>();
List<GameDataElement> toAlter = new LinkedList<GameDataElement>();
for (GameDataElement element : node.getBacklinks()) {
GameDataElement activeElement = element;
if (element instanceof JSONElement) {
activeElement = node.getProject().getGameDataElement((Class<? extends JSONElement>) element.getClass(), element.id);
} else if (element instanceof TMXMap) {
activeElement = node.getProject().getMap(element.id);
} else if (element instanceof WorldmapSegment) {
activeElement = node.getProject().getWorldmapSegment(element.id);
}
if (activeElement.writable) {
//No need to alter. Check if we flag a new modification.
if (!activeElement.needsSaving()) {
toModify.add(activeElement);
}
} else {
toAlter.add(activeElement);
}
}
if (!(toModify.isEmpty() && toAlter.isEmpty())) {
IdChangeImpactWizard.Result result = IdChangeImpactWizard.showIdChangeImapctWizard(target, toModify, toAlter);
if (result == IdChangeImpactWizard.Result.ok) {
for (GameDataElement element : toModify) {
element.state = GameDataElement.State.modified;
element.childrenChanged(new ArrayList<ProjectTreeNode>());
}
for (GameDataElement element : toAlter) {
if (element instanceof JSONElement) {
node.getProject().makeWritable((JSONElement)element);
} else if (element instanceof TMXMap) {
node.getProject().makeWritable((TMXMap)element);
} else if (element instanceof WorldmapSegment) {
node.getProject().makeWritable((WorldmapSegment)element);
}
}
return true;
}
} else {
return true;
}
return false;
}
//setText in cancelIdEdit generates to edit events, one replacing the contents with the empty string, and one with the target.id. We want to skip the first one.
public boolean skipNext = false;
public void cancelIdEdit(final JTextField idField) {
Runnable revertField = new Runnable(){
@Override
public void run() {
skipNext = true;
idField.setText(target.id);
}
};
SwingUtilities.invokeLater(revertField);
}
}

View File

@@ -166,7 +166,7 @@ public class NPCEditor extends JSONElementEditor {
public void insertFormViewDataField(JPanel pane) {
final NPC npc = (NPC) target;
final FieldUpdateListener listener = new NPCFieldUpdate();
final FieldUpdateListener listener = new NPCFieldUpdater();
npcIcon = createButtonPane(pane, npc.getProject(), npc, NPC.class, npc.getImage(), Spritesheet.Category.monster, listener);
@@ -504,17 +504,29 @@ public class NPCEditor extends JSONElementEditor {
return true;
}
public class NPCFieldUpdate implements FieldUpdateListener {
public class NPCFieldUpdater implements FieldUpdateListener {
@Override
public void valueChanged(JComponent source, Object value) {
NPC npc = (NPC)target;
boolean updateHit = false;
if (source == idField) {
npc.id = (String) value;
NPCEditor.this.name = npc.getDesc();
npc.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(NPCEditor.this);
//Events caused by cancel an ID edition. Dismiss.
if (skipNext) {
skipNext = false;
return;
}
if (target.id.equals((String) value)) return;
if (idChanging()) {
npc.id = (String) value;
NPCEditor.this.name = npc.getDesc();
npc.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(NPCEditor.this);
} else {
cancelIdEdit(idField);
return;
}
} else if (source == nameField) {
npc.name = (String) value;
NPCEditor.this.name = npc.getDesc();

View File

@@ -314,10 +314,22 @@ public class QuestEditor extends JSONElementEditor {
public void valueChanged(JComponent source, Object value) {
Quest quest = (Quest) target;
if (source == idField) {
quest.id = (String) value;
QuestEditor.this.name = quest.getDesc();
quest.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(QuestEditor.this);
//Events caused by cancel an ID edition. Dismiss.
if (skipNext) {
skipNext = false;
return;
}
if (target.id.equals((String) value)) return;
if (idChanging()) {
quest.id = (String) value;
QuestEditor.this.name = quest.getDesc();
quest.childrenChanged(new ArrayList<ProjectTreeNode>());
ATContentStudio.frame.editorChanged(QuestEditor.this);
} else {
cancelIdEdit(idField);
return;
}
} else if (source == nameField) {
quest.name = (String) value;
QuestEditor.this.name = quest.getDesc();

View File

@@ -1615,7 +1615,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
gdeIcon.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (map.state == GameDataElement.State.modified || map.state == GameDataElement.State.created) {
if (map.needsSaving()) {
int confirm = JOptionPane.showConfirmDialog(TMXMapEditor.this, "You have unsaved changes in ATCS.\nYou'd better save your changes in ATCS before opening this map with the external editor.\nDo you want to save before opening the file?", "Save before opening?", JOptionPane.YES_NO_CANCEL_OPTION);
if (confirm == JOptionPane.CANCEL_OPTION) return;
if (confirm == JOptionPane.YES_OPTION) {
@@ -1633,7 +1633,7 @@ public class TMXMapEditor extends Editor implements TMXMap.MapChangedOnDiskListe
@Override
public void actionPerformed(ActionEvent e) {
if (map.state == GameDataElement.State.modified) {
if (map.needsSaving()) {
int confirm = JOptionPane.showConfirmDialog(TMXMapEditor.this, "You modified this map in ATCS. All ATCS-made changes will be lost if you confirm.\n On the other hand, if you save using ATCS, all external changes will be lost.\n Do you want to reload?", "Confirm reload?", JOptionPane.OK_CANCEL_OPTION);
if (confirm == JOptionPane.CANCEL_OPTION) return;
}

View File

@@ -63,7 +63,8 @@ public class WorldMapEditor extends Editor {
public String mapBeingAddedID = null;
public String selectedLabel = null;
WorldMapView mapView = null;
public WorldMapEditor(WorldmapSegment worldmap) {
target = worldmap;
this.name = worldmap.id;
@@ -114,7 +115,7 @@ public class WorldMapEditor extends Editor {
addLabelField(pane, "Worldmap File: ", ((Worldmap)worldmap.getParent()).worldmapFile.getAbsolutePath());
pane.add(createButtonPane(worldmap), JideBoxLayout.FIX);
final WorldMapView mapView = new WorldMapView(worldmap);
mapView = new WorldMapView(worldmap);
JScrollPane mapScroller = new JScrollPane(mapView);
final JViewport vPort = mapScroller.getViewport();
@@ -296,6 +297,7 @@ public class WorldMapEditor extends Editor {
for (String s : mapView.selected) {
map.labelledMaps.get(selectedLabel).add(s);
}
notifyModelModified();
mapView.revalidate();
mapView.repaint();
}
@@ -319,6 +321,7 @@ public class WorldMapEditor extends Editor {
worldmap.labels.put(created.id, created);
worldmap.labels.remove(selectedLabel);
selectedLabel = created.id;
notifyModelModified();
mapView.revalidate();
mapView.repaint();
}
@@ -334,6 +337,7 @@ public class WorldMapEditor extends Editor {
worldmap.labelledMaps.remove(selectedLabel);
worldmap.labels.remove(selectedLabel);
selectedLabel = null;
notifyModelModified();
mapView.revalidate();
mapView.repaint();
}
@@ -348,6 +352,7 @@ public class WorldMapEditor extends Editor {
public void labelCreated(NamedArea created) {
worldmap.labelledMaps.put(created.id, new ArrayList<String>());
worldmap.labelledMaps.get(created.id).addAll(mapView.selected);
notifyModelModified();
mapView.revalidate();
mapView.repaint();
}
@@ -436,11 +441,12 @@ public class WorldMapEditor extends Editor {
mapView.selected.remove(selectedMap);
mapSelectionChanged();
mapView.updateFromModel();
notifyModelModified();
update = true;
} else if (editMode == EditMode.addMap && mapBeingAddedID != null) {
if (e.getButton() == MouseEvent.BUTTON1) {
mapView.recomputeSize();
mapView.pushToModel();
pushToModel();
}
mapView.updateFromModel();
update = true;
@@ -463,7 +469,7 @@ public class WorldMapEditor extends Editor {
public void mouseReleased(MouseEvent e) {
dragStart = null;
if (editMode == EditMode.moveMaps) {
mapView.pushToModel();
pushToModel();
}
}
@@ -713,4 +719,16 @@ public class WorldMapEditor extends Editor {
message.repaint();
}
public void pushToModel() {
mapView.pushToModel();
notifyModelModified();
}
public void notifyModelModified() {
target.state = GameDataElement.State.modified;
this.name = ((WorldmapSegment)target).getDesc();
target.childrenChanged(new ArrayList<ProjectTreeNode>());
}
}