All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.mit.csail.sdg.alloy4.OurConsole Maven / Gradle / Ivy

The newest version!
/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package edu.mit.csail.sdg.alloy4;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

import org.alloytools.graphics.util.AlloyGraphics;
import org.alloytools.util.table.Table;

/**
 * Graphical input/output prompt.
 * 

* This class's constructor takes a Computer object, then constructs a * JScrollPane in which the user can type commands, and the output from the * Computer object will be displayed. Empty input lines are ignored. This * interactive prompt supports UP and DOWN arrow command histories and basic * copy/cut/paste editing. *

* For each user input, if the Computer object returns a String, it is displayed * in blue. But if the Computer object throws an exception, the exception will * be displayed in red. *

* Thread Safety: Can be called only by the AWT event thread. */ public final class OurConsole extends JScrollPane { final static int menuShortcutKeyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); /** This ensures the class can be serialized reliably. */ private static final long serialVersionUID = 0; /** The style for default text. */ private final static AttributeSet plain = style("Verdana,Helvetica", 14, false, false, false, Color.BLACK, 0); /** The style for table text. */ private final static AttributeSet mono = style("Input Mono,DejaVu Sans Mono,Courier New,Courier", 14, false, false, false, Color.BLACK, 10); /** The style for bold text. */ private final static AttributeSet bold = style("Verdana,Helvetica", 14, true, false, false, Color.BLACK, 0); /** The style for successful result. */ private final static AttributeSet good = style("Verdana,Helvetica", 14, false, false, false, Color.BLUE, 10); /** The style for failed result. */ private final static AttributeSet bad = style("Verdana,Helvetica", 14, false, false, false, Color.RED, 10); /** * The number of characters that currently exist above the horizontal divider * bar. (The interactive console is composed of a JTextPane which contains 0 or * more input/output pairs, followed by a horizontal divider bar, followed by an * embedded sub-JTextPane (where the user can type in the next input)) */ private int len = 0; /** * The main JTextPane containing 0 or more input/output pairs, followed by a * horizontal bar, followed by this.sub */ private final JTextPane main = do_makeTextPane(false, 5, 5, 5); /** * The sub JTextPane where the user can type in the next command. */ private final JTextPane sub = do_makeTextPane(true, 10, 10, 0); /** * The history of all commands entered so far, plus an extra String representing * the user's next command. */ private final List history = new ArrayList(); { history.add(""); } /** * The position in this.history that is currently showing. */ private int browse = 0; /* * Helper method that construct a mutable style with the given font name, font * size, boldness, color, and left indentation. */ static MutableAttributeSet style(String fontName, int fontSize, boolean boldness, boolean italic, boolean strike, Color color, int leftIndent) { fontName = AlloyGraphics.matchBestFontName(fontName); MutableAttributeSet s = new SimpleAttributeSet(); StyleConstants.setFontFamily(s, fontName); StyleConstants.setFontSize(s, fontSize); StyleConstants.setLineSpacing(s, -0.2f); StyleConstants.setBold(s, boldness); StyleConstants.setItalic(s, italic); StyleConstants.setForeground(s, color); StyleConstants.setLeftIndent(s, leftIndent); StyleConstants.setStrikeThrough(s, strike); return s; } /** * Construct a JScrollPane that allows the user to interactively type in * commands and see replies. * * @param computer - this object is used to evaluate the user input * @param syntaxHighlighting - if true, the "input area" will be * syntax-highlighted * @param initialMessages - this is a list of String and Boolean; each String is * printed to the screen as is, and Boolean.TRUE will turn subsequent * text bold, and Boolean.FALSE will turn subsequent text non-bold. */ public OurConsole(final Computer computer, boolean syntaxHighlighting, Object... initialMessages) { super(VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); if (syntaxHighlighting) { sub.setDocument(new OurSyntaxUndoableDocument("Verdana", 14)); } setViewportView(main); // show the initial message AttributeSet st = plain; for (Object x : initialMessages) { if (x instanceof Boolean) st = (Boolean.TRUE.equals(x) ? bold : plain); else do_add(-1, String.valueOf(x), st); } do_add(-1, "\n", plain); // we must add a linebreak to ensure that // subsequent text belong to a "different // paragraph" // insert the divider and the sub JTextPane StyledDocument doc = main.getStyledDocument(); JPanel divider = new JPanel(); divider.setBackground(Color.LIGHT_GRAY); divider.setPreferredSize(new Dimension(1, 1)); MutableAttributeSet dividerStyle = new SimpleAttributeSet(); StyleConstants.setComponent(dividerStyle, divider); MutableAttributeSet inputStyle = new SimpleAttributeSet(); StyleConstants.setComponent(inputStyle, sub); len = doc.getLength(); do_add(-1, " \n", dividerStyle); // The space character won't be // displayed; it will instead be // drawn as a divider do_add(-1, " \n", inputStyle); // The space character won't be // displayed; it will instead display // the input buffer final Caret subCaret = sub.getCaret(), mainCaret = main.getCaret(); // When caret moves in the sub JTextPane, we cancel any active selection // in the main JTextPane subCaret.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { if (mainCaret.getMark() != mainCaret.getDot()) mainCaret.setDot(mainCaret.getDot()); } }); // When caret moves in the main JTextPane, we cancel any active // selection in the sub JTextPane mainCaret.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { if (subCaret.getMark() != subCaret.getDot()) subCaret.setDot(subCaret.getDot()); } }); // now, create the paste/copy/cut actions AbstractAction alloy_paste = new AbstractAction("alloy_paste") { static final long serialVersionUID = 0; @Override public void actionPerformed(ActionEvent x) { sub.paste(); } }; AbstractAction alloy_copy = new AbstractAction("alloy_copy") { static final long serialVersionUID = 0; @Override public void actionPerformed(ActionEvent x) { System.out.println("copy"); if (sub.getSelectionStart() != sub.getSelectionEnd()) sub.copy(); else main.copy(); } }; AbstractAction alloy_cut = new AbstractAction("alloy_cut") { static final long serialVersionUID = 0; @Override public void actionPerformed(ActionEvent x) { if (sub.getSelectionStart() != sub.getSelectionEnd()) sub.cut(); else main.copy(); } }; // create the keyboard associations: ctrl-{c,v,x,insert} and // shift-{insert,delete} for (JTextPane x : Arrays.asList(main, sub)) { x.getActionMap().put("alloy_paste", alloy_paste); x.getActionMap().put("alloy_copy", alloy_copy); x.getActionMap().put("alloy_cut", alloy_cut); x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_V, menuShortcutKeyMask), "alloy_paste"); x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, menuShortcutKeyMask), "alloy_copy"); x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_X, menuShortcutKeyMask), "alloy_cut"); x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, Event.SHIFT_MASK), "alloy_paste"); x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, Event.CTRL_MASK), "alloy_copy"); x.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, Event.SHIFT_MASK), "alloy_cut"); } // configure so that, upon receiving focus, we automatically focus and // scroll to the sub-JTextPane FocusAdapter focus = new FocusAdapter() { @Override public void focusGained(FocusEvent e) { sub.requestFocusInWindow(); sub.scrollRectToVisible(new Rectangle(0, sub.getY(), 1, sub.getHeight())); } }; addFocusListener(focus); sub.addFocusListener(focus); main.addFocusListener(focus); // configure so that mouse clicks in the main JTextPane will immediately // transfer focus to the sub JTextPane main.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { sub.requestFocusInWindow(); } @Override public void mouseClicked(MouseEvent e) { sub.requestFocusInWindow(); } }); // configure the behavior for PAGE_UP, PAGE_DOWN, UP, DOWN, TAB, and // ENTER sub.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { if (e.getKeyChar() == '\t') { e.consume(); } if (e.getKeyChar() == '\n') { e.consume(); String cmd = sub.getText(); sub.setText(""); do_command(computer, cmd); } } @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_TAB) e.consume(); if (e.getKeyCode() == KeyEvent.VK_PAGE_UP) { e.consume(); do_pageup(); } if (e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) { e.consume(); do_pagedown(); } if (e.getKeyCode() == KeyEvent.VK_UP) { e.consume(); if (browse == history.size() - 1) { history.set(browse, sub.getText()); } if (browse > 0 && browse - 1 < history.size()) { browse--; sub.setText(history.get(browse)); } } if (e.getKeyCode() == KeyEvent.VK_DOWN) { e.consume(); if (browse < history.size() - 1) { browse++; sub.setText(history.get(browse)); } } } @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_TAB) e.consume(); } }); } /** * This helper method constructs a JTextPane with the given settings. */ private static JTextPane do_makeTextPane(boolean editable, int topMargin, int bottomMargin, int otherMargin) { JTextPane x = OurAntiAlias.pane(null, Color.BLACK, Color.WHITE, new Font("Verdana", Font.PLAIN, 14)); x.setEditable(editable); x.setAlignmentX(0); x.setAlignmentY(0); x.setCaretPosition(0); x.setMargin(new Insets(topMargin, otherMargin, bottomMargin, otherMargin)); return x; } /** This method processes a user command. */ private void do_command(Computer computer, String cmd) { cmd = cmd.trim(); if (cmd.length() == 0) return; cmd = TableView.revertSuffix(cmd); StyledDocument doc = main.getStyledDocument(); if (history.size() >= 2 && cmd.equals(history.get(history.size() - 2))) { // If the user merely repeated the most recent command, then don't // grow the history history.set(history.size() - 1, ""); } else { // Otherwise, grow the history history.set(history.size() - 1, cmd); history.add(""); } browse = history.size() - 1; // display the command int old = doc.getLength(); do_add(len, cmd + "\n\n", plain); len += (doc.getLength() - old); // perform the computation boolean isBad = false; Object result; try { result = computer.compute(cmd); } catch (Throwable ex) { result = ex.toString(); isBad = true; } int savePosition = len; // display the outcome old = doc.getLength(); do_add(len, result.toString().trim() + "\n\n", (isBad ? bad : good)); len += (doc.getLength() - old); // indent the outcome main.setSelectionStart(savePosition + 1); main.setSelectionEnd(len); main.setParagraphAttributes(good, false); // redraw then scroll to the bottom invalidate(); repaint(); validate(); sub.scrollRectToVisible(new Rectangle(0, sub.getY(), 1, sub.getHeight())); do_pagedown(); // need to do this after the validate() so that the // scrollbar knows the new limit } /** Performs "page up" in the JScrollPane. */ private void do_pageup() { JScrollBar bar = getVerticalScrollBar(); bar.setValue(bar.getValue() - 200); } /** Performs "page down" in the JScrollPane. */ private void do_pagedown() { JScrollBar bar = getVerticalScrollBar(); bar.setValue(bar.getValue() + 200); } /** * Insert the given text into the given location and with the given style if * where>=0; append the text if where<0. */ private void do_add(int where, String text, AttributeSet style) { try { StyledDocument doc = main.getStyledDocument(); if (TableView.isTable(text)) { Table table = TableView.toTable(text, false); doc.insertString(where >= 0 ? where : doc.getLength(), table.toString(), mono); main.getCaret().setSelectionVisible(false); } else doc.insertString(where >= 0 ? where : doc.getLength(), text, style); } catch (BadLocationException ex) {} } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy