checks for file modifications

This commit is contained in:
bancika
2011-01-15 14:04:33 +00:00
parent d16ee1d890
commit 52df431beb
8 changed files with 248 additions and 52 deletions
@@ -26,6 +26,12 @@ public enum EventType {
* or loaded with undo/redo operations.
*/
PROJECT_LOADED,
/**
* Called when either current file name or modified flag are changed. The
* first parameter is the current file name, the second one is a boolean
* modified flag.
*/
FILE_STATUS_CHANGED,
/**
* Called when selection rectangle is changed. Object of type
* {@link Rectangle} is the only parameter and it contains the new selection
@@ -74,6 +74,18 @@ public interface IPlugInPort {
*/
void loadProject(Project project, boolean freshStart);
void createNewProject();
void loadProjectFromFile(String fileName);
void saveProjectToFile(String fileName);
String getCurrentFileName();
boolean isProjectModified();
boolean allowFileAction();
/**
* Returns all available {@link ComponentType}s classified by category.
* Result is a {@link Map} between category name to a {@link List} of all
@@ -45,8 +45,8 @@ public class Project implements Serializable {
gridSpacing = DEFAULT_GRID_SPACING;
}
public Project(String title, String author, String description, Size width, Size height, Size gridSpacing,
List<IDIYComponent<?>> components, Set<Set<IDIYComponent<?>>> groups) {
public Project(String title, String author, String description, Size width, Size height,
Size gridSpacing, List<IDIYComponent<?>> components, Set<Set<IDIYComponent<?>>> groups) {
super();
this.title = title;
this.author = author;
@@ -103,7 +103,7 @@ public class Project implements Serializable {
this.height = height;
}
@EditableProperty
@EditableProperty(name = "Grid spacing")
public Size getGridSpacing() {
return gridSpacing;
}
@@ -47,4 +47,8 @@ public interface IView {
* @param parentMenuName
*/
void addSubmenu(String name, Icon icon, String parentMenuName);
void showMessage(String message, String title, int messageType);
int showConfirmDialog(String message, String title, int optionType, int messageType);
}
@@ -7,6 +7,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
@@ -17,12 +18,15 @@ import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import org.diylc.common.BadPositionException;
import org.diylc.core.Project;
import org.diylc.common.EventType;
import org.diylc.common.IPlugIn;
import org.diylc.common.IPlugInPort;
import org.diylc.plugins.canvas.CanvasPlugin;
import org.diylc.plugins.clipboard.ClipboardManager;
import org.diylc.plugins.file.FileManager;
@@ -50,7 +54,7 @@ public class MainFrame extends JFrame implements IView {
public MainFrame() {
super("DIYLC 3");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
setPreferredSize(new Dimension(800, 600));
createBasePanels();
menuMap = new HashMap<String, JMenu>();
@@ -58,8 +62,6 @@ public class MainFrame extends JFrame implements IView {
this.presenter = new Presenter(this);
setTitle(getTitle() + " v." + presenter.getCurrentVersionNumber());
presenter.installPlugin(new ToolBox());
presenter.installPlugin(new FileManager());
presenter.installPlugin(new ClipboardManager());
@@ -69,21 +71,28 @@ public class MainFrame extends JFrame implements IView {
presenter.installPlugin(new StatusBar());
canvasPlugin = new CanvasPlugin();
presenter.installPlugin(canvasPlugin);
presenter.installPlugin(new FramePlugin());
Project project = new Project();
// project.getComponents().add(new MockDIYComponent());
presenter.loadProject(project, true);
presenter.createNewProject();
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
presenter.dispose();
if (presenter.allowFileAction()) {
dispose();
presenter.dispose();
System.exit(0);
}
}
@Override
public void windowClosing(WindowEvent e) {
presenter.dispose();
if (presenter.allowFileAction()) {
dispose();
presenter.dispose();
System.exit(0);
}
}
});
@@ -195,6 +204,16 @@ public class MainFrame extends JFrame implements IView {
menuMap.put(name, submenu);
}
@Override
public void showMessage(String message, String title, int messageType) {
JOptionPane.showMessageDialog(this, message, title, messageType);
}
@Override
public int showConfirmDialog(String message, String title, int optionType, int messageType) {
return JOptionPane.showConfirmDialog(this, message, title, optionType, messageType);
}
private JMenu findOrCreateMenu(String menuName) {
JMenu menu;
if (menuMap.containsKey(menuName)) {
@@ -206,4 +225,31 @@ public class MainFrame extends JFrame implements IView {
}
return menu;
}
class FramePlugin implements IPlugIn {
private IPlugInPort plugInPort;
@Override
public void connect(IPlugInPort plugInPort) {
this.plugInPort = plugInPort;
}
@Override
public EnumSet<EventType> getSubscribedEventTypes() {
return EnumSet.of(EventType.FILE_STATUS_CHANGED);
}
@Override
public void processMessage(EventType eventType, Object... params) {
if (eventType == EventType.FILE_STATUS_CHANGED) {
String fileName = plugInPort.getCurrentFileName() == null ? "Untitled" : plugInPort
.getCurrentFileName();
String modified = plugInPort.isProjectModified() ? " (modified)" : "";
setTitle(String.format("DIYLC 3.%s - %s %s", plugInPort.getCurrentVersionNumber(),
fileName, modified));
}
}
}
}
@@ -2,7 +2,6 @@ package org.diylc.plugins.file;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -15,7 +14,6 @@ import org.apache.log4j.Logger;
import org.diylc.common.EventType;
import org.diylc.common.IPlugIn;
import org.diylc.common.IPlugInPort;
import org.diylc.core.Project;
import org.diylc.gui.DialogFactory;
import org.diylc.images.IconLoader;
@@ -79,8 +77,10 @@ public class FileManager implements IPlugIn {
@Override
public void actionPerformed(ActionEvent e) {
LOG.info("Creating blank project.");
plugInPort.loadProject(new Project(), true);
if (!plugInPort.allowFileAction()) {
return;
}
plugInPort.createNewProject();
}
}
@@ -96,23 +96,13 @@ public class FileManager implements IPlugIn {
@Override
public void actionPerformed(ActionEvent e) {
if (!plugInPort.allowFileAction()) {
return;
}
File file = DialogFactory.getInstance().showOpenDialog(FileFilterEnum.DIY.getFilter(),
null, FileFilterEnum.DIY.getExtensions()[0], new ProjectPreview());
if (file != null) {
LOG.info("Loading project from file.");
FileInputStream fis;
try {
fis = new FileInputStream(file);
Project project = (Project) xStream.fromXML(fis);
plugInPort.loadProject(project, true);
fis.close();
} catch (FileNotFoundException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
} catch (IOException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
plugInPort.loadProjectFromFile(file.getAbsolutePath());
}
}
}
@@ -277,7 +267,9 @@ public class FileManager implements IPlugIn {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
if (plugInPort.allowFileAction()) {
System.exit(0);
}
}
}
@@ -64,6 +64,16 @@ public class ProjectPreview extends JPanel implements PropertyChangeListener, IF
@Override
public void addSubmenu(String name, Icon icon, String parentMenuName) {
}
@Override
public int showConfirmDialog(String message, String title, int optionType,
int messageType) {
return 0;
}
@Override
public void showMessage(String message, String title, int messageType) {
}
});
xStream = new XStream(new DomDriver());
@@ -137,12 +147,12 @@ public class ProjectPreview extends JPanel implements PropertyChangeListener, IF
zoomRatio = rect.getHeight() / d.getHeight();
}
// d.setSize(d.getWidth() * zoomRatio, d.getHeight() * zoomRatio);
// int x = (int) (rect.getWidth() - d.getWidth() * zoomRatio) / 2;
// int y = (int) (rect.getHeight() - d.getHeight() * zoomRatio) / 2;
// int x = (int) (rect.getWidth() - d.getWidth() * zoomRatio) / 2;
// int y = (int) (rect.getHeight() - d.getHeight() * zoomRatio) / 2;
// System.out.println(x + "," + y);
// g2d.translate(x, y);
// g2d.translate(x, y);
g2d.scale(zoomRatio, zoomRatio);
presenter.draw(g2d, EnumSet.noneOf(DrawOption.class), null);
@@ -10,6 +10,11 @@ import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
@@ -24,6 +29,7 @@ import java.util.Set;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import org.apache.log4j.Logger;
import org.diylc.common.BadPositionException;
@@ -48,6 +54,8 @@ import com.diyfever.gui.miscutils.Utils;
import com.diyfever.gui.simplemq.MessageDispatcher;
import com.diyfever.gui.update.VersionNumber;
import com.rits.cloning.Cloner;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
/**
* The main presenter class, contains core app logic and drawing routines.
@@ -70,6 +78,8 @@ public class Presenter implements IPlugInPort {
private double zoomLevel = 1;
private Map<IDIYComponent<?>, Area> componentAreaMap;
private Project currentProject;
private String currentFileName = null;
private boolean modified = false;
private Map<String, List<ComponentType>> componentTypes;
// Maps component class names to ComponentType objects.
private Map<String, ComponentType> componentTypeMap;
@@ -80,7 +90,9 @@ public class Presenter implements IPlugInPort {
// that designate which of their control points are being dragged.
private Map<IDIYComponent<?>, Set<Integer>> controlPointMap;
// Utilities
private Cloner cloner;
private XStream xStream = new XStream(new DomDriver());
private Rectangle selectionRect;
@@ -187,6 +199,82 @@ public class Presenter implements IPlugInPort {
messageDispatcher.dispatchMessage(EventType.REPAINT);
}
@Override
public void createNewProject() {
LOG.info("createNewFile()");
try {
Project project = new Project();
setDefaultProperties(project);
loadProject(project, true);
this.currentFileName = null;
this.modified = false;
fireFileStatusChanged();
} catch (Exception e) {
LOG.error("Could not create new file", e);
}
}
@Override
public void loadProjectFromFile(String fileName) {
LOG.info(String.format("loadProjectFromFile(%s)", fileName));
try {
FileInputStream fis = new FileInputStream(fileName);
Project project = (Project) xStream.fromXML(fis);
loadProject(project, true);
fis.close();
this.currentFileName = fileName;
this.modified = false;
fireFileStatusChanged();
} catch (FileNotFoundException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
} catch (IOException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
}
@Override
public boolean allowFileAction() {
if (this.modified) {
int response = view.showConfirmDialog(
"There are unsaved changes. Are you sure you want to abandon these changes?",
"Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
return response == JOptionPane.YES_OPTION;
}
return true;
}
@Override
public void saveProjectToFile(String fileName) {
LOG.info(String.format("saveProjectToFile(%s)", fileName));
FileOutputStream fos;
try {
fos = new FileOutputStream(fileName);
xStream.toXML(currentProject, fos);
fos.close();
this.currentFileName = fileName;
this.modified = false;
fireFileStatusChanged();
} catch (FileNotFoundException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
} catch (IOException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
}
@Override
public String getCurrentFileName() {
return this.currentFileName;
}
@Override
public boolean isProjectModified() {
return this.modified;
}
@SuppressWarnings("unchecked")
@Override
public Map<String, List<ComponentType>> getComponentTypes() {
@@ -699,6 +787,7 @@ public class Presenter implements IPlugInPort {
if (!preDragProject.equals(currentProject)) {
messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, preDragProject, cloner
.deepClone(currentProject), "Move");
fireFileStatusChanged();
}
fireSelectionChanged();
messageDispatcher.dispatchMessage(EventType.REPAINT);
@@ -714,18 +803,26 @@ public class Presenter implements IPlugInPort {
}
messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, oldProject, cloner
.deepClone(currentProject), "Add");
this.modified = true;
fireFileStatusChanged();
messageDispatcher.dispatchMessage(EventType.REPAINT);
}
@Override
public void deleteSelectedComponents() {
LOG.debug("deleteSelectedComponents()");
if (selectedComponents.isEmpty()) {
LOG.debug("Nothing to delete");
return;
}
Project oldProject = cloner.deepClone(currentProject);
// Remove selected components from any groups.
ungroupComponents(selectedComponents);
currentProject.getComponents().removeAll(selectedComponents);
messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, oldProject, cloner
.deepClone(currentProject), "Delete");
this.modified = true;
fireFileStatusChanged();
messageDispatcher.dispatchMessage(EventType.REPAINT);
}
@@ -787,6 +884,8 @@ public class Presenter implements IPlugInPort {
if (!oldProject.equals(currentProject)) {
messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, oldProject, cloner
.deepClone(currentProject), "Group");
this.modified = true;
fireFileStatusChanged();
}
}
@@ -800,6 +899,8 @@ public class Presenter implements IPlugInPort {
if (!oldProject.equals(currentProject)) {
messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, oldProject, cloner
.deepClone(currentProject), "Group");
this.modified = true;
fireFileStatusChanged();
}
}
@@ -819,6 +920,11 @@ public class Presenter implements IPlugInPort {
calculateSelectionDimension());
}
private void fireFileStatusChanged() {
messageDispatcher.dispatchMessage(EventType.FILE_STATUS_CHANGED, this.currentFileName,
this.modified);
}
/**
* Removes all the groups that contain at least one of the specified
* components.
@@ -956,24 +1062,14 @@ public class Presenter implements IPlugInPort {
}
}
// Extract properties.
List<PropertyWrapper> properties = ComponentProcessor.getInstance().extractProperties(
componentType.getInstanceClass());
// Override with default values if available.
for (PropertyWrapper property : properties) {
Object defaultValue = ConfigurationManager.getInstance().getConfigurationItem(
DEFAULTS_KEY + componentType.getInstanceClass().getName() + ":"
+ property.getName());
if (defaultValue != null) {
property.setValue(cloner.deepClone(defaultValue));
property.writeTo(component);
}
}
setDefaultProperties(component);
// Notify the listeners.
if (!oldProject.equals(currentProject)) {
messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, oldProject, cloner
.deepClone(currentProject), "Add " + componentType.getName());
this.modified = true;
fireFileStatusChanged();
}
fireSelectionChanged();
@@ -984,6 +1080,32 @@ public class Presenter implements IPlugInPort {
messageDispatcher.dispatchMessage(EventType.REPAINT);
}
/**
* Finds any properties that have default values and injects default values.
* Typically it should be used for {@link IDIYComponent} and {@link Project}
* objects.
*
* @param object
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private void setDefaultProperties(Object object) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
// Extract properties.
List<PropertyWrapper> properties = ComponentProcessor.getInstance().extractProperties(
object.getClass());
// Override with default values if available.
for (PropertyWrapper property : properties) {
Object defaultValue = ConfigurationManager.getInstance().getConfigurationItem(
DEFAULTS_KEY + object.getClass().getName() + ":" + property.getName());
if (defaultValue != null) {
property.setValue(cloner.deepClone(defaultValue));
property.writeTo(object);
}
}
}
@Override
public List<PropertyWrapper> getMutualSelectionProperties() {
try {
@@ -1012,6 +1134,8 @@ public class Presenter implements IPlugInPort {
if (!oldProject.equals(currentProject)) {
messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, oldProject, cloner
.deepClone(currentProject), "Edit Selection");
this.modified = true;
fireFileStatusChanged();
}
messageDispatcher.dispatchMessage(EventType.REPAINT);
messageDispatcher.dispatchMessage(EventType.SELECTION_SIZE_CHANGED,
@@ -1049,6 +1173,8 @@ public class Presenter implements IPlugInPort {
if (!oldProject.equals(currentProject)) {
messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, oldProject, cloner
.deepClone(currentProject), "Edit Project");
this.modified = true;
fireFileStatusChanged();
}
messageDispatcher.dispatchMessage(EventType.REPAINT);
messageDispatcher.dispatchMessage(EventType.ZOOM_CHANGED, zoomLevel);