/*
* Copyright (C) 2011 Alasdair C. Hamilton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package ketUI;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.Graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.TransferHandler;
import ket.*;
import ket.display.HTMLTools;
import ket.display.ColourScheme;
import ket.display.ColourSchemeDecorator;
import ket.display.LatexTools;
import ket.display.box.Box;
import ket.math.*;
import ket.math.convert.ArgumentParser;
import ketUI.Clipboard;
import ketUI.chord.Chord;
import ketUI.chord.ChordEventHandler;
import ketUI.chord.KeyboardEventHandler;
import ketUI.modes.*;
import ketUI.panel.AnimatedDisplay;
import ketUI.panel.FocusRequestDaemon;
import ketUI.panel.FullScreenFocusListener;
import ketUI.panel.KetPanel;
import ketUI.panel.KetPanelFocusListener;
import ketUI.panel.KetFrame;
/**
* The top-level model of a collection of equations including associated
* graphical user interface components, methods to load and save documents as
* well as to initialize such documents.
*/
public class Document {
final DocumentManager documentManager;
final KetPanel ketPanel;
final KeyboardEventHandler keyboardEventHandler;
final ScrollListener scrollListener;
final FrameManager frameManager;
final MathCollection mathCollection;
final Modes modes;
ColourSchemeDecorator colourScheme;
boolean readOnly;
int boxFontSize;
/**
* There are three types of document sources: blank documents,
* templates that are stored within the Ket.jar file and regular input
* files that are stored elsewhere. To distinguish blank documents,
* pass in a null filename and to indicate a template, use *.template
* while regular documents are *.txt.
*/
public Document(DocumentManager documentManager, JFrame existingFrame, String filename, boolean editable) {
boxFontSize = Box.DEFAULT_BOX_FONT_SIZE;
this.documentManager = documentManager;
this.mathCollection = new MathCollection(documentManager!=null?documentManager.getKnownArguments():Ket.KNOWN_ARGUMENTS);
this.modes = new Modes(this);
this.colourScheme = new ColourSchemeDecorator(ColourScheme.WHITEBOARD, mathCollection, modes.getSearch(), modes);
this.ketPanel = new KetPanel(this);
this.ketPanel.setPreferredSize(new Dimension(FrameManager.SCREEN_WIDTH, FrameManager.SCREEN_HEIGHT));
this.frameManager = new FrameManager(documentManager, this, existingFrame);
this.keyboardEventHandler = new KeyboardEventHandler(this, ketPanel);
this.scrollListener = new ScrollListener(this);
initContent(filename);
if (editable) {
ketPanel.addKeyListener(keyboardEventHandler);
ketPanel.addMouseWheelListener(scrollListener);
} else {
ketPanel.setFocusable(false);
modes.setDocumentState(DocumentState.ECHO_VIEW);
}
if (frameManager.isStandAlone()) {
frameManager.invokeLater();
}
}
public Document(DocumentManager documentManager, JFrame existingFrame, String filename) {
this(documentManager, existingFrame, filename, true);
}
public Document(JFrame existingFrame, boolean editable) {
this(null, existingFrame, null, editable);
}
// TODO: THIS HAS AN ENTIRELY DIFFERENT BEHAVIOUR
public Document(DocumentManager documentManager, JFrame existingFrame, String filename, String label) {
this(documentManager, existingFrame, filename);
getCursor().selectEquation(label);
int index = getCursor().getEquation().getEquationIndex();
getKetPanel().fromMiddle(index);
}
public Document(DocumentManager documentManager, JFrame existingFrame, String filename, int equationIndex) {
this(documentManager, existingFrame, filename);
getCursor().selectEquation(equationIndex);
int index = getCursor().getEquation().getEquationIndex();
getKetPanel().fromMiddle(index);
}
private void initContent(String filename) {
boolean loadFile = filename!=null;
if (loadFile) {
loadFile = initialFileSetup(filename);
}
if ( ! loadFile ) {
// Default to a blank document.
blankDocumentSetup();
}
}
public void setColourScheme(ColourSchemeDecorator colourScheme) {
this.colourScheme = colourScheme;
}
public ColourSchemeDecorator getColourScheme() {
return colourScheme;
}
/**
* Load a specified filename.
*/
private boolean initialFileSetup(String filename) {
readOnly = false;
// Load a file, returning if true and defaulting to a blank
// document (below) if an error is encountered.
boolean readSuccess = false;
if (filename.matches(".+\\.template")) {
// A template (i.e. within Ket.jar).
readSuccess = mathCollection.readJar(filename);
if (readSuccess) {
// Don't overwrite template files.
readOnly = true;
}
} else {
// A regular file (i.e. outside Ket.jar).
File file = new File(filename);
if (file.exists()) {
readSuccess = mathCollection.read(filename);
}
}
if (readSuccess) {
// View from the top of the equation.
Argument visibleRoot = getEquationList().getFirstEquation().getVisibleRoot();
getSelection().setOnly(visibleRoot);
ketPanel.fromTop(0);
getKeyboardEventHandler().setToInitialState();
ketPanel.updateAndRepaint();
frameManager.setFilename(filename);
return true;
} else {
return false;
}
}
private void blankDocumentSetup() {
Equation blankEquation = new Equation();
Selection selection = mathCollection.getSelection();
EquationList el = selection.getEquationList();
el.addLast(blankEquation);
selection.setCurrent(blankEquation.getRoot());
mathCollection.getUndo().initHistory(el);
getKeyboardEventHandler().setToInitialState();
ketPanel.updateAndRepaint();
frameManager.setFilename(null);
}
public boolean increaseBoxFontSize() {
boxFontSize = (boxFontSize+5<Box.LARGEST_FONT_SIZE) ? boxFontSize+5 : Box.LARGEST_FONT_SIZE;
return false;
}
public boolean decreaseBoxFontSize() {
boxFontSize = (boxFontSize-5>Box.SMALLEST_FONT_SIZE) ? boxFontSize-5 : Box.SMALLEST_FONT_SIZE;
return false;
}
public int getBoxFontSize() {
return boxFontSize;
}
public void setBoxFontSize(int boxFontSize) {
this.boxFontSize = boxFontSize;
}
/**
* Handle mouse wheel scroll events.
*/
public void scrollDown() {
if (ketPanel.isListDisplayMode()) {
ketPanel.moveViewHalfPageDown();
} else if (ketPanel.isGridDisplayMode()) {
getKetPanel().moveViewDown();
} else {
getCursor().selectDown();
}
ketPanel.updateAndRepaint();
}
/**
* Handle mouse wheel scroll events.
*/
public void scrollUp() {
if (ketPanel.isListDisplayMode()) {
ketPanel.moveViewHalfPageUp();
} else if (ketPanel.isGridDisplayMode()) {
getKetPanel().moveViewUp();
} else {
getCursor().selectUp();
}
ketPanel.updateAndRepaint();
}
/**
* Check for any unsaved changes.
*/
public boolean containsUnsavedChanges() {
return mathCollection.containsUnsavedChanges();
}
public void ensureSelectionVisibility() {
if ( ! isSelectionVisible() ) {
// BUG: Selection is selected too ofen.
getKetPanel().ensureSelectionVisibility();
}
}
////////////////////
// HELPER METHODS //
////////////////////
public Modes getModes() {
return modes;
}
public Cursor getCursor() {
return getSelection().getCursor();
}
public EquationList getEquationList() {
return getSelection().getEquationList();
}
public Selection getSelection() {
return mathCollection.getSelection();
}
public MathCollection getMathCollection() {
return mathCollection;
}
public KetPanel getKetPanel() {
return ketPanel;
}
public KeyboardEventHandler getKeyboardEventHandler() {
return keyboardEventHandler;
}
public boolean isSelectionVisible() { //? Check if the argument or its parent is visible.
Argument current = getSelection().getCurrent();
boolean visible = ketPanel.isArgumentVisible(current);
if (visible) return true;
Ket.out.println("[not visible: " + current + "]");
for (Branch a : current.getAncestors()) {
visible = ketPanel.isArgumentVisible(a);
if (visible) return true;
Ket.out.println("[not visible: " + a + "]");
}
Ket.out.println("[selection is not visible]");
return false;
}
public DocumentManager getDocumentManager() {
return documentManager;
}
public String toString() {
String string = "<Document filename='"+frameManager.getFilename()+"'>\n";
string += mathCollection.toString();
string += "</Document>\n";
return string;
}
/**
* When this is used as a panel in an external frame, use this to conveniently convert from strings to arguments.
*/
public Argument parseArgument(String string) {
KnownArguments knownArguments = modes.getKnownArguments();
Clipboard clipboard = modes.getClipboard();
return ArgumentParser.parseArgument(string, knownArguments, clipboard, mathCollection);
}
public FrameManager getFrameManager() {
return frameManager;
}
public void updateAndRepaint() {
getKetPanel().updateAndRepaint();
}
CommandListener commandListener;
public void setCommandListener(CommandListener commandListener) {
this.commandListener = commandListener;
}
public CommandListener getCommandListener() {
return commandListener;
}
}