First drafts of "Writer Mode".

Some bug fixes.
This commit is contained in:
Zukero
2016-09-16 15:23:00 +02:00
parent ce908f0033
commit c18ff9d2b4
14 changed files with 1851 additions and 17 deletions

View File

@@ -5,10 +5,14 @@ import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import prefuse.data.expression.parser.ExpressionParser;
import com.gpl.rpg.atcontentstudio.model.Workspace;
import com.gpl.rpg.atcontentstudio.ui.StudioFrame;
import com.gpl.rpg.atcontentstudio.ui.WorkerDialog;
@@ -30,6 +34,8 @@ public class ATContentStudio {
ConfigCache.init();
Logger.getLogger(ExpressionParser.class.getName()).setLevel(Level.OFF);
try {
String laf = ConfigCache.getFavoriteLaFClassName();
if (laf == null) laf = UIManager.getSystemLookAndFeelClassName();

View File

@@ -31,6 +31,7 @@ import com.gpl.rpg.atcontentstudio.model.maps.Worldmap;
import com.gpl.rpg.atcontentstudio.model.maps.WorldmapSegment;
import com.gpl.rpg.atcontentstudio.model.sprites.SpriteSheetSet;
import com.gpl.rpg.atcontentstudio.model.sprites.Spritesheet;
import com.gpl.rpg.atcontentstudio.model.tools.WriterModeDataSet;
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
public class GameSource implements ProjectTreeNode, Serializable {
@@ -44,6 +45,7 @@ public class GameSource implements ProjectTreeNode, Serializable {
public transient TMXMapSet gameMaps;
public transient SpriteSheetSet gameSprites;
public transient Worldmap worldmap;
public transient WriterModeDataSet writerModeDataSet;
private transient SavedSlotCollection v;
public static enum Type {
@@ -86,6 +88,9 @@ public class GameSource implements ProjectTreeNode, Serializable {
readResourceList();
}
}
if (type == Type.created) {
this.writerModeDataSet = new WriterModeDataSet(this);
}
this.gameData = new GameDataSet(this);
this.gameMaps = new TMXMapSet(this);
this.gameSprites = new SpriteSheetSet(this);
@@ -95,6 +100,9 @@ public class GameSource implements ProjectTreeNode, Serializable {
v.add(gameMaps);
v.add(gameSprites);
v.add(worldmap);
if (type == Type.created) {
v.add(writerModeDataSet);
}
}
public void readResourceList() {

View File

@@ -247,7 +247,7 @@ public class Dialogue extends JSONElement {
case spawnAll:
case removeSpawnArea:
case deactivateSpawnArea:
reward.map = proj.getMap(reward.map_name);
reward.map = reward.map_name != null ? proj.getMap(reward.map_name) : null;
break;
case actorCondition:
reward.reward_obj = proj.getActorCondition(reward.reward_obj_id);
@@ -315,6 +315,11 @@ public class Dialogue extends JSONElement {
if (rclone.reward_obj != null) {
rclone.reward_obj.addBacklink(clone);
}
rclone.map = r.map;
rclone.map_name = r.map_name;
if (rclone.map != null) {
rclone.map.addBacklink(clone);
}
clone.rewards.add(rclone);
}
}

View File

@@ -0,0 +1,251 @@
package com.gpl.rpg.atcontentstudio.model.tools;
import java.awt.Image;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.tree.TreeNode;
import com.gpl.rpg.atcontentstudio.model.GameDataElement.State;
import com.gpl.rpg.atcontentstudio.model.GameSource.Type;
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
import com.gpl.rpg.atcontentstudio.model.Project;
import com.gpl.rpg.atcontentstudio.model.ProjectTreeNode;
import com.gpl.rpg.atcontentstudio.model.SaveEvent;
import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataSet;
import com.gpl.rpg.atcontentstudio.model.tools.WriterModeData.WriterDialogue;
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
public class WriterModeData extends GameDataElement {
public WriterModeDataSet parent;
public Image npcIcon;
public WriterDialogue begin;
public Map<String, WriterDialogue> dialogueThreads = new LinkedHashMap<String, WriterDialogue>();
public Map<String, Integer> threadsNextIndex = new LinkedHashMap<String, Integer>();
public WriterModeData(WriterModeDataSet parent, String id_prefix){
this.parent = parent;
this.begin = new WriterDialogue();
begin.id_prefix = id_prefix;
begin.index = getNextIndex(id_prefix);
begin.text = "";
}
public WriterModeData(WriterModeDataSet parent, Map jsonObj) {
this.parent = parent;
this.begin = new WriterDialogue(jsonObj);
this.state = State.parsed;
}
public int getNextIndex(String id_prefix) {
Integer index = threadsNextIndex.get(id_prefix);
if (index == null) index = 0;
while (getProject().getDialogue(id_prefix+index) != null) {
index++;
}
threadsNextIndex.put(id_prefix, index + 1);
return index;
}
public abstract class WriterNode {
public String text;
public abstract String getTitle();
public abstract Map toJson();
}
public class WriterDialogue extends WriterNode {
public String id_prefix;
public int index;
public List<WriterReply> replies = new LinkedList<WriterReply>();
public WriterDialogue() {}
public WriterDialogue(String id_prefix) {
text = "";
this.id_prefix = id_prefix;
index = getNextIndex(id_prefix);
}
@Override
public String getTitle() {
return "Dialogue "+id_prefix+index;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Map toJson() {
Map dialogueJson = new HashMap();
dialogueJson.put("id_prefix", id_prefix);
dialogueJson.put("index", index);
dialogueJson.put("text", text);
if (!replies.isEmpty()) {
List repliesJson = new ArrayList();
for (WriterReply reply : replies) {
repliesJson.add(reply.toJson());
}
dialogueJson.put("replies", repliesJson);
}
return dialogueJson;
}
public WriterDialogue(Map json) {
this.index = Integer.parseInt((String) json.get("index"));
this.id_prefix = (String) json.get("id_prefix");
this.text = (String) json.get("text");
List repliesJson = (List) json.get("replies");
for (Object rJson : repliesJson) {
this.replies.add(new WriterReply(this, (Map)rJson));
}
}
}
public class SpecialDialogue extends WriterDialogue {}
public class SelectorDialogue extends SpecialDialogue {}
public class ShopDialogue extends SpecialDialogue {}
public class FightDialogue extends SpecialDialogue {}
public class EndDialogue extends SpecialDialogue {}
public class WriterReply extends WriterNode {
public WriterDialogue parent;
public WriterDialogue next_dialogue;
public WriterReply() {}
public WriterReply(WriterDialogue parent) {
this.parent = parent;
this.text = "";
parent.replies.add(this);
}
public WriterReply(WriterDialogue parent, Map json) {
this.parent = parent;
this.text = (String) json.get("text");
if (json.containsKey("next_dialogue")) {
next_dialogue = new WriterDialogue((Map) json.get("next_dialogue"));
}
}
@Override
public String getTitle() {
return "Reply in "+parent.id_prefix+parent.index;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Map toJson() {
Map replyJson = new HashMap();
replyJson.put("text", text);
if (next_dialogue != null) {
replyJson.put("next_dialogue", next_dialogue.toJson());
}
return replyJson;
}
}
public class SpecialReply extends WriterReply {
public SpecialReply(WriterDialogue parent) {
super(parent);
}
}
public class EmptyReply extends SpecialReply {
public EmptyReply(WriterDialogue parent) {
super(parent);
}
}
@Override
public String getDesc() {
return (this.state == State.modified ? "*" : "")+begin.id_prefix;
}
@Override
public Project getProject() {
return parent.getProject();
}
@Override
public Image getIcon() {
return DefaultIcons.getDialogueIcon();
}
@Override
public Image getOpenIcon() {
return null;
}
@Override
public Image getClosedIcon() {
return null;
}
@Override
public Image getLeafIcon() {
return getIcon();
}
@Override
public GameDataSet getDataSet() {
return null;
}
@Override
public void parse() {
// TODO
}
@Override
public void link() {
//Useless here.
}
@Override
public GameDataElement clone() {
//TODO
return null;
}
@Override
public void elementChanged(GameDataElement oldOne, GameDataElement newOne) {
// Useless here.
}
@Override
public String getProjectFilename() {
return WriterModeDataSet.DEFAULT_REL_PATH_IN_PROJECT;
}
@Override
public void save() {
parent.save();
}
@Override
public List<SaveEvent> attemptSave() {
List<SaveEvent> events = parent.attemptSave();
if (events == null || events.isEmpty()) {
return null;
}
if (events.size() == 1 && events.get(0).type == SaveEvent.Type.alsoSave && events.get(0).target == this) {
save();
return null;
}
return events;
}
@SuppressWarnings("rawtypes")
public Map toJson() {
return begin.toJson();
}
}

View File

@@ -0,0 +1,233 @@
package com.gpl.rpg.atcontentstudio.model.tools;
import java.awt.Image;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.tree.TreeNode;
import org.json.simple.JSONArray;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import com.gpl.rpg.atcontentstudio.Notification;
import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter;
import com.gpl.rpg.atcontentstudio.model.GameDataElement.State;
import com.gpl.rpg.atcontentstudio.model.GameSource;
import com.gpl.rpg.atcontentstudio.model.GameSource.Type;
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
import com.gpl.rpg.atcontentstudio.model.Project;
import com.gpl.rpg.atcontentstudio.model.ProjectTreeNode;
import com.gpl.rpg.atcontentstudio.model.SaveEvent;
import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataSet;
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
public class WriterModeDataSet implements ProjectTreeNode, Serializable {
private static final long serialVersionUID = 5434504851883441971L;
public static final String DEFAULT_REL_PATH_IN_PROJECT = "writer.json";
public GameSource parent;
public File writerFile;
List<WriterModeData> writerModeDataList = new LinkedList<WriterModeData>();
public WriterModeDataSet(GameSource gameSource) {
this.parent = gameSource;
writerFile = new File(parent.baseFolder, DEFAULT_REL_PATH_IN_PROJECT);
parse();
}
@Override
public TreeNode getChildAt(int childIndex) {
return null;
}
@Override
public int getChildCount() {
return writerModeDataList.size();
}
@Override
public TreeNode getParent() {
return parent;
}
@Override
public int getIndex(TreeNode node) {
return writerModeDataList.indexOf(node);
}
@Override
public boolean getAllowsChildren() {
return true;
}
@Override
public boolean isLeaf() {
return false;
}
@Override
public Enumeration children() {
return Collections.enumeration(writerModeDataList);
}
@Override
public void childrenAdded(List<ProjectTreeNode> path) {
path.add(0,this);
parent.childrenAdded(path);
}
@Override
public void childrenChanged(List<ProjectTreeNode> path) {
path.add(0,this);
parent.childrenChanged(path);
}
@Override
public void childrenRemoved(List<ProjectTreeNode> path) {
path.add(0,this);
parent.childrenRemoved(path);
}
@Override
public void notifyCreated() {
childrenAdded(new ArrayList<ProjectTreeNode>());
}
@Override
public String getDesc() {
return "Dialogue sketchs";
}
@Override
public Project getProject() {
return parent.getProject();
}
@Override
public GameDataSet getDataSet() {
return null;
}
@Override
public Image getIcon() {
return DefaultIcons.getStdClosedIcon();
}
@Override
public Image getOpenIcon() {
return DefaultIcons.getStdOpenIcon();
}
@Override
public Image getClosedIcon() {
return DefaultIcons.getStdClosedIcon();
}
@Override
public Image getLeafIcon() {
return null;
}
@Override
public Type getDataType() {
return parent.getDataType();
}
@Override
public boolean isEmpty() {
return writerModeDataList.isEmpty();
}
@SuppressWarnings("rawtypes")
public void save() {
List<Map> dataToSave = new LinkedList<Map>();
for (WriterModeData data : writerModeDataList) {
dataToSave.add(data.toJson());
}
if (dataToSave.isEmpty() && writerFile.exists()) {
if (writerFile.delete()) {
Notification.addSuccess("File "+writerFile.getAbsolutePath()+" deleted.");
} else {
Notification.addError("Error deleting file "+writerFile.getAbsolutePath());
}
return;
}
StringWriter writer = new JsonPrettyWriter();
try {
JSONArray.writeJSONString(dataToSave, writer);
} catch (IOException e) {
//Impossible with a StringWriter
}
String toWrite = writer.toString();
try {
FileWriter w = new FileWriter(writerFile);
w.write(toWrite);
w.close();
for (WriterModeData element : writerModeDataList) {
element.state = GameDataElement.State.saved;
}
Notification.addSuccess("Json file "+writerFile.getAbsolutePath()+" saved.");
} catch (IOException e) {
Notification.addError("Error while writing json file "+writerFile.getAbsolutePath()+" : "+e.getMessage());
e.printStackTrace();
}
}
public List<SaveEvent> attemptSave() {
List<SaveEvent> events = new LinkedList<SaveEvent>();
for (WriterModeData data : writerModeDataList) {
if (data.state == State.created || data.state == State.modified) {
events.add(new SaveEvent(SaveEvent.Type.alsoSave, data));
}
}
return events;
}
public void parse() {
if (!writerFile.exists()) return;
JSONParser parser = new JSONParser();
FileReader reader = null;
try {
reader = new FileReader(writerFile);
List writerDataListJson = (List) parser.parse(reader);
for (Object obj : writerDataListJson) {
Map jsonObj = (Map)obj;
writerModeDataList.add(new WriterModeData(this, jsonObj));
}
} catch (FileNotFoundException e) {
Notification.addError("Error while parsing JSON file "+writerFile.getAbsolutePath()+": "+e.getMessage());
e.printStackTrace();
} catch (IOException e) {
Notification.addError("Error while parsing JSON file "+writerFile.getAbsolutePath()+": "+e.getMessage());
e.printStackTrace();
} catch (ParseException e) {
Notification.addError("Error while parsing JSON file "+writerFile.getAbsolutePath()+": "+e.getMessage());
e.printStackTrace();
} finally {
if (reader != null)
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -236,6 +236,15 @@ public class ProjectsTree extends JPanel {
addNextSeparator = false;
}
if (actions.testWriter.isEnabled()) {
addNextSeparator = true;
popupMenu.add(new JMenuItem(actions.testWriter));
}
if (addNextSeparator) {
popupMenu.add(new JSeparator());
addNextSeparator = false;
}
if (konamiCodeEntered) {
JMenuItem openTrainer = new JMenuItem("Start Andor's Trainer...");
popupMenu.add(openTrainer);

View File

@@ -1,5 +1,7 @@
package com.gpl.rpg.atcontentstudio.ui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
@@ -15,6 +17,7 @@ import java.util.Set;
import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.tree.TreePath;
@@ -34,9 +37,11 @@ import com.gpl.rpg.atcontentstudio.model.gamedata.JSONElement;
import com.gpl.rpg.atcontentstudio.model.maps.TMXMap;
import com.gpl.rpg.atcontentstudio.model.maps.TMXMapSet;
import com.gpl.rpg.atcontentstudio.model.saves.SavedGamesSet;
import com.gpl.rpg.atcontentstudio.model.tools.WriterModeData;
import com.gpl.rpg.atcontentstudio.ui.tools.BeanShellView;
import com.gpl.rpg.atcontentstudio.ui.tools.ItemsTableView;
import com.gpl.rpg.atcontentstudio.ui.tools.NPCsTableView;
import com.gpl.rpg.atcontentstudio.ui.tools.writermode.WriterModeEditor;
public class WorkspaceActions {
@@ -314,6 +319,20 @@ public class WorkspaceActions {
};
};
public ATCSAction testWriter = new ATCSAction("Create dialogue sketch", "Test the Writer Mode"){
public void actionPerformed(ActionEvent e) {
if (selectedNode == null || selectedNode.getProject() == null) return;
WriterModeData data = new WriterModeData(selectedNode.getProject().createdContent.writerModeDataSet, "test_");
JFrame frame = new JFrame("Writer Mode tests");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(new WriterModeEditor(data), BorderLayout.CENTER);
frame.setMinimumSize(new Dimension(250, 200));
frame.pack();
frame.setVisible(true);
};
};
List<ATCSAction> actions = new ArrayList<WorkspaceActions.ATCSAction>();
public WorkspaceActions() {
@@ -331,6 +350,7 @@ public class WorkspaceActions {
actions.add(exportProject);
actions.add(showAbout);
actions.add(exitATCS);
actions.add(testWriter);
selectionChanged(null, null);
}

View File

@@ -109,15 +109,43 @@ public class DialogueEditor extends JSONElementEditor {
private JSpinner requirementValue;
private BooleanBasedCheckBox requirementNegated;
private DialogueGraphView dialogueGraphView;
public DialogueEditor(Dialogue dialogue) {
super(dialogue, dialogue.getDesc(), dialogue.getIcon());
addEditorTab(form_view_id, getFormView());
addEditorTab(json_view_id, getJSONView());
JPanel pane = new JPanel();
addEditorTab(graph_view_id, createDialogueGraphView(dialogue));
}
public JPanel createDialogueGraphView(final Dialogue dialogue) {
final JPanel pane = new JPanel();
pane.setLayout(new BorderLayout());
pane.add(new JScrollPane(new DialogueGraphView(dialogue, null)), BorderLayout.CENTER);
addEditorTab(graph_view_id, pane);
dialogueGraphView = new DialogueGraphView(dialogue, null);
pane.add(dialogueGraphView, BorderLayout.CENTER);
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new JideBoxLayout(buttonPane, JideBoxLayout.LINE_AXIS));
JButton reloadButton = new JButton("Refresh graph");
buttonPane.add(reloadButton, JideBoxLayout.FIX);
buttonPane.add(new JPanel(), JideBoxLayout.VARY);
pane.add(buttonPane, BorderLayout.NORTH);
reloadButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pane.remove(dialogueGraphView);
dialogueGraphView = new DialogueGraphView(dialogue, null);
pane.add(dialogueGraphView, BorderLayout.CENTER);
pane.revalidate();
pane.repaint();
}
});
return pane;
}
public void insertFormViewDataField(final JPanel pane) {

View File

@@ -65,6 +65,15 @@ public abstract class JSONElementEditor extends Editor {
editorTabs.put(id, editor);
}
public void removeEditorTab(String id) {
if (id == null) return;
for (int i =0; i <editorTabsHolder.getTabCount(); i++) {
if (id.equals(editorTabsHolder.getTitleAt(i))) {
editorTabsHolder.removeTabAt(i);
editorTabs.remove(id);
}
}
}
public JPanel getJSONView() {
jsonEditorPane = new RSyntaxTextArea();
jsonEditorPane.setText(((JSONElement)target).toJsonString());

View File

@@ -47,6 +47,7 @@ public class NPCEditor extends JSONElementEditor {
private static final String form_view_id = "Form";
private static final String json_view_id = "JSON";
private static final String dialogue_tree_id = "Dialogue Tree";
private NPC.TimedConditionEffect selectedHitEffectSourceCondition;
private NPC.TimedConditionEffect selectedHitEffectTargetCondition;
@@ -96,15 +97,62 @@ public class NPCEditor extends JSONElementEditor {
private JSpinner targetConditionDuration;
private JSpinner targetConditionChance;
private JPanel dialogueGraphPane;
private DialogueGraphView dialogueGraphView;
public NPCEditor(NPC npc) {
super(npc, npc.getDesc(), npc.getIcon());
addEditorTab(form_view_id, getFormView());
addEditorTab(json_view_id, getJSONView());
if (npc.dialogue != null) {
JPanel pane = new JPanel();
pane.setLayout(new BorderLayout());
pane.add(new JScrollPane(new DialogueGraphView(npc.dialogue, npc)), BorderLayout.CENTER);
addEditorTab("Dialogue Tree", pane);
createDialogueGraphView(npc);
addEditorTab(dialogue_tree_id, dialogueGraphPane);
}
}
public JPanel createDialogueGraphView(final NPC npc) {
dialogueGraphPane = new JPanel();
dialogueGraphPane.setLayout(new BorderLayout());
dialogueGraphView = new DialogueGraphView(npc.dialogue, npc);
dialogueGraphPane.add(dialogueGraphView, BorderLayout.CENTER);
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new JideBoxLayout(buttonPane, JideBoxLayout.LINE_AXIS));
JButton reloadButton = new JButton("Refresh graph");
buttonPane.add(reloadButton, JideBoxLayout.FIX);
buttonPane.add(new JPanel(), JideBoxLayout.VARY);
dialogueGraphPane.add(buttonPane, BorderLayout.NORTH);
reloadButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
reloadGraphView(npc);
}
});
return dialogueGraphPane;
}
public void reloadGraphView(NPC npc) {
if (npc.dialogue != null) {
if (dialogueGraphPane != null) {
dialogueGraphPane.remove(dialogueGraphView);
dialogueGraphView = new DialogueGraphView(npc.dialogue, npc);
dialogueGraphPane.add(dialogueGraphView, BorderLayout.CENTER);
dialogueGraphPane.revalidate();
dialogueGraphPane.repaint();
} else {
createDialogueGraphView(npc);
addEditorTab(dialogue_tree_id, dialogueGraphPane);
}
} else {
if (dialogueGraphPane != null) {
removeEditorTab(dialogue_tree_id);
dialogueGraphPane = null;
dialogueGraphView = null;
}
}
}
@@ -487,6 +535,7 @@ public class NPCEditor extends JSONElementEditor {
} else {
npc.dialogue_id = null;
}
reloadGraphView(npc);
} else if (source == droplistBox) {
if (npc.droplist != null) {
npc.droplist.removeBacklink(npc);

View File

@@ -17,7 +17,6 @@ import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
@@ -25,8 +24,6 @@ import javax.swing.event.TableModelListener;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import org.fife.ui.rtextarea.ColorBackgroundPainterStrategy;
import com.gpl.rpg.atcontentstudio.ATContentStudio;
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
import com.gpl.rpg.atcontentstudio.model.ProjectTreeNode;
@@ -70,8 +67,8 @@ public class QuestEditor extends JSONElementEditor {
createButtonPane(pane, quest.getProject(), quest, Quest.class, quest.getImage(), null, listener);
addTextField(pane, "Internal ID: ", quest.id, quest.writable, listener);
addTextField(pane, "Quest Name: ", quest.name, quest.writable, listener);
idField = addTextField(pane, "Internal ID: ", quest.id, quest.writable, listener);
nameField = addTextField(pane, "Quest Name: ", quest.name, quest.writable, listener);
visibleBox = addIntegerBasedCheckBox(pane, "Visible in quest log", quest.visible_in_log, quest.writable, listener);
JPanel stagesPane = new JPanel();

View File

@@ -2,8 +2,6 @@ package com.gpl.rpg.atcontentstudio.ui.gamedataeditors.dialoguetree;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseEvent;
@@ -55,7 +53,6 @@ import com.gpl.rpg.atcontentstudio.model.gamedata.NPC;
import com.gpl.rpg.atcontentstudio.model.gamedata.Requirement;
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
import com.gpl.rpg.atcontentstudio.ui.gamedataeditors.DialogueEditor;
import com.gpl.rpg.atcontentstudio.ui.map.TMXMapEditor.TMXReplacementViewer;
import com.jidesoft.swing.JideBoxLayout;
public class DialogueGraphView extends Display {
@@ -66,7 +63,6 @@ public class DialogueGraphView extends Display {
public static final String NODES = "graph.nodes";
public static final String EDGES = "graph.edges";
public static final String EDGES_LABELS = "edgesLabels";
public static final String AGGR = "aggregates";
public static final String LABEL = "label";
public static final String ICON = "icon";

View File

@@ -0,0 +1,921 @@
package com.gpl.rpg.atcontentstudio.ui.tools.writermode;
import java.awt.BorderLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.plaf.basic.BasicInternalFrameUI;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.Action;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.FontAction;
import prefuse.action.assignment.StrokeAction;
import prefuse.action.layout.graph.NodeLinkTreeLayout;
import prefuse.activity.Activity;
import prefuse.controls.ControlAdapter;
import prefuse.controls.PanControl;
import prefuse.controls.WheelZoomControl;
import prefuse.controls.ZoomControl;
import prefuse.data.Edge;
import prefuse.data.FilteredSpanningTree;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Schema;
import prefuse.data.SpanningTree;
import prefuse.data.Tree;
import prefuse.data.expression.BooleanLiteral;
import prefuse.data.expression.ColumnExpression;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.EdgeRenderer;
import prefuse.render.LabelRenderer;
import prefuse.render.NullRenderer;
import prefuse.util.ColorLib;
import prefuse.util.PrefuseLib;
import prefuse.visual.VisualItem;
import prefuse.visual.expression.InGroupPredicate;
import com.gpl.rpg.atcontentstudio.model.tools.WriterModeData;
import com.gpl.rpg.atcontentstudio.model.tools.WriterModeData.EmptyReply;
import com.gpl.rpg.atcontentstudio.model.tools.WriterModeData.WriterDialogue;
import com.gpl.rpg.atcontentstudio.model.tools.WriterModeData.WriterNode;
import com.gpl.rpg.atcontentstudio.model.tools.WriterModeData.WriterReply;
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
public class WriterModeEditor extends JPanel {
private static final long serialVersionUID = -6591631891278528494L;
private JComponent overlay = null;
private Display view;
private WriterModeData data;
private WriterNode selected = null;
private WriterNode prevSelected = null;
public WriterModeEditor(WriterModeData data) {
this.data = data;
selected = data.begin;
view = new WriterGraphView();
view.setLocation(0, 0);
setLayout(new BorderLayout());
add(view, BorderLayout.CENTER);
}
private void disposeOverlay() {
if (overlay != null) view.remove(overlay);
overlay = null;
view.requestFocus();
view.revalidate();
}
public static final String GRAPH = "graph";
public static final String NODES = "graph.nodes";
public static final String NULL_NODES = "graph.nullNodes";
public static final String EDGES = "graph.edges";
public static final String EDGES_LABELS = "edgesLabels";
public static final String LABEL = "label";
public static final String ICON = "icon";
public static final String TARGET = "target";
public static final String IS_REPLY = "reply";
public static final String THREAD_START = "threadStart";
public static final String SELECTED = "selected";
public static final String IS_TREE_EDGE = "isTreeEdge";
private static final Schema DECORATOR_SCHEMA = PrefuseLib.getVisualItemSchema();
private class WriterGraphView extends Display {
private static final long serialVersionUID = -7992763190713052045L;
private MyGraph graph;
private Map<WriterModeData.WriterNode, Node> cells = new HashMap<WriterModeData.WriterNode, Node>();
private Node nullNode = null;
private Edge pendingEdge = null;
private boolean edgePending = false;
public WriterGraphView() {
super(new Visualization());
loadGraph();
nullNode = graph.addNode();
nullNode.setBoolean(NULL_NODES, true);
// add visual data groups
m_vis.addGraph(GRAPH, graph);
m_vis.setInteractive(EDGES, null, false);
LabelRenderer nodeR = new MyLabelRenderer(LABEL);
nodeR.setHorizontalTextAlignment(prefuse.Constants.LEFT);
EdgeRenderer edgeR = new EdgeRenderer(prefuse.Constants.EDGE_TYPE_LINE, prefuse.Constants.EDGE_ARROW_FORWARD);
LabelRenderer edgeLabelR = new LabelRenderer(LABEL);
edgeLabelR.setRenderType(LabelRenderer.RENDER_TYPE_DRAW);
DefaultRendererFactory drf = new DefaultRendererFactory();
drf.setDefaultRenderer(nodeR);
drf.setDefaultEdgeRenderer(edgeR);
drf.add(new InGroupPredicate(EDGES_LABELS), edgeLabelR);
drf.add(new ColumnExpression(NULL_NODES), new NullRenderer());
m_vis.setRendererFactory(drf);
DECORATOR_SCHEMA.setDefault(VisualItem.FILLCOLOR, ColorLib.gray(255));
DECORATOR_SCHEMA.setDefault(VisualItem.STROKECOLOR, ColorLib.rgba(0, 0, 0, 0));
DECORATOR_SCHEMA.setDefault(VisualItem.TEXTCOLOR, ColorLib.gray(0));
m_vis.addDecorators(EDGES_LABELS, EDGES, DECORATOR_SCHEMA);
// set up the visual operators
// first set up all the color actions
ColorAction nStrokeColor = new NodeStrokeColorAction(NODES, VisualItem.STROKECOLOR);
// nStrokeColor.setDefaultColor(ColorLib.gray(100));
// nStrokeColor.add("_hover", ColorLib.rgb(255,100,100));
StrokeAction nStroke = new StrokeAction(NODES);
ColorAction nFill = new ColorAction(NODES, VisualItem.FILLCOLOR);
nFill.setDefaultColor(ColorLib.gray(255));
// ColorAction nFill = new NPCPhraseColorAction(NODES, VisualItem.FILLCOLOR);
//
ColorAction eEdges = new ColorAction(EDGES, VisualItem.STROKECOLOR);
eEdges.setDefaultColor(ColorLib.gray(100));
ColorAction eArrows = new ColorAction(EDGES, VisualItem.FILLCOLOR);
eArrows.setDefaultColor(ColorLib.gray(100));
// ColorAction eEdgesLabels = new ConnectedEdgeColorAction(EDGES_LABELS, VisualItem.TEXTCOLOR);
// StrokeAction eStroke = new EdgesStrokeAction(EDGES);
FontAction aFont = new FontAction();
ColorAction aFontColor = new ColorAction(NODES, VisualItem.TEXTCOLOR);
aFontColor.setDefaultColor(ColorLib.rgb(0, 0, 0));
// bundle the color actions
ActionList colors = new ActionList(Activity.INFINITY);
colors.add(nStrokeColor);
colors.add(nFill);
colors.add(nStroke);
colors.add(eEdges);
colors.add(eArrows);
// colors.add(eEdgesLabels);
// colors.add(eStroke);
colors.add(aFont);
colors.add(aFontColor);
colors.add(new RepaintAction());
m_vis.putAction("colors", colors);
// now create the main layout routine
ActionList layout = new ActionList();//Activity.INFINITY);
NodeLinkTreeLayout treeLayout = new NodeLinkTreeLayout(GRAPH, prefuse.Constants.ORIENT_LEFT_RIGHT, 120, 40, 40);
treeLayout.setLayoutAnchor(new Point2D.Double(25,300));
layout.add(treeLayout);
// layout.add(new EdgesLabelDecoratorLayout(EDGES_LABELS));
layout.add(new RepaintAction());
m_vis.putAction("layout", layout);
ActionList scrollToSelectedList = new ActionList();
Action scrollToSelected = new ScrollToSelectedAction(false);
scrollToSelectedList.add(scrollToSelected);
m_vis.putAction("scrollToSelected", scrollToSelectedList);
ActionList scrollToSelectedAndEditList = new ActionList();
Action scrollToSelectedAndEdit = new ScrollToSelectedAction(true);
scrollToSelectedAndEditList.add(scrollToSelectedAndEdit);
m_vis.putAction("scrollToSelectedAndEdit", scrollToSelectedAndEditList);
// set up the display
setSize(500,500);
pan(250, 250);
setHighQuality(true);
// addControlListener(new TooltipControl());
addControlListener(new GraphInputControl());
addControlListener(new WheelZoomControl());
addControlListener(new ZoomControl());
addControlListener(new PanControl());
// set things running
m_vis.run("colors");
m_vis.run("layout");
m_vis.run("scrollToSelected");
setFocusTraversalKeysEnabled(false);
}
public void loadGraph() {
graph = new MyGraph(true, IS_TREE_EDGE);
graph.addColumn(LABEL, String.class, "");
graph.addColumn(ICON, Image.class, DefaultIcons.getNullifyIcon());
graph.addColumn(TARGET, WriterModeData.WriterNode.class, null);
graph.addColumn(IS_REPLY, boolean.class, false);
graph.addColumn(THREAD_START, boolean.class, false);
graph.addColumn(SELECTED, boolean.class, false);
graph.addColumn(IS_TREE_EDGE, boolean.class, true);
graph.addColumn(NULL_NODES, boolean.class, false);
if (data != null && data.begin != null) {
selected = data.begin;
addDialogueNode(data.begin);
}
}
public Node addDialogueNode(WriterModeData.WriterDialogue dialogue) {
if (cells.get(dialogue) == null) {
Node dNode = graph.addNode();
cells.put(dialogue, dNode);
dNode.setString(LABEL, dialogue.text);
dNode.set(TARGET, dialogue);
if (dialogue.index == 0) {
dNode.setBoolean(THREAD_START, true);
}
Node rNode;
int i = 1;
for (WriterModeData.WriterReply reply : dialogue.replies) {
if (reply instanceof EmptyReply && reply.next_dialogue != null) {
if (cells.get(reply.next_dialogue) == null) {
rNode = addDialogueNode(reply.next_dialogue);
Edge e = graph.addEdge(dNode, rNode);
} else {
rNode = cells.get(reply.next_dialogue);
Edge e = graph.addEdge(dNode, rNode);
e.setBoolean(IS_TREE_EDGE, false);
}
} else {
if (cells.get(reply) == null) {
rNode = addReplyNode(reply);
Edge e = graph.addEdge(dNode, rNode);
// e.setString(LABEL, "#"+i++);
} else {
rNode = cells.get(reply);
Edge e = graph.addEdge(dNode, rNode);
e.setBoolean(IS_TREE_EDGE, false);
}
}
}
}
return cells.get(dialogue);
}
public Node addReplyNode(WriterModeData.WriterReply reply) {
if (cells.get(reply) == null) {
Node rNode = graph.addNode();
rNode.setBoolean(IS_REPLY, true);
cells.put(reply, rNode);
if (reply.text != null) {
rNode.setString(LABEL, reply.text);
rNode.set(TARGET, reply);
if (reply.next_dialogue != null) {
if (cells.get(reply.next_dialogue) == null) {
Node dNode = addDialogueNode(reply.next_dialogue);
Edge e = graph.addEdge(rNode, dNode);
} else {
Node dNode = cells.get(reply.next_dialogue);
Edge e = graph.addEdge(rNode, dNode);
e.setBoolean(IS_TREE_EDGE, false);
}
}
}
}
return cells.get(reply);
}
class MyLabelRenderer extends LabelRenderer {
public MyLabelRenderer(String label) {
super(label);
}
@Override
protected Image getImage(VisualItem item) {
return item.getBoolean(IS_REPLY) ? DefaultIcons.getHeroIcon() : DefaultIcons.getNPCIcon();
}
@Override
protected String getText(VisualItem item) {
return wordWrap(super.getText(item), 40);
}
public String wordWrap(String in, int length) {
final String newline = "\n";
//:: Trim
while(in.length() > 0 && (in.charAt(0) == '\t' || in.charAt(0) == ' ')) in = in.substring(1);
//:: If Small Enough Already, Return Original
if(in.length() < length) return in;
//:: If Next length Contains Newline, Split There
if(in.substring(0, length).contains(newline)) return in.substring(0, in.indexOf(newline)).trim() + newline + wordWrap(in.substring(in.indexOf("\n") + 1), length);
//:: Otherwise, Split Along Nearest Previous Space/Tab/Dash
int spaceIndex = Math.max(Math.max( in.lastIndexOf(" ", length), in.lastIndexOf("\t", length)), in.lastIndexOf("-", length));
//:: If No Nearest Space, Split At length
if(spaceIndex == -1) spaceIndex = length;
//:: Split
return in.substring(0, spaceIndex).trim() + newline + wordWrap(in.substring(spaceIndex), length);
}
}
class NodeStrokeColorAction extends ColorAction {
final int defaultColor = ColorLib.gray(100);
final int hoverColor = ColorLib.rgb(255,100,100);
final int selectedColor = ColorLib.rgb(100,100,255);
public NodeStrokeColorAction(String group, String field) {
super(group, field);
}
@Override
public int getColor(VisualItem item) {
if (item.get(TARGET) != null && item.get(TARGET) == selected) {
return selectedColor;
}
if (item.isHover()) {
return hoverColor;
}
return defaultColor;
}
}
class GraphInputControl extends ControlAdapter {
@Override
public void itemClicked(VisualItem item, MouseEvent e) {
if (!edgePending) {
if (e.getClickCount() == 1) {
if (item.get(TARGET) != null) {
prevSelected = selected;
selected = (WriterModeData.WriterNode)item.get(TARGET);
}
} else if (e.getClickCount() == 2) {
if (item.get(TARGET) != null) {
showEditorOnSelectedAt(e.getPoint());
}
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
selected = null;
view.requestFocus();
}
@Override
public void keyReleased(KeyEvent e) {
if (edgePending && e.getKeyCode() == KeyEvent.VK_SHIFT) {
stopPendingEdge();
}
KeyStroke event = KeyStroke.getKeyStrokeForEvent(e);
if (event.equals(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true))) {
selectAndScroll(nextNodeUp(selected));
} else if (event.equals(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true))) {
selectAndScroll(nextNodeDown(selected));
} else if (event.equals(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true)) || event.equals(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK, true))) {
selectAndScroll(nextNodeLeft(selected));
} else if (event.equals(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true)) || event.equals(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0, true))) {
selectAndScroll(nextNodeRight(selected));
} else if (event.equals(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true))) {
showEditorOnSelected();
} else if (event.equals(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.ALT_DOWN_MASK, true))) {
createNextDefaultNode.actionPerformed(null);
} else if (event.equals(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_DOWN_MASK, true))) {
createContinueTalkingNode.actionPerformed(null);
}
}
@Override
public void itemKeyReleased(VisualItem item, KeyEvent e) {
keyReleased(e);
}
@Override
public void keyPressed(KeyEvent e) {
if (selected != null && e.getKeyCode() == KeyEvent.VK_SHIFT) {
startPendingEdge();
}
}
@Override
public void itemKeyPressed(VisualItem item, KeyEvent e) {
keyPressed(e);
}
@Override
public void mouseMoved(MouseEvent e) {
if (edgePending) {
Point p = e.getPoint();
Point2D p2 = getAbsoluteCoordinate(p, null);
m_vis.getVisualItem(NODES, nullNode).setX(p2.getX());
m_vis.getVisualItem(NODES, nullNode).setY(p2.getY());
m_vis.run("colors");
revalidate();
repaint();
disposeOverlay();
}
}
@Override
public void itemMoved(VisualItem item, MouseEvent e) {
mouseMoved(e);
}
}
public void startPendingEdge() {
pendingEdge = graph.addEdge(cells.get(selected), nullNode);
edgePending = true;
m_vis.run("colors");
m_vis.run("layout");
revalidate();
repaint();
}
public void stopPendingEdge() {
graph.removeEdge(pendingEdge);
pendingEdge = null;
edgePending = false;
m_vis.run("colors");
m_vis.run("layout");
revalidate();
repaint();
}
static final String disposeEditorString = "disposeEditor";
final AbstractAction disposeEditor = new AbstractAction("Dispose Editor") {
private static final long serialVersionUID = 6640035253411399809L;
@Override
public void actionPerformed(ActionEvent e) {
disposeOverlay();
}
};
static final String commitEditAndDisposeEditorString = "commitEditAndDisposeEditor";
final AbstractAction commitEditAndDisposeEditor = new AbstractAction("Commit Edit") {
private static final long serialVersionUID = 8039766217709796328L;
@Override
public void actionPerformed(ActionEvent e) {
if (area == null) return;
selected.text = area.getText();
cells.get(selected).set(LABEL, selected.text);
m_vis.run("colors");
revalidate();
repaint();
disposeOverlay();
}
};
private void commitAreaText() {
selected.text = area.getText();
cells.get(selected).set(LABEL, selected.text);
}
static final String createNextDefaultNodeString = "createNextDefaultNode";
final AbstractAction createNextDefaultNode = new AbstractAction("Create next default") {
private static final long serialVersionUID = 1658086056088672748L;
@Override
public void actionPerformed(ActionEvent e) {
WriterNode newWrNode = null;
Node newNode = null;
if (selected instanceof WriterDialogue) {
newWrNode = data.new WriterReply((WriterDialogue) selected);
newNode = addReplyNode(((WriterReply)newWrNode));
} else if (selected instanceof WriterReply) {
if (((WriterReply)selected).next_dialogue != null) {
newWrNode = ((WriterReply)selected).next_dialogue;
newNode = cells.get(newWrNode);
} else {
newWrNode = data.new WriterDialogue(((WriterReply)selected).parent.id_prefix);
((WriterReply)selected).next_dialogue = ((WriterDialogue)newWrNode);
newNode = addDialogueNode(((WriterDialogue)newWrNode));
}
}
Edge edge = graph.addEdge(cells.get(selected), newNode);
setSelected(newWrNode);
m_vis.run("colors");
m_vis.run("layout");
m_vis.run("scrollToSelectedAndEdit");
revalidate();
repaint();
}
};
static final String commitAndCreateNextDefaultNodeString = "commitAndCreateNextDefaultNode";
final AbstractAction commitAndCreateNextDefaultNode = new AbstractAction("Commit And Create next default") {
private static final long serialVersionUID = 1658086056088672748L;
@Override
public void actionPerformed(ActionEvent e) {
commitAreaText();
createNextDefaultNode.actionPerformed(e);
}
};
static final String createContinueTalkingNodeString = "createContinueTalkingNode";
final AbstractAction createContinueTalkingNode = new AbstractAction("Create next phrase without reply") {
private static final long serialVersionUID = 1658086056088672748L;
@Override
public void actionPerformed(ActionEvent e) {
commitAreaText();
WriterDialogue newWrNode = null;
Node newNode = null;
if (selected instanceof WriterDialogue) {
EmptyReply temp = data.new EmptyReply((WriterDialogue) selected);
newWrNode = data.new WriterDialogue(((WriterDialogue) selected).id_prefix);
temp.next_dialogue = newWrNode;
newNode = addDialogueNode(newWrNode);
Edge edge = graph.addEdge(cells.get(selected), newNode);
setSelected(newWrNode);
}
m_vis.run("colors");
m_vis.run("layout");
m_vis.run("scrollToSelectedAndEdit");
revalidate();
repaint();
}
};
class ScrollToSelectedAction extends Action {
final boolean openEditor;
public ScrollToSelectedAction(boolean openEditor) {
super();
this.openEditor = openEditor;
}
@Override
public void run(double frac) {
new Thread() {
@Override
public void run() {
if (selected == null) return;
VisualItem newItem = WriterGraphView.this.m_vis.getVisualItem(NODES, cells.get(selected));
if (prevSelected != null) {
VisualItem prevItem = m_vis.getVisualItem(NODES, cells.get(prevSelected));
Point2D target = getScreenCoordinates(new Point2D.Double(prevItem.getX(), prevItem.getY()), null);
animatePan(prevItem.getX() - newItem.getX(), prevItem.getY() - newItem.getY(), 200);
if (openEditor)showEditorOnSelectedAt(target);
} else {
animatePanToAbs(new Point2D.Double(newItem.getX(), newItem.getY()), 200);
}
}
}.start();
}
};
private void showEditorOnSelected() {
if (selected == null) return;
Node selNode = cells.get(selected);
if (selNode != null) {
VisualItem vItem = m_vis.getVisualItem(NODES, selNode);
if (vItem != null) {
showEditorOnSelectedAt(getScreenCoordinates(new Point2D.Double(vItem.getX(), vItem.getY()),null));
}
}
}
JTextArea area;
private void showEditorOnSelectedAt(final Point2D p) {
if (overlay != null) disposeOverlay();
//System.out.println(p);
area = new JTextArea(selected.text);
JInternalFrame frame = new JInternalFrame(selected.getTitle(), true);
frame.getContentPane().setLayout(new BorderLayout());
JPanel pane = new JPanel();
pane.setLayout(new BorderLayout());
pane.add(new JScrollPane(area));
frame.setSize(250, 80);
frame.setLocation(new Point((int)p.getX(), (int)p.getY()));
frame.setVisible(true);
frame.getContentPane().add(pane, BorderLayout.CENTER);
((BasicInternalFrameUI)frame.getUI()).getNorthPane().remove(0);
JButton commit = new JButton(commitEditAndDisposeEditor);
commit.setToolTipText("Save text and close editor (Ctrl + Enter)");
JButton cancel = new JButton(disposeEditor);
cancel.setToolTipText("Discard changes and close editor (Escape)");
view.add(frame);
overlay = frame;
frame.requestFocus();
area.requestFocus();
area.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
disposeOverlay();
}
@Override
public void focusGained(FocusEvent e) {}
});
area.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), disposeEditorString);
area.getActionMap().put(disposeEditorString, disposeEditor);
area.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK, true), commitEditAndDisposeEditorString);
area.getActionMap().put(commitEditAndDisposeEditorString, commitEditAndDisposeEditor);
area.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.ALT_DOWN_MASK, true), commitAndCreateNextDefaultNodeString);
area.getActionMap().put(commitAndCreateNextDefaultNodeString, commitAndCreateNextDefaultNode);
area.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_DOWN_MASK, true), createContinueTalkingNodeString);
area.getActionMap().put(createContinueTalkingNodeString, createContinueTalkingNode);
}
public void setSelected(WriterNode wrNode) {
prevSelected = selected;
selected = wrNode;
}
public void selectAndScroll(WriterNode node) {
if (node != null) {
setSelected(node);
m_vis.run("colors");
m_vis.run("layout");
m_vis.run("scrollToSelected");
revalidate();
repaint();
}
}
public void selectScrollAndEdit(WriterNode node) {
if (node != null) {
setSelected(node);
m_vis.run("colors");
m_vis.run("layout");
m_vis.run("scrollToSelectedAndEdit");
revalidate();
repaint();
}
}
public WriterNode nextNodeRight(WriterNode wrNode) {
Node node = cells.get(wrNode);
Node nextNode = graph.getFirstTreeChild(node);
return nextNode == null ? null : (WriterNode) nextNode.get(TARGET);
}
public WriterNode nextNodeLeft(WriterNode wrNode) {
Node node = cells.get(wrNode);
Node nextNode = graph.getTreeParent(node);
return nextNode == null ? null : (WriterNode) nextNode.get(TARGET);
}
public WriterNode nextNodeUp(WriterNode wrNode) {
if (wrNode == null) return null;
Node node = cells.get(wrNode);
Node nextNode = graph.getPreviousTreeSibling(node);
if (nextNode == null) nextNode = findPreviousSiblingWithLastChildAtDepth(node, 0);
return nextNode == null ? null : (WriterNode) nextNode.get(TARGET);
}
private Node findPreviousSiblingWithLastChildAtDepth(Node node, int depth) {
if (graph.getTreeParent(node) == null) return null;
Node prevSibl = graph.getPreviousTreeSibling(node);
if (prevSibl != null) {
Node candidate = findLastChildAtDepth(prevSibl, depth);
return candidate != null ? candidate : findPreviousSiblingWithLastChildAtDepth(prevSibl, depth);
} else {
return findPreviousSiblingWithLastChildAtDepth(graph.getTreeParent(node), depth + 1);
}
}
private Node findLastChildAtDepth(Node node, int depth) {
if (depth == 0) return node;
if (graph.getLastTreeChild(node) != null) return findLastChildAtDepth(graph.getLastTreeChild(node), depth - 1);
return findPreviousSiblingWithLastChildAtDepth(node, depth);
}
public WriterNode nextNodeDown(WriterNode wrNode) {
if (wrNode == null) return null;
Node node = cells.get(wrNode);
Node nextNode = graph.getNextTreeSibling(node);
if (nextNode == null) nextNode = findNextSiblingWithLastChildAtDepth(node, 0);
return nextNode == null ? null : (WriterNode) nextNode.get(TARGET);
}
private Node findNextSiblingWithLastChildAtDepth(Node node, int depth) {
if (graph.getTreeParent(node) == null) return null;
Node nextSibl = graph.getNextTreeSibling(node);
if (nextSibl != null) {
Node candidate = findFirstChildAtDepth(nextSibl, depth);
return candidate != null ? candidate : findNextSiblingWithLastChildAtDepth(nextSibl, depth);
} else {
return findNextSiblingWithLastChildAtDepth(graph.getTreeParent(node), depth + 1);
}
}
private Node findFirstChildAtDepth(Node node, int depth) {
if (depth == 0) return node;
if (graph.getFirstTreeChild(node) != null) return findFirstChildAtDepth(graph.getFirstTreeChild(node), depth - 1);
return findNextSiblingWithLastChildAtDepth(node, depth);
}
public Point2D getScreenCoordinates(Point2D abs, Point2D screen) {
return getTransform().transform(abs, screen);
}
public class MyGraph extends Graph {
private String m_spanningTreeFilter;
private FilteredSpanningTree m_filteredSpanning = null;
public MyGraph(boolean directed, String spanningFilterColumn) {
super(directed);
m_spanningTreeFilter = spanningFilterColumn;
}
public Tree getFilteredSpanningTree() {
if ( m_filteredSpanning == null )
return getFilteredSpanningTree((Node)nodes().next());
else
return m_filteredSpanning;
}
public Tree getFilteredSpanningTree(Node root) {
nodeCheck(root, true);
if ( m_filteredSpanning == null ) {
m_filteredSpanning = new FilteredSpanningTree(this, root, m_spanningTreeFilter);
} else if ( m_filteredSpanning.getRoot() != root ) {
m_filteredSpanning.buildSpanningTree(root);
}
return m_filteredSpanning;
}
@Override
protected void updateDegrees(int e, int s, int t, int incr) {
super.updateDegrees(e, s, t, incr);
clearFilteredSpanningTree();
}
public void clearFilteredSpanningTree() {
m_filteredSpanning = null;
}
public Node getTreeParent(Node n) {
return getFilteredSpanningTree().getParent(n);
}
public Node getNextTreeSibling(Node n) {
return getFilteredSpanningTree().getNextSibling(n);
}
public Node getPreviousTreeSibling(Node n) {
return getFilteredSpanningTree().getPreviousSibling(n);
}
public Node getFirstTreeChild(Node n) {
return getFilteredSpanningTree().getFirstChild(n);
}
public Node getLastTreeChild(Node n) {
return getFilteredSpanningTree().getLastChild(n);
}
}
// class TooltipControl extends ControlAdapter {
//
// @Override
// public void itemEntered(VisualItem item, MouseEvent e) {
// if (item.get(TARGET) != null) {
// tooltippedItem = item;
// if (!tooltipActivated) {
// setToolTipText("");
// ToolTipManager.sharedInstance().registerComponent(WriterGraphView.this);
// ToolTipManager.sharedInstance().setEnabled(true);
// tooltipActivated = true;
// }
// }
// }
// @Override
// public void itemExited(VisualItem item, MouseEvent e) {
// //Hides the tooltip...
// ToolTipManager.sharedInstance().setEnabled(false);
// ToolTipManager.sharedInstance().unregisterComponent(WriterGraphView.this);
// tooltipActivated = false;
// }
// }
//
// JToolTip tt = null;
// private VisualItem tooltippedItem = null;
// private VisualItem lastTTItem = null;
// private boolean tooltipActivated = false;
//
// @Override
// public Point getToolTipLocation(MouseEvent event) {
// return new Point(event.getX() + 5, event.getY() + 5);
// }
//
// @Override
// public JToolTip createToolTip() {
// if (tt == null) tt = super.createToolTip();
// if (tooltippedItem == lastTTItem) {
// return tt;
// }
// tt = super.createToolTip();
// lastTTItem = tooltippedItem;
// tt.setLayout(new BorderLayout());
// JPanel content = new JPanel();
// content.setLayout(new JideBoxLayout(content, JideBoxLayout.PAGE_AXIS));
// JLabel label;
// if (tooltippedItem != null) {
// Object target = tooltippedItem.get(TARGET);
// if (target != null) {
// if (target instanceof Dialogue) {
// Dialogue d = (Dialogue) target;
// label = new JLabel(new ImageIcon(DefaultIcons.getDialogueIcon()));
// label.setText(d.id);
// content.add(label, JideBoxLayout.FIX);
// if (tooltippedItem.get(REPLY) == null) {
// if (d.rewards != null && !d.rewards.isEmpty()) {
// for (Dialogue.Reward r : d.rewards) {
// label = new JLabel();
// DialogueEditor.decorateRewardJLabel(label, r);
// content.add(label, JideBoxLayout.FIX);
// }
// }
// } else {
// Object replObj = tooltippedItem.get(REPLY);
// if (replObj instanceof Dialogue.Reply) {
// Dialogue.Reply r = (Dialogue.Reply) replObj;
// if (r.requirements != null && !r.requirements.isEmpty()) {
// for (Requirement req : r.requirements) {
// label = new JLabel();
// DialogueEditor.decorateRequirementJLabel(label, req);
// content.add(label, JideBoxLayout.FIX);
// }
// }
// }
// }
// }
// }
//
// }
//
// tt.add(content, BorderLayout.CENTER);
// tt.setPreferredSize(tt.getLayout().preferredLayoutSize(tt));
// return tt;
// }
}
}

View File

@@ -0,0 +1,302 @@
package prefuse.data;
import java.util.BitSet;
import java.util.Iterator;
import java.util.LinkedList;
import prefuse.data.Edge;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Schema;
import prefuse.data.Table;
import prefuse.data.Tree;
import prefuse.data.Tuple;
import prefuse.data.tuple.TupleManager;
import prefuse.visual.tuple.TableEdgeItem;
public class FilteredSpanningTree extends Tree {
/** Extra edge table data field recording the id of the source edge
* a tree edge represents. */
public static final String SOURCE_EDGE = "source";
/** Edge table schema used by the spanning tree. */
protected static final Schema EDGE_SCHEMA = new Schema();
static {
EDGE_SCHEMA.addColumn(DEFAULT_SOURCE_KEY, int.class, new Integer(-1));
EDGE_SCHEMA.addColumn(DEFAULT_TARGET_KEY, int.class, new Integer(-1));
EDGE_SCHEMA.addColumn(SOURCE_EDGE, int.class);
}
/** A reference to the backing graph that this tree spans. */
protected Graph m_backing;
/** The boolean field to check. If false, edge is filtered out. */
protected int m_filter;
/**
* Create a new SpanningTree.
* @param g the backing Graph to span
* @param root the Node to use as the root of the spanning tree
* @param filterField the Edge column to use to filter out non-tree edges (must be boolean);
*/
public FilteredSpanningTree(Graph g, Node root, String filterField) {
super(g.getNodeTable(), EDGE_SCHEMA.instantiate());
m_filter = g.getEdgeTable().getColumnNumber(filterField);
if (g.getEdgeTable().getColumn(m_filter) != null && !g.getEdgeTable().getColumn(m_filter).canGetBoolean()) {
throw new UnsupportedOperationException(
"The filter column must be boolean.");
}
m_backing = g;
TupleManager etm = new TupleManager(getEdgeTable(), null,
TableEdgeItem.class) {
public Tuple getTuple(int row) {
return m_backing.getEdge(m_table.getInt(row, SOURCE_EDGE));
}
};
getEdgeTable().setTupleManager(etm);
super.setTupleManagers(g.m_nodeTuples, etm);
buildSpanningTree(root);
}
/**
* Build the spanning tree, starting at the given root. Uses an
* unweighted breadth first traversal to build the spanning tree.
* @param root the root node of the spanning tree
*/
public void buildSpanningTree(Node root) {
// re-use a previously allocated tree if possible
super.clearEdges();
super.setRoot(root);
// build unweighted spanning tree by BFS
LinkedList q = new LinkedList();
BitSet visit = new BitSet();
q.add(root); visit.set(root.getRow());
Table edges = getEdgeTable();
while ( !q.isEmpty() ) {
Node p = (Node)q.removeFirst();
for ( Iterator iter = p.edges(); iter.hasNext(); ) {
Edge e = (Edge)iter.next();
if (e.getBoolean(m_filter)) {
Node n = e.getAdjacentNode(p);
if ( !visit.get(n.getRow()) ) {
q.add(n); visit.set(n.getRow());
int er = super.addChildEdge(p.getRow(), n.getRow());
edges.setInt(er, SOURCE_EDGE, e.getRow());
}
}
}
}
}
// ------------------------------------------------------------------------
// Disallow most mutator methods
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Tree#addChild(int)
*/
public int addChild(int parent) {
throw new UnsupportedOperationException(
"Changes to tree structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Tree#addChild(prefuse.data.Node)
*/
public Node addChild(Node parent) {
throw new UnsupportedOperationException(
"Changes to tree structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Tree#addChildEdge(int, int)
*/
public int addChildEdge(int parent, int child) {
throw new UnsupportedOperationException(
"Changes to tree structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Tree#addChildEdge(prefuse.data.Node, prefuse.data.Node)
*/
public Edge addChildEdge(Node parent, Node child) {
throw new UnsupportedOperationException(
"Changes to tree structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Tree#addRoot()
*/
public Node addRoot() {
throw new UnsupportedOperationException(
"Changes to tree structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Tree#addRootRow()
*/
public int addRootRow() {
throw new UnsupportedOperationException(
"Changes to tree structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Tree#removeChild(int)
*/
public boolean removeChild(int node) {
throw new UnsupportedOperationException(
"Changes to tree structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Tree#removeChild(prefuse.data.Node)
*/
public boolean removeChild(Node n) {
throw new UnsupportedOperationException(
"Changes to tree structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Tree#removeChildEdge(prefuse.data.Edge)
*/
public boolean removeChildEdge(Edge e) {
throw new UnsupportedOperationException(
"Changes to tree structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Tree#removeChildEdge(int)
*/
public boolean removeChildEdge(int edge) {
throw new UnsupportedOperationException(
"Changes to tree structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Tree#setRoot(prefuse.data.Node)
*/
void setRoot(Node root) {
throw new UnsupportedOperationException(
"Changes to tree structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Graph#addEdge(int, int)
*/
public int addEdge(int s, int t) {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Graph#addEdge(prefuse.data.Node, prefuse.data.Node)
*/
public Edge addEdge(Node s, Node t) {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Graph#addNode()
*/
public Node addNode() {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Graph#addNodeRow()
*/
public int addNodeRow() {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.tuple.TupleSet#clear()
*/
public void clear() {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Graph#removeEdge(prefuse.data.Edge)
*/
public boolean removeEdge(Edge e) {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Graph#removeEdge(int)
*/
public boolean removeEdge(int edge) {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Graph#removeNode(int)
*/
public boolean removeNode(int node) {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Graph#removeNode(prefuse.data.Node)
*/
public boolean removeNode(Node n) {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.tuple.TupleSet#removeTuple(prefuse.data.Tuple)
*/
public boolean removeTuple(Tuple t) {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Graph#setEdgeTable(prefuse.data.Table)
*/
public void setEdgeTable(Table edges) {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
/**
* Unsupported operation. Spanning trees should not be edited.
* @see prefuse.data.Graph#setTupleManagers(prefuse.data.tuple.TupleManager, prefuse.data.tuple.TupleManager)
*/
public void setTupleManagers(TupleManager ntm, TupleManager etm) {
throw new UnsupportedOperationException(
"Changes to graph structure not allowed for spanning trees.");
}
}