Compare commits
6 commits
7879178b1a
...
3b0eb9052d
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3b0eb9052d | ||
![]() |
adbdfd3b99 | ||
![]() |
7b3ddf17e0 | ||
![]() |
a56a3be9ef | ||
![]() |
81e22d094b | ||
![]() |
4b47ea8d59 |
28 changed files with 630 additions and 95 deletions
59
app/src/main/java/xl/controller/EditorController.java
Normal file
59
app/src/main/java/xl/controller/EditorController.java
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package xl.controller;
|
||||||
|
|
||||||
|
import xl.model.XLModel;
|
||||||
|
import xl.gui.Editor;
|
||||||
|
import xl.gui.SlotLabel;
|
||||||
|
import xl.gui.StatusLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The controller for the editor.
|
||||||
|
*
|
||||||
|
* The controller is responsible for handling the editor actions.
|
||||||
|
*/
|
||||||
|
public class EditorController {
|
||||||
|
private XLModel xlModel;
|
||||||
|
private Editor editor;
|
||||||
|
private SlotLabel currentSlot;
|
||||||
|
private StatusLabel statusLabel;
|
||||||
|
|
||||||
|
public EditorController(XLModel xlModel, StatusLabel statusLabel) {
|
||||||
|
this.xlModel = xlModel;
|
||||||
|
this.statusLabel = statusLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentSlot(SlotLabel currentSlot) {
|
||||||
|
this.currentSlot = currentSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEditor(Editor editor) {
|
||||||
|
this.editor = editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleEditorAction() {
|
||||||
|
String content = editor.getText();
|
||||||
|
String address = currentSlot.getAddress();
|
||||||
|
|
||||||
|
if (content.isEmpty()) {
|
||||||
|
boolean isOkRemove = xlModel.remove(address);
|
||||||
|
if (isOkRemove) {
|
||||||
|
currentSlot.setText("");
|
||||||
|
statusLabel.setText("");
|
||||||
|
} else {
|
||||||
|
statusLabel.setText(xlModel.getStatus());
|
||||||
|
}
|
||||||
|
} else if (content.charAt(0) == '#') {
|
||||||
|
xlModel.add(address, content);
|
||||||
|
currentSlot.setText(content.substring(1));
|
||||||
|
statusLabel.setText("");
|
||||||
|
} else {
|
||||||
|
xlModel.add(address, content);
|
||||||
|
if (xlModel.getStatus().equals("")) {
|
||||||
|
content = xlModel.getAddressValue(address);
|
||||||
|
currentSlot.setText(content);
|
||||||
|
} else {
|
||||||
|
statusLabel.setText(xlModel.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
app/src/main/java/xl/controller/SlotLabelController.java
Normal file
60
app/src/main/java/xl/controller/SlotLabelController.java
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package xl.controller;
|
||||||
|
import java.awt.Color;
|
||||||
|
import xl.model.XLModel;
|
||||||
|
import xl.gui.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The controller for the slot labels.
|
||||||
|
*
|
||||||
|
* The controller is responsible for handling the slot label actions.
|
||||||
|
*/
|
||||||
|
public class SlotLabelController {
|
||||||
|
private XLModel xlModel;
|
||||||
|
private Editor editor;
|
||||||
|
private SlotLabel currentSlot;
|
||||||
|
private EditorController editorController;
|
||||||
|
private StatusLabel statusLabel;
|
||||||
|
private CurrentLabel currentLabel;
|
||||||
|
|
||||||
|
public SlotLabelController(XLModel xlModel, Editor editor, StatusLabel statusLabel) {
|
||||||
|
this.xlModel = xlModel;
|
||||||
|
this.editor = editor;
|
||||||
|
this.statusLabel = statusLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentLabel(CurrentLabel currentLabel) {
|
||||||
|
this.currentLabel = currentLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentSlot(SlotLabel currentSlot) {
|
||||||
|
this.currentSlot = currentSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEditorController(EditorController editorController) {
|
||||||
|
this.editorController = editorController;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleSlotClick(SlotLabel slotLabel) {
|
||||||
|
// Retrieve the address of the clicked slot
|
||||||
|
String address = slotLabel.getAddress();
|
||||||
|
currentLabel.setText(address);
|
||||||
|
xlModel.setCurrentAddress(address);
|
||||||
|
|
||||||
|
// Clear the status label
|
||||||
|
statusLabel.setText("");
|
||||||
|
|
||||||
|
// Reset the background color of the previous slot
|
||||||
|
currentSlot.setBackground(Color.WHITE);
|
||||||
|
|
||||||
|
// Set the background color of the clicked slot
|
||||||
|
slotLabel.setBackground(Color.YELLOW);
|
||||||
|
currentSlot = slotLabel;
|
||||||
|
editorController.setCurrentSlot(currentSlot);
|
||||||
|
|
||||||
|
// Retrieve the content of the clicked slot
|
||||||
|
String content = xlModel.getAddressContent(address);
|
||||||
|
|
||||||
|
// Set the content of the clicked slot in the editor
|
||||||
|
editor.setText(content);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,9 +3,21 @@ package xl.gui;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
public class Editor extends JTextField {
|
import xl.controller.EditorController;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
public Editor() {
|
public class Editor extends JTextField {
|
||||||
|
|
||||||
|
public Editor(EditorController editorController) {
|
||||||
setBackground(Color.WHITE);
|
setBackground(Color.WHITE);
|
||||||
|
|
||||||
|
// Listen for changes in the text
|
||||||
|
addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent ae) {
|
||||||
|
editorController.handleEditorAction();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,15 @@ package xl.gui;
|
||||||
import static java.awt.BorderLayout.CENTER;
|
import static java.awt.BorderLayout.CENTER;
|
||||||
import static java.awt.BorderLayout.WEST;
|
import static java.awt.BorderLayout.WEST;
|
||||||
|
|
||||||
|
import xl.controller.EditorController;
|
||||||
|
import xl.controller.SlotLabelController;
|
||||||
|
import xl.model.XLModel;
|
||||||
|
|
||||||
public class SheetPanel extends BorderPanel {
|
public class SheetPanel extends BorderPanel {
|
||||||
|
|
||||||
public SheetPanel(int rows, int columns) {
|
public SheetPanel(int rows, int columns, XLModel xlModel, SlotLabelController slotLabelController,
|
||||||
|
EditorController editorController) {
|
||||||
add(WEST, new RowLabels(rows));
|
add(WEST, new RowLabels(rows));
|
||||||
add(CENTER, new SlotLabels(rows, columns));
|
add(CENTER, new SlotLabels(rows, columns, xlModel, slotLabelController, editorController));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,45 @@
|
||||||
package xl.gui;
|
package xl.gui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import xl.controller.SlotLabelController;
|
||||||
|
import java.util.Observer;
|
||||||
|
import java.util.Observable;
|
||||||
|
import xl.model.XLModel;
|
||||||
|
|
||||||
public class SlotLabel extends ColoredLabel {
|
public class SlotLabel extends ColoredLabel implements Observer {
|
||||||
|
private String address;
|
||||||
|
|
||||||
public SlotLabel() {
|
public SlotLabel(SlotLabelController slotLabelController, int row, char column) {
|
||||||
super(" ", Color.WHITE, RIGHT);
|
super(" ", Color.WHITE, RIGHT);
|
||||||
|
this.address = "" + column + row;
|
||||||
|
|
||||||
|
// MouseListener for mouse clicks
|
||||||
|
addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
slotLabelController.handleSlotClick(SlotLabel.this);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(Observable o, Object arg) {
|
||||||
|
if (o instanceof XLModel) {
|
||||||
|
XLModel xlModel = (XLModel) o;
|
||||||
|
if (xlModel.containsKey(address)) {
|
||||||
|
String tempCurrentAdress = xlModel.getAddressContent(address);
|
||||||
|
xlModel.setCurrentAddress(address);
|
||||||
|
String content = xlModel.getAddressValue(address);
|
||||||
|
setText(content);
|
||||||
|
xlModel.setCurrentAddress(tempCurrentAdress);
|
||||||
|
} else {
|
||||||
|
setText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,25 +4,35 @@ import java.awt.Color;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
|
import xl.model.XLModel;
|
||||||
|
import xl.controller.EditorController;
|
||||||
|
import xl.controller.SlotLabelController;
|
||||||
|
|
||||||
public class SlotLabels extends GridPanel {
|
public class SlotLabels extends GridPanel {
|
||||||
|
|
||||||
private List<SlotLabel> labelList;
|
private List<SlotLabel> labelList;
|
||||||
|
|
||||||
public SlotLabels(int rows, int cols) {
|
public SlotLabels(int rows, int cols, XLModel xlModel, SlotLabelController slotLabelController,
|
||||||
|
EditorController editorController) {
|
||||||
super(rows + 1, cols);
|
super(rows + 1, cols);
|
||||||
labelList = new ArrayList<SlotLabel>(rows * cols);
|
labelList = new ArrayList<SlotLabel>(rows * cols);
|
||||||
for (char ch = 'A'; ch < 'A' + cols; ch++) {
|
for (char ch = 'A'; ch < 'A' + cols; ch++) {
|
||||||
add(new ColoredLabel(Character.toString(ch), Color.LIGHT_GRAY, SwingConstants.CENTER));
|
add(new ColoredLabel(Character.toString(ch), Color.LIGHT_GRAY, SwingConstants.CENTER));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int row = 1; row <= rows; row++) {
|
for (int row = 1; row <= rows; row++) {
|
||||||
for (char ch = 'A'; ch < 'A' + cols; ch++) {
|
for (char ch = 'A'; ch < 'A' + cols; ch++) {
|
||||||
SlotLabel label = new SlotLabel();
|
SlotLabel label = new SlotLabel(slotLabelController, row, ch);
|
||||||
|
xlModel.addObserver(label);
|
||||||
add(label);
|
add(label);
|
||||||
labelList.add(label);
|
labelList.add(label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SlotLabel firstLabel = labelList.get(0);
|
SlotLabel firstLabel = labelList.get(0);
|
||||||
firstLabel.setBackground(Color.YELLOW);
|
firstLabel.setBackground(Color.YELLOW);
|
||||||
|
xlModel.setCurrentAddress("A1");
|
||||||
|
slotLabelController.setCurrentSlot(firstLabel);
|
||||||
|
editorController.setCurrentSlot(firstLabel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
package xl.gui;
|
package xl.gui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.util.Observable;
|
|
||||||
import java.util.Observer;
|
|
||||||
|
|
||||||
public class StatusLabel extends ColoredLabel implements Observer {
|
public class StatusLabel extends ColoredLabel {
|
||||||
|
|
||||||
public StatusLabel() {
|
public StatusLabel() {
|
||||||
super("", Color.WHITE);
|
super("", Color.WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(Observable observable, Object object) {
|
|
||||||
setText("");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,15 @@ package xl.gui;
|
||||||
import static java.awt.BorderLayout.CENTER;
|
import static java.awt.BorderLayout.CENTER;
|
||||||
import static java.awt.BorderLayout.WEST;
|
import static java.awt.BorderLayout.WEST;
|
||||||
|
|
||||||
|
import xl.controller.SlotLabelController;
|
||||||
|
import xl.model.XLModel;
|
||||||
|
|
||||||
public class StatusPanel extends BorderPanel {
|
public class StatusPanel extends BorderPanel {
|
||||||
|
|
||||||
protected StatusPanel(StatusLabel statusLabel) {
|
protected StatusPanel(StatusLabel statusLabel, XLModel solver, SlotLabelController slotLabelController) {
|
||||||
add(WEST, new CurrentLabel());
|
CurrentLabel currentLabel = new CurrentLabel();
|
||||||
|
slotLabelController.setCurrentLabel(currentLabel);
|
||||||
|
add(WEST, currentLabel);
|
||||||
add(CENTER, statusLabel);
|
add(CENTER, statusLabel);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,11 @@ import static java.awt.BorderLayout.SOUTH;
|
||||||
|
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
|
import xl.controller.EditorController;
|
||||||
|
import xl.controller.SlotLabelController;
|
||||||
import xl.gui.menu.XLMenuBar;
|
import xl.gui.menu.XLMenuBar;
|
||||||
|
import xl.model.XLModel;
|
||||||
|
|
||||||
public class XL extends JFrame {
|
public class XL extends JFrame {
|
||||||
|
|
||||||
|
@ -15,23 +19,31 @@ public class XL extends JFrame {
|
||||||
private StatusLabel statusLabel = new StatusLabel();
|
private StatusLabel statusLabel = new StatusLabel();
|
||||||
private XLList xlList;
|
private XLList xlList;
|
||||||
|
|
||||||
public XL(XL oldXL) {
|
public XL(XL oldXL, XLModel XLModel) {
|
||||||
this(oldXL.xlList, oldXL.counter);
|
this(oldXL.xlList, oldXL.counter, XLModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public XL(XLList xlList, XLCounter counter) {
|
public XL(XLList xlList, XLCounter counter, XLModel XLModel) {
|
||||||
super("Untitled-" + counter);
|
super("Window " + counter);
|
||||||
this.xlList = xlList;
|
this.xlList = xlList;
|
||||||
this.counter = counter;
|
this.counter = counter;
|
||||||
xlList.add(this);
|
xlList.add(this);
|
||||||
counter.increment();
|
counter.increment();
|
||||||
JPanel statusPanel = new StatusPanel(statusLabel);
|
|
||||||
JPanel sheetPanel = new SheetPanel(ROWS, COLUMNS);
|
EditorController editorController = new EditorController(XLModel, statusLabel);
|
||||||
Editor editor = new Editor();
|
Editor editor = new Editor(editorController);
|
||||||
|
editorController.setEditor(editor);
|
||||||
|
|
||||||
|
SlotLabelController slotLabelController = new SlotLabelController(XLModel, editor, statusLabel);
|
||||||
|
slotLabelController.setEditorController(editorController);
|
||||||
|
|
||||||
|
JPanel statusPanel = new StatusPanel(statusLabel, XLModel, slotLabelController);
|
||||||
|
JPanel sheetPanel = new SheetPanel(ROWS, COLUMNS, XLModel, slotLabelController, editorController);
|
||||||
|
|
||||||
add(NORTH, statusPanel);
|
add(NORTH, statusPanel);
|
||||||
add(CENTER, editor);
|
add(CENTER, editor);
|
||||||
add(SOUTH, sheetPanel);
|
add(SOUTH, sheetPanel);
|
||||||
setJMenuBar(new XLMenuBar(this, xlList, statusLabel));
|
setJMenuBar(new XLMenuBar(this, xlList, statusLabel, XLModel));
|
||||||
pack();
|
pack();
|
||||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||||
setResizable(false);
|
setResizable(false);
|
||||||
|
@ -44,6 +56,6 @@ public class XL extends JFrame {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
new XL(new XLList(), new XLCounter());
|
new XL(new XLList(), new XLCounter(), new XLModel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,18 @@ import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
class ClearAllMenuItem extends JMenuItem implements ActionListener {
|
import xl.model.XLModel;
|
||||||
|
|
||||||
public ClearAllMenuItem() {
|
class ClearAllMenuItem extends JMenuItem implements ActionListener {
|
||||||
|
private XLModel xlModel;
|
||||||
|
|
||||||
|
public ClearAllMenuItem(XLModel xlModel) {
|
||||||
super("Clear all");
|
super("Clear all");
|
||||||
|
this.xlModel = xlModel;
|
||||||
addActionListener(this);
|
addActionListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
// TODO
|
xlModel.clearAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,23 @@ package xl.gui.menu;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
|
import xl.model.XLModel;
|
||||||
|
import xl.gui.StatusLabel;
|
||||||
|
|
||||||
class ClearMenuItem extends JMenuItem implements ActionListener {
|
class ClearMenuItem extends JMenuItem implements ActionListener {
|
||||||
|
|
||||||
public ClearMenuItem() {
|
private XLModel xlModel;
|
||||||
|
private StatusLabel statusLabel;
|
||||||
|
|
||||||
|
public ClearMenuItem(XLModel xlModel, StatusLabel statusLabel) {
|
||||||
super("Clear");
|
super("Clear");
|
||||||
|
this.xlModel = xlModel;
|
||||||
|
this.statusLabel = statusLabel;
|
||||||
addActionListener(this);
|
addActionListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
// TODO
|
xlModel.clearCell();
|
||||||
|
statusLabel.setText(xlModel.getStatus());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package xl.gui.menu;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
import xl.gui.XL;
|
import xl.gui.XL;
|
||||||
import xl.gui.XLList;
|
import xl.gui.XLList;
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,22 @@ package xl.gui.menu;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import javax.swing.JFileChooser;
|
import javax.swing.JFileChooser;
|
||||||
|
|
||||||
|
import xl.model.XLModel;
|
||||||
import xl.gui.StatusLabel;
|
import xl.gui.StatusLabel;
|
||||||
import xl.gui.XL;
|
import xl.gui.XL;
|
||||||
|
|
||||||
class LoadMenuItem extends OpenMenuItem {
|
class LoadMenuItem extends OpenMenuItem {
|
||||||
|
|
||||||
public LoadMenuItem(XL xl, StatusLabel statusLabel) {
|
private XLModel xlModel;
|
||||||
|
|
||||||
|
public LoadMenuItem(XL xl, StatusLabel statusLabel, XLModel xlModel) {
|
||||||
super(xl, statusLabel, "Load");
|
super(xl, statusLabel, "Load");
|
||||||
|
this.xlModel = xlModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void action(String path) throws FileNotFoundException {
|
protected void action(String path) throws FileNotFoundException {
|
||||||
// TODO
|
xlModel.load(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int openDialog(JFileChooser fileChooser) {
|
protected int openDialog(JFileChooser fileChooser) {
|
||||||
|
|
|
@ -3,19 +3,23 @@ package xl.gui.menu;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
|
import xl.model.XLModel;
|
||||||
import xl.gui.XL;
|
import xl.gui.XL;
|
||||||
|
|
||||||
class NewMenuItem extends JMenuItem implements ActionListener {
|
class NewMenuItem extends JMenuItem implements ActionListener {
|
||||||
|
|
||||||
private XL xl;
|
private XL xl;
|
||||||
|
private XLModel model;
|
||||||
|
|
||||||
public NewMenuItem(XL xl) {
|
public NewMenuItem(XL xl) {
|
||||||
super("New");
|
super("New");
|
||||||
this.xl = xl;
|
this.xl = xl;
|
||||||
|
model = new XLModel();
|
||||||
addActionListener(this);
|
addActionListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent event) {
|
public void actionPerformed(ActionEvent event) {
|
||||||
new XL(xl);
|
new XL(xl, model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import javax.swing.JFileChooser;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.filechooser.FileFilter;
|
import javax.swing.filechooser.FileFilter;
|
||||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||||
|
|
||||||
import xl.gui.StatusLabel;
|
import xl.gui.StatusLabel;
|
||||||
import xl.gui.XL;
|
import xl.gui.XL;
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,22 @@ package xl.gui.menu;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import javax.swing.JFileChooser;
|
import javax.swing.JFileChooser;
|
||||||
|
|
||||||
|
import xl.model.XLModel;
|
||||||
import xl.gui.StatusLabel;
|
import xl.gui.StatusLabel;
|
||||||
import xl.gui.XL;
|
import xl.gui.XL;
|
||||||
|
|
||||||
class SaveMenuItem extends OpenMenuItem {
|
class SaveMenuItem extends OpenMenuItem {
|
||||||
|
|
||||||
public SaveMenuItem(XL xl, StatusLabel statusLabel) {
|
private XLModel xlModel;
|
||||||
|
|
||||||
|
public SaveMenuItem(XL xl, StatusLabel statusLabel, XLModel xlModel) {
|
||||||
super(xl, statusLabel, "Save");
|
super(xl, statusLabel, "Save");
|
||||||
|
this.xlModel = xlModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void action(String path) throws FileNotFoundException {
|
protected void action(String path) throws FileNotFoundException {
|
||||||
// TODO
|
xlModel.save(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int openDialog(JFileChooser fileChooser) {
|
protected int openDialog(JFileChooser fileChooser) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package xl.gui.menu;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
import xl.gui.XL;
|
import xl.gui.XL;
|
||||||
|
|
||||||
class WindowMenuItem extends JMenuItem implements ActionListener {
|
class WindowMenuItem extends JMenuItem implements ActionListener {
|
||||||
|
|
|
@ -2,21 +2,23 @@ package xl.gui.menu;
|
||||||
|
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
|
|
||||||
|
import xl.model.XLModel;
|
||||||
import xl.gui.StatusLabel;
|
import xl.gui.StatusLabel;
|
||||||
import xl.gui.XL;
|
import xl.gui.XL;
|
||||||
import xl.gui.XLList;
|
import xl.gui.XLList;
|
||||||
|
|
||||||
public class XLMenuBar extends JMenuBar {
|
public class XLMenuBar extends JMenuBar {
|
||||||
|
|
||||||
public XLMenuBar(XL xl, XLList xlList, StatusLabel statusLabel) {
|
public XLMenuBar(XL xl, XLList xlList, StatusLabel statusLabel, XLModel xlModel) {
|
||||||
JMenu file = new JMenu("File");
|
JMenu file = new JMenu("File");
|
||||||
JMenu edit = new JMenu("Edit");
|
JMenu edit = new JMenu("Edit");
|
||||||
file.add(new SaveMenuItem(xl, statusLabel));
|
file.add(new SaveMenuItem(xl, statusLabel, xlModel));
|
||||||
file.add(new LoadMenuItem(xl, statusLabel));
|
file.add(new LoadMenuItem(xl, statusLabel, xlModel));
|
||||||
file.add(new NewMenuItem(xl));
|
file.add(new NewMenuItem(xl));
|
||||||
file.add(new CloseMenuItem(xl, xlList));
|
file.add(new CloseMenuItem(xl, xlList));
|
||||||
edit.add(new ClearMenuItem());
|
edit.add(new ClearMenuItem(xlModel, statusLabel));
|
||||||
edit.add(new ClearAllMenuItem());
|
edit.add(new ClearAllMenuItem(xlModel));
|
||||||
add(file);
|
add(file);
|
||||||
add(edit);
|
add(edit);
|
||||||
add(new WindowMenu(xlList));
|
add(new WindowMenu(xlList));
|
||||||
|
|
22
app/src/main/java/xl/model/BombCell.java
Normal file
22
app/src/main/java/xl/model/BombCell.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package xl.model;
|
||||||
|
|
||||||
|
import xl.expr.Environment;
|
||||||
|
import xl.util.XLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a cell with a bomb value.
|
||||||
|
*
|
||||||
|
* This is essentially a placeholder for an invalid cell reference.
|
||||||
|
*/
|
||||||
|
public class BombCell implements Cell {
|
||||||
|
public BombCell() {}
|
||||||
|
|
||||||
|
public double cellValue(Environment e) {
|
||||||
|
throw XLException.ERROR_RECURSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String displayValue() {
|
||||||
|
throw XLException.ERROR_RECURSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
11
app/src/main/java/xl/model/Cell.java
Normal file
11
app/src/main/java/xl/model/Cell.java
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package xl.model;
|
||||||
|
|
||||||
|
import xl.expr.Environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for a cell in the spreadsheet.
|
||||||
|
*/
|
||||||
|
public interface Cell {
|
||||||
|
public double cellValue(Environment e);
|
||||||
|
public String displayValue();
|
||||||
|
}
|
21
app/src/main/java/xl/model/CommentCell.java
Normal file
21
app/src/main/java/xl/model/CommentCell.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package xl.model;
|
||||||
|
|
||||||
|
import xl.expr.Environment;
|
||||||
|
/**
|
||||||
|
* A class representing a cell with a comment value.
|
||||||
|
*/
|
||||||
|
public class CommentCell implements Cell {
|
||||||
|
private String commentText;
|
||||||
|
|
||||||
|
public CommentCell(String commentText) {
|
||||||
|
this.commentText = commentText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double cellValue(Environment e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String displayValue() {
|
||||||
|
return commentText;
|
||||||
|
}
|
||||||
|
}
|
26
app/src/main/java/xl/model/ExpressionCell.java
Normal file
26
app/src/main/java/xl/model/ExpressionCell.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package xl.model;
|
||||||
|
|
||||||
|
import xl.expr.Environment;
|
||||||
|
import xl.expr.Expr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a cell with an expression value.
|
||||||
|
*/
|
||||||
|
public class ExpressionCell implements Cell {
|
||||||
|
private Expr expr;
|
||||||
|
|
||||||
|
public ExpressionCell(Expr expr) {
|
||||||
|
this.expr = expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double cellValue(Environment env) {
|
||||||
|
return expr.value(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String displayValue() {
|
||||||
|
return expr.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
41
app/src/main/java/xl/model/XLBufferedReader.java
Normal file
41
app/src/main/java/xl/model/XLBufferedReader.java
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package xl.model;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import xl.expr.Expr;
|
||||||
|
import xl.expr.ExprParser;
|
||||||
|
import xl.util.XLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized BufferedReader for reading XL files.
|
||||||
|
*/
|
||||||
|
public class XLBufferedReader extends BufferedReader {
|
||||||
|
|
||||||
|
public XLBufferedReader(String name) throws FileNotFoundException {
|
||||||
|
super(new FileReader(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(Map<String, Cell> map) {
|
||||||
|
try {
|
||||||
|
while (ready()) {
|
||||||
|
String string = readLine();
|
||||||
|
int i = string.indexOf('=');
|
||||||
|
String address = string.substring(0, i);
|
||||||
|
String content = string.substring(i + 1);
|
||||||
|
|
||||||
|
if(content.startsWith("#")){
|
||||||
|
map.put(address, new CommentCell(content.substring(1)));
|
||||||
|
} else {
|
||||||
|
ExprParser parser = new ExprParser();
|
||||||
|
Expr expression = parser.build(content);
|
||||||
|
map.put(address, new ExpressionCell(expression));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new XLException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
208
app/src/main/java/xl/model/XLModel.java
Normal file
208
app/src/main/java/xl/model/XLModel.java
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
package xl.model;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import xl.expr.Environment;
|
||||||
|
import xl.expr.ExprParser;
|
||||||
|
import xl.util.XLException;
|
||||||
|
|
||||||
|
public class XLModel extends Observable implements Environment {
|
||||||
|
private Map<String, Cell> map;
|
||||||
|
private String status;
|
||||||
|
private String currentAddress;
|
||||||
|
|
||||||
|
// The observers that are registered to be notified
|
||||||
|
private List<Observer> observers = new ArrayList<>();
|
||||||
|
|
||||||
|
public XLModel() {
|
||||||
|
status = "";
|
||||||
|
currentAddress = "A1";
|
||||||
|
map = new java.util.HashMap<String, Cell>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentAddress() {
|
||||||
|
return currentAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentAddress(String currentAddress) {
|
||||||
|
this.currentAddress = currentAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Cell> getCell(String address) {
|
||||||
|
return Optional.ofNullable(map.get(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove(String address) {
|
||||||
|
// Save the existing cell at the specified address
|
||||||
|
Cell oldCell = map.get(address);
|
||||||
|
|
||||||
|
if (oldCell != null) {
|
||||||
|
map.remove(address);
|
||||||
|
|
||||||
|
// If the output is invalid, restore the old cell
|
||||||
|
if (hasWrongOutputs()) {
|
||||||
|
map.put(address, oldCell);
|
||||||
|
status = "Cannot remove cell";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String address, String text) {
|
||||||
|
try {
|
||||||
|
createCell(address, text);
|
||||||
|
} catch (XLException e) {
|
||||||
|
status = "Error creating cell";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell observers that the model has changed
|
||||||
|
setChanged();
|
||||||
|
notifyObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearStatus() {
|
||||||
|
status = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearCell() {
|
||||||
|
if (remove(currentAddress)) {
|
||||||
|
notifyObservers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearAll() {
|
||||||
|
map.clear();
|
||||||
|
clearStatus();
|
||||||
|
setChanged();
|
||||||
|
notifyObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(String path) {
|
||||||
|
try (XLBufferedReader reader = new XLBufferedReader(path)) {
|
||||||
|
map.clear();
|
||||||
|
reader.load(map);
|
||||||
|
status = "";
|
||||||
|
} catch (IOException e) {
|
||||||
|
status = "Error loading file";
|
||||||
|
}
|
||||||
|
setChanged();
|
||||||
|
notifyObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(String path) {
|
||||||
|
try (XLPrintStream xlPrintStream = new XLPrintStream(path)) {
|
||||||
|
xlPrintStream.save(map.entrySet());
|
||||||
|
} catch (IOException e) {
|
||||||
|
status = "Error saving file";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddressValue(String address) {
|
||||||
|
if (map.containsKey(address)) {
|
||||||
|
Cell cell = map.get(address);
|
||||||
|
if (cell instanceof CommentCell) {
|
||||||
|
return cell.displayValue();
|
||||||
|
}
|
||||||
|
return Double.toString(cell.cellValue(this));
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddressContent(String address) {
|
||||||
|
if (map.containsKey(address)) {
|
||||||
|
Cell cell = map.get(address);
|
||||||
|
if (cell instanceof CommentCell) {
|
||||||
|
return "#" + map.get(address).displayValue();
|
||||||
|
}
|
||||||
|
return map.get(address).displayValue();
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsKey(String address) {
|
||||||
|
return map.containsKey(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCell(String address, String text) {
|
||||||
|
if (text.length() > 0 && text.charAt(0) == '#') {
|
||||||
|
map.put(address, new CommentCell(text.substring(1)));
|
||||||
|
} else {
|
||||||
|
ExprParser parser = new ExprParser();
|
||||||
|
try {
|
||||||
|
Cell newCell = new ExpressionCell(parser.build(text));
|
||||||
|
Cell oldCell = map.get(address);
|
||||||
|
BombCell bomb = new BombCell();
|
||||||
|
map.put(address, bomb);
|
||||||
|
if (hasCircularDependancy(newCell) || hasWrongOutputs()) {
|
||||||
|
if (oldCell != null) {
|
||||||
|
map.put(address, oldCell);
|
||||||
|
} else {
|
||||||
|
map.remove(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw XLException.ERROR_CREATECELL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasCircularDependancy(Cell cell) {
|
||||||
|
try {
|
||||||
|
cell.cellValue(this);
|
||||||
|
map.put(currentAddress, cell);
|
||||||
|
status = "";
|
||||||
|
} catch (XLException e) {
|
||||||
|
status = e.getMessage();
|
||||||
|
return true;
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
status = e.toString();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasWrongOutputs() {
|
||||||
|
for (Map.Entry<String, Cell> entry : map.entrySet()) {
|
||||||
|
try {
|
||||||
|
entry.getValue().cellValue(this);
|
||||||
|
} catch (Exception e) {
|
||||||
|
status = "cannot update";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double value(String name) {
|
||||||
|
// Check if the cell is referencing itself
|
||||||
|
if (currentAddress.equals(name)) {
|
||||||
|
throw XLException.ERROR_RECURSIVE;
|
||||||
|
}
|
||||||
|
// Check if the cell is empty
|
||||||
|
if (map.containsKey(name)) {
|
||||||
|
return map.get(name).cellValue(this);
|
||||||
|
} else {
|
||||||
|
throw XLException.ERROR_EMPTY_REF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addObserver(Observer o) {
|
||||||
|
observers.add(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notifyObservers() {
|
||||||
|
for (Observer o : observers) {
|
||||||
|
o.update(this, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
app/src/main/java/xl/model/XLPrintStream.java
Normal file
31
app/src/main/java/xl/model/XLPrintStream.java
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package xl.model;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized PrintStream for writing XL files.
|
||||||
|
*/
|
||||||
|
public class XLPrintStream extends PrintStream {
|
||||||
|
|
||||||
|
public XLPrintStream(String fileName) throws FileNotFoundException {
|
||||||
|
super(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(Set<Entry<String, Cell>> set) {
|
||||||
|
for (Entry<String, Cell> entry : set) {
|
||||||
|
print(entry.getKey());
|
||||||
|
print('=');
|
||||||
|
Cell cell = entry.getValue();
|
||||||
|
if (cell instanceof CommentCell) {
|
||||||
|
println("#" + cell.displayValue());
|
||||||
|
} else {
|
||||||
|
println(cell.displayValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flush();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
package xl.util;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
// TODO move to another package
|
|
||||||
public class XLBufferedReader extends BufferedReader {
|
|
||||||
|
|
||||||
public XLBufferedReader(String name) throws FileNotFoundException {
|
|
||||||
super(new FileReader(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Change Object to something appropriate
|
|
||||||
public void load(Map<String, Object> map) {
|
|
||||||
try {
|
|
||||||
while (ready()) {
|
|
||||||
String string = readLine();
|
|
||||||
int i = string.indexOf('=');
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new XLException(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,10 @@ package xl.util;
|
||||||
|
|
||||||
public class XLException extends RuntimeException {
|
public class XLException extends RuntimeException {
|
||||||
|
|
||||||
|
public static final XLException ERROR_RECURSIVE = new XLException("Error: Circular Dependency");
|
||||||
|
public static final XLException ERROR_EMPTY_REF = new XLException("Empty: Reference to empty cell");
|
||||||
|
public static final XLException ERROR_CREATECELL = new XLException("Error: Could not create cell");
|
||||||
|
|
||||||
public XLException(String message) {
|
public XLException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package xl.util;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
// TODO move to another package
|
|
||||||
public class XLPrintStream extends PrintStream {
|
|
||||||
|
|
||||||
public XLPrintStream(String fileName) throws FileNotFoundException {
|
|
||||||
super(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Change Object to something appropriate
|
|
||||||
public void save(Set<Entry<String, Object>> set) {
|
|
||||||
for (Entry<String, Object> entry : set) {
|
|
||||||
print(entry.getKey());
|
|
||||||
print('=');
|
|
||||||
println(entry.getValue());
|
|
||||||
}
|
|
||||||
flush();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue