/*
 * @(#)StringViewer.java    2.6.0g   1 December 2007
 *
 * Copyright 2007
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.*;
import edu.neu.ccs.filter.StringableFilter;
import edu.neu.ccs.util.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * <p>Class <code>StringViewer</code> provides static
 * tools for viewing a <code>String</code> in a GUI.</p>
 * 
 * <p>As of 2.6.0e, this class can also instantiate
 * a <code>StringViewer</code> object that may be
 * embedded in a GUI and manipulated by the caller.
 * A <code>StringViewer</code> object embeds a
 * <code>TextAreaView</code> which is placed in a
 * <code>JPTScrollPane</code> which is placed in
 * this object viewed as a <code>DisplayPanel</code>.</p>
 * 
 * <p>The caller can control the preferred size of
 * the viewport in the scroll pane via the width and
 * height parameters to the constructor.  If these
 * parameters are omitted, the width defaults to 800
 * and the height to 600.  The minimum width and
 * height is forced to be 100.</p>
 * 
 * <p>As of 2.6.0f, this class implements
 * <code>TypedView</code> by delegating method calls
 * to an internal <code>TextAreaView</code>.  The goal
 * is to permit this class to integrate with other JPT
 * tools.</p>
 * 
 * <p>In effect, <code>StringViewer</code> now enables
 * the creation of a text area in a scroll pane whose
 * viewport size is controlled.  This enables the use
 * of a <code>TypedView</code> for multi-line text in
 * a context where the screen real estate in the GUI
 * will be predictable.</p>
 * 
 * <p>Upon further reflection, we decided that it would
 * be helpful to implement <code>GeneralView</code> in
 * addition to <code>TypedView</code> so those methods
 * were added in 2.6.0g.</p>
 * 
 * <p>This class does not implement action listeners
 * because they are not supported by the underlying
 * <code>JTextArea</code> class.  This class also
 * does not implement property change listeners.</p>
 * 
 * @author  Richard Rasala
 * @version 2.6.0g
 * @since   2.6.0
 */
public class StringViewer
    extends DisplayPanel
    implements GeneralView, Fragile, JPTConstants 
{
    
    /** The default scroll pane viewport width = 800. */
    public static final int WIDTH  = 800;
    
    /** The default scroll pane viewport height = 600. */
    public static final int HEIGHT = 600;
    
    /** The minimum width and height = 100. */
    public static final int MINIMUM = 100;
    
    /** The vertical scroll bar policy of ALWAYS. */
    public static final int V_ALWAYS = JPTScrollPane.VERTICAL_SCROLLBAR_ALWAYS;
    
    /** The horizontal scroll bar policy of ALWAYS. */
    public static final int H_ALWAYS = JPTScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS;
    
    
    /** The default monospaced font name. */
    public static final String FONTNAME =
        Fonts.getMonospacedFontFamilyName();
    
    
    /**
     * <p>The default font size.</p>
     * 
     * <pre>    14 + (int) LookAndFeelTools.getNetFontSizeAdjustment()</pre>
     */
    private static int staticFontSize =
        14 + (int) LookAndFeelTools.getNetFontSizeAdjustment();
    
    
    /**
     * <p>The display font for the static calls which uses
     * the default font name, the default font size, and
     * BOLD as the style.</p>
     * 
     * <p>This data is intentionally not final so it may be
     * changed by the method <code>setDefaultFont</code>.</p>
     * 
     * <p>As of 2.6.0e, this font is also the default font
     * for <code>StringViewer</code> objects if no font is
     * provided in a constructor.</p>
     */
    private static Font staticFont = new Font(FONTNAME, Font.BOLD, staticFontSize);
    
    
    /** The internal <code>TextAreaView</code>. */
    protected TextAreaView textarea = null;
    
    
    /** The internal <code>JPTScrollPane</code>. */
    protected JPTScrollPane scrollpane = null;
    
    
    /** The GeneralViewSupport object. */
    protected GeneralViewSupport delegate = null;
    
    
    /**
     * <p>The default constructor that uses
     * empty initial text,
     * a width of 800,
     * a height of 600,
     * and the default static font.</p>
     */
    public StringViewer() {
        initialize(null, WIDTH, HEIGHT, null);
    }
    
    
    /**
     * <p>The constructor that uses
     * empty initial text,
     * the given width and height,
     * and the default static font.</p>
     * 
     * @param width  the viewport width
     * @param height the viewport height
     */
    public StringViewer(int width, int height) {
        initialize(null, width, height, null);
    }
    
    
    /**
     * <p>The constructor that uses
     * empty initial text,
     * a width of 800,
     * a height of 600,
     * and the given font.</p>
     * 
     * @param font the font for the text area
     */
    public StringViewer(Font font) {
        initialize(null, WIDTH, HEIGHT, font);
    }
    
    
    /**
     * <p>The constructor that uses
     * empty initial text,
     * the given width and height,
     * and the given font.</p>
     * 
     * @param width  the viewport width
     * @param height the viewport height
     * @param font   the font for the text area
     */
    public StringViewer(int width, int height, Font font) {
        initialize(null, width, height, font);
    }
    
    
    
    /**
     * <p>The default constructor that uses
     * the given initial text,
     * a width of 800,
     * a height of 600,
     * and the default static font.</p>
     * 
     * @param text the initial text
     */
    public StringViewer(String text) {
        initialize(text, WIDTH, HEIGHT, null);
    }
    
    
    /**
     * <p>The constructor that uses
     * the given initial text,
     * the given width and height,
     * and the default static font.</p>
     * 
     * @param text the initial text
     * @param width  the viewport width
     * @param height the viewport height
     */
    public StringViewer(String text, int width, int height) {
        initialize(text, width, height, null);
    }
    
    
    /**
     * <p>The constructor that uses
     * the given initial text,
     * a width of 800,
     * a height of 600,
     * and the given font.</p>
     * 
     * @param text the initial text
     * @param font the font for the text area
     */
    public StringViewer(String text, Font font) {
        initialize(text, WIDTH, HEIGHT, font);
    }
    
    
    /**
     * <p>The constructor that uses
     * the given initial text,
     * the given width and height,
     * and the given font.</p>
     * 
     * @param text the initial text
     * @param width  the viewport width
     * @param height the viewport height
     * @param font   the font for the text area
     */
    public StringViewer(String text, int width, int height, Font font) {
        initialize(text, width, height, font);
    }
    
    
    
    /** The common constructor initialization code. */
    protected void initialize
        (String text, int width, int height, Font font)
    {
        delegate = new GeneralViewSupport
            (this, this.listenerList, null, null, null, null);
        
        if (text == null)
            text = "";
        
        textarea = new TextAreaView(text);
        
        if (font == null)
            font = staticFont;
        
        textarea.setFont(font);
        
        scrollpane = new JPTScrollPane(textarea, V_ALWAYS, H_ALWAYS);
        
        width =  (width  < MINIMUM) ? MINIMUM : width;
        height = (height < MINIMUM) ? MINIMUM : height;
        
        scrollpane.setViewportPreferredSize(width, height);
        
        setDataType(XString.class);
        
        addObject(scrollpane);
    }
    
    
    /////////////////
    // General API //
    /////////////////
    
    /** <p>Returns the internal <code>TextAreaView</code>.</p> */
    public TextAreaView getTextArea() {
        return textarea;
    }
    
    
    /** <p>Returns the internal <code>JPTScrollPane</code>.</p> */
    public JPTScrollPane getScrollPane() {
        return scrollpane;
    }
    
    
    /**
     * <p>Returns the text in the text area.</p>
     */
    public String getText() {
        return textarea.getText();
    }
    
    
    /**
     * <p>Returns the length of the text in the text area.</p>
     */
    public int getTextLength() {
        return textarea.getText().length();
    }
    
    
    /**
     * <p>Sets the text in the text area.</p>
     * 
     * <p>If the given text is <code>null</code>,
     * clears the text area.</p>
     * 
     * @param text the text to set
     */
    public void setText(String text) {
        if (text == null)
            text = "";
        
        textarea.setText(text);
    }
    
    
    /**
     * <p>Sets the text in the text area and adds
     * a newline.</p>
     * 
     * <p>If the given text is <code>null</code>,
     * sets the text to a newline.</p>
     * 
     * @param text the text to set
     */
    public void setTextWithNewline(String text) {
        if ((text == null) || (text.length() == 0))
            textarea.setText("\n");
        else
            textarea.setText(text + "\n");
    }
    
    
    /**
     * <p>Append the given text in the text area.</p>
     * 
     * <p>If the given text is <code>null</code>,
     * then does nothing.</p>
     * 
     * @param text the text to set
     */
    public void append(String text) {
        if ((text == null) || (text.length() == 0))
            return;
        
        textarea.append(text);
    }
    
    
    /**
     * <p>Append the given text in the text area
     * and then append a newline.</p>
     * 
     * <p>If the given text is <code>null</code>,
     * then append a newline.</p>
     * 
     * @param text the text to set
     */
    public void appendWithNewline(String text) {
        if ((text == null) || (text.length() == 0))
            textarea.append("\n");
        else
            textarea.append(text + "\n");
    }
    
    
    /**
     * <p>Insert the given text in the text area
     * at the given position.</p>
     * 
     * <p>If the given text is <code>null</code>,
     * then does nothing.</p>
     * 
     * <p>If the insert position is not between
     * 0 and <code>getTextLength()</code>,
     * then inserts at the nearest valid position.</p>
     * 
     * @param text the text to set
     * @param position the insert position
     */
    public void insert(String text, int position) {
        if ((text == null) || (text.length() == 0))
            return;
        
        int length = getTextLength();
        
        if (position < 0)
            position = 0;
        else
        if (position > length)
            position = length;
        
        textarea.insert(text, position);
    }
    
    
    /**
     * <p>Insert the given text in the text area
     * at the given position.</p>
     * 
     * <p>Calls:</p>
     * 
     * <pre>    insert(text, position)</pre>
     * 
     * @param position the insert position
     * @param text the text to set
     */
    public void insert(int position, String text) {
        insert(text, position);
    }
    
    
    /////////////////
    // Displayable //
    /////////////////
    
    /** Equivalent to <code>setText</code>.</p> */
    public void setViewState(String data) {
        setText(data);
    }
    
    
    /** Equivalent to <code>getText</code>.</p> */
    public String getViewState() {
        return getText();
    }
    
    
    public void setDefaultViewState(String data) {
        delegate.setDefaultViewState(data);
    }
    
    
    public String getDefaultViewState() {
        return delegate.getDefaultViewState();
    }
    
    
    public void reset() {
        delegate.reset();
    }
    
    
    /////////////////
    // GeneralView //
    /////////////////
    
    public GeneralView makeCopy() {
        // construct copy
        String text = getText();
        
        Dimension dimension = scrollpane.getViewport().getPreferredSize();
        
        int width  = dimension.width;
        int height = dimension.height;
        
        Font font = textarea.getFont();
        
        StringViewer copy = new StringViewer(text, width, height, font);
        
        // copy settings
        copy.setDataType        (getDataType());
        copy.setViewState       (getViewState());
        copy.setDefaultViewState(getDefaultViewState());
        copy.setInputProperties (new InputProperties(getInputProperties()));
        copy.setFilter          (getFilter());
        
        return copy;
    }
    
    
    public void setDataType(Class dataType) {
        delegate.setDataType(dataType);
    }
    
    
    public Class getDataType() {
        return delegate.getDataType();
    }
    
    
    public void setFilter(StringableFilter filter) {
        delegate.setFilter(filter);
    }
    
    
    public StringableFilter getFilter() {
        return delegate.getFilter();
    }
    
    
    public Stringable demandObject() {
        return delegate.demandObject();
    }
    
    
    public Stringable requestObject()
        throws CancelledException
    {
        return delegate.requestObject();
    }
    
    
    public Stringable demandObject(StringableFilter filter) {
        return delegate.demandObject(filter);
    }
    
    
    public Stringable requestObject(StringableFilter filter)
        throws CancelledException
    {
        return delegate.requestObject(filter);
    }
    
    
    public Stringable demandObject(Class dataType, StringableFilter filter) {
        return delegate.demandObject(dataType, filter);
    }
    
    
    public Stringable requestObject(Class dataType, StringableFilter filter)
        throws CancelledException
    {
        return delegate.requestObject(dataType, filter);
    }
    
    
    public void setInputProperties(InputProperties properties) {
        delegate.setInputProperties(properties);
    }
    
    
    public InputProperties getInputProperties() {
        return delegate.getInputProperties();
    }
    
    
    public void setErrorPromptTitleSuggestion(
        String errorPrompt,
        String dialogTitle,
        String suggestion )
    {
         delegate.setErrorPromptTitleSuggestion(errorPrompt, dialogTitle, suggestion );
    }
    
    
    /////////////
    // Fragile //
    /////////////
    
    public void addMalformedDataListener(MalformedDataListener l) {
        delegate.addMalformedDataListener(l);
    }
    
    
    public void removeMalformedDataListener(MalformedDataListener l) {
        delegate.removeMalformedDataListener(l);
    }
    
    
    ////////////////////
    // ActionListener //
    ////////////////////
    
    /**
     * This method is required by the <code>GeneralView</code>
     * interface but the implementation does nothing since
     * the underlying <code>JTextArea</code> does not fire
     * action events.
     */
    public void addActionListener(ActionListener listener) { }
    
    
    /**
     * This method is required by the <code>GeneralView</code>
     * interface but the implementation does nothing since
     * the underlying <code>JTextArea</code> does not fire
     * action events.
     */
    public void removeActionListener(ActionListener listener) { }
    
    
    ////////////////////
    // Static methods //
    ////////////////////
    
    /**
     * <p>Wraps the given <code>String</code> in a
     * <code>TextAreaView</code> object; then wraps
     * the text area in a <code>JPTScrollPane</code>
     * object whose viewport is bounded by
     * the given width and height;
     * this scroll pane is returned.</p>
     * 
     * <p>If the given string is <code>null</code>,
     * the scroll pane will show an error message.</p>
     * 
     * <p>The width and height are forced to be at
     * least the value of <code>MINIMUM</code> = 100.</p>
     * 
     * @param string the string to view
     * @param width  the viewport width  bound
     * @param height the viewport height bound
     */
    public static JPTScrollPane makeScrollView
        (String string, int width, int height)
    {
        if (string == null)
            string = "Null String passed to StringViewer.makeScrollView";
        
        TextAreaView textarea = new TextAreaView(string);
        
        textarea.setFont(staticFont);
        
        JPTScrollPane scrollpane = new JPTScrollPane(textarea, V_ALWAYS, H_ALWAYS);
        
        width =  (width  < MINIMUM) ? MINIMUM : width;
        height = (height < MINIMUM) ? MINIMUM : height;
        
        scrollpane.setViewportPreferredSize(width, height);
        
        return scrollpane;
    }
    
    
    /**
     * <p>Wraps the given <code>String</code> in a
     * <code>TextAreaView</code> object; then wraps
     * the text area in a <code>JPTScrollPane</code>
     * object whose viewport is bounded by
     * 800-by-600;
     * this scroll pane is returned.</p>
     * 
     * <p>If the given string is <code>null</code>,
     * the scroll pane will show an error message.</p>
     * 
     * @param string the string to view
     */
    public static JPTScrollPane makeScrollView(String string) {
        return makeScrollView(string, WIDTH, HEIGHT);
    }
    
    
    /**
     * <p>Shows the given string in a frame that contains
     * a scroll pane whose viewport is bounded by
     * the given width and height.</p>
     * 
     * <p>If the given string is <code>null</code>,
     * the frame will show an error message.</p>
     * 
     * @param string the string to view
     * @param width  the viewport width  bound
     * @param height the viewport height bound
     */
    public static void showStringInFrame
        (String string, int width, int height)
    {
        makeScrollView(string, width, height).frame();
    }
    
    
    /**
     * <p>Shows the given string in a frame that contains
     * a scroll pane whose viewport is bounded by
     * 800-by-600.</p>
     * 
     * <p>If the given string is <code>null</code>,
     * the frame will show an error message.</p>
     * 
     * @param string the string to view
     */
    public static void showStringInFrame(String string) {
        showStringInFrame(string, WIDTH, HEIGHT);
    }
    
    
    /**
     * <p>Shows the given string in an OK dialog that
     * contains a scroll pane bounded by
     * the given width and height.</p>
     * 
     * <p>If the given string is <code>null</code>, the
     * dialog will show an error message.</p>
     * 
     * @param string the string to view
     * @param width  the viewport width  bound
     * @param height the viewport height bound
     */
    public static void showStringInDialog
        (String string, int width, int height)
    {
        makeScrollView(string, width, height).OKDialog();
    }
    
    
    /**
     * <p>Shows the given string in an OK dialog that
     * contains a scroll pane bounded by
     * 800-by-600.</p>
     * 
     * <p>If the given string is <code>null</code>, the
     * dialog will show an error message.</p>
     * 
     * @param string the string to view
     */
    public static void showStringInDialog(String string) {
        showStringInDialog(string, WIDTH, HEIGHT);
    }
    
    
    /**
     * Returns the current default static font.
     */
    public static Font getDefaultFont() {
        return staticFont;
    }
    
    
    /**
     * <p>Sets the default static font.</p>
     * 
     * <p>Does nothing if newfont is <code>null</code>.</p>
     * 
     * @param newfont the new default static font
     */
    public static void setDefaultFont(Font newfont) {
        if (newfont != null)
            staticFont = newfont;
    }
}

