/*
* Copyright 2003-2010 Tufts University Licensed under the
* Educational Community License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.osedu.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package tufts.vue;
import tufts.vue.LWComponent.Key;
import tufts.vue.gui.ColorMenuButton;
import tufts.vue.gui.DockWindow;
import tufts.vue.gui.GUI;
import tufts.vue.gui.TextRow;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Font;
import java.awt.Color;
import java.awt.BasicStroke;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Point2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JComboBox;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.*;
import javax.swing.text.html.CSS;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import com.lightdev.app.shtm.SHTMLDocument;
import com.lightdev.app.shtm.SHTMLEditorKit;
import com.lightdev.app.shtm.Util;
//import java.awt.font.LineBreakMeasurer;
//import java.awt.font.TextAttribute;
/**
* A multi-line editable text object that supports left/center/right
* aligment for its lines of text.
*
* Used in two modes: (1) "normal" mode -- used to paint multi-line
* text objects (labels, notes, etc) and (2) "edit". In normal mode,
* this JComponent has no parent -- it isn't added to any AWT
* hierarchy -- it's only used to paint as part of the
* LWMap/LWContainer paint tree (via the draw(Graphics2D)) method. In
* edit mode, it's temporarily added to the canvas so it can receive
* user input. Only one instance of these is ever added and active in
* AWT at the same time. We have to do some funky wrangling to deal
* with zoom, because the JComponent can't paint and interact on a
* zoomed (scaled) graphics context (unless we were to implement mouse
* event retargeting, which is a future possibility). So if there is
* a scale active on the currently displayed map, we manually derive a
* new font for the whole text object (the Document) and set it to
* that temporarily while it's active in edit mode, and then re-set it
* upon removal. Note that users of this class (e.g., LWNode) should
* not bother to paint it (call draw()) if it's in edit mode
* (getParent() != null) as the AWT/Swing tree is dealing with that
* while it's in its activated edit state.
*
* We use a JTextPane because it supports a StyledDocument, which is
* what we need to be able to set left/center/right aligment for all
* the paragraphs in the document. This is a bit heavy weight for our
* uses right now as we only make use of one font at a time for the whole
* document (this is the heaviest weight text component in Swing).
* JTextArea would have worked for us, except it only supports its
* fixed default of left-aligned text. However, eventually we're
* probably going to want to suport intra-string formatting (fonts,
* colors, etc) and so we'll be ready for that, with the exception of
* the hack mentioned above to handle zooming (tho we could
* theoretically iterate through the whole document, individually
* deriving zoomed fonts for every font found in the Document.)
*
* Note that you can get center/variable alignment even with
* a JLabel if it's text starts with <HTML>, and you
* put in a center tag. Unfortunately, JTextArea's don't
* do HTML w/out setting up a StyledDocument manually.
*
*
* @author Scott Fraize
* @version $Revision: 1.44 $ / $Date: 2010-02-03 19:17:40 $ / $Author: mike $
*
*/
public class RichTextBox extends com.lightdev.app.shtm.SHTMLEditorPane
implements VueConstants
, FocusListener
, KeyListener
, DocumentListener
, MouseListener
, ActionListener
{
/**
*
*/
private static final long serialVersionUID = 1L;
// todo: duplicate not working[? for wrap only? ]
private static final Color SelectionColor = GUI.getTextHighlightColor();//VueResources.getColor("mapViewer.textBox.selection.color");
private boolean revert = false;
private static boolean TestDebug = false;
private static boolean TestHarness = false;
private LWComponent lwc;
/** bounds: generally used by the component as local coordinates (relative to the coordinate 0,0)
* The width/height are set here in TextBox */
private final Rectangle2D.Float mBounds = new Rectangle2D.Float();
private boolean wasOpaque; /** were we opaque before we started an edit? */
private float mMaxCharWidth;
private float mMaxWordWidth;
// private RichTextHighlighter vueHighlighter = null;
RichTextBox(LWComponent lwc)
{
this(lwc, null);
}
RichTextBox(LWComponent lwc, String text)
{
if (DEBUG.TEXT && DEBUG.LAYOUT) tufts.Util.printClassTrace("tufts.vue.", "NEW RichTextBox, txt=" + text);
if (TestDebug||DEBUG.TEXT) out("NEW [" + text + "] " + lwc);
// vueHighlighter = new RichTextHighlighter(this);
// this.setHighlighter(vueHighlighter);
SHTMLEditorKit kit = new SHTMLEditorKit(/* renderMode */);
//kit.resetStyleSheet();
setEditorKit(kit);
this.lwc = lwc;
setDragEnabled(false);
setBorder(null);
if (text != null)
setText(text);
setMargin(null);
setOpaque(false); // don't bother to paint background
setVisible(true);
addMouseListener(this);
addKeyListener(this);
addFocusListener(this);
getDocument().addDocumentListener(this);
if (VueUtil.isWindowsPlatform() && SelectionColor != null)
setSelectionColor(SelectionColor);
if (VueUtil.isWindowsPlatform() && SelectionColor != null)
setSelectedTextColor(Color.black);
mBounds.x = Float.NaN; // mark as uninitialized
mBounds.y = Float.NaN; // mark as uninitialized
if (TestDebug||DEBUG.TEXT) out("constructed " + getSize());
}
LWComponent getLWC() {
return this.lwc;
}
/*
* When activated for editing, draw an appropriate background color
* for the node -- the we need to do because if it's a small on-screen
* font at the moment (depending on zoom level), we make the
* text box always appear at the 100% zoomed font (because we're
* not managing scaled repaint of the added object or retargeting
* scaled mouse events, etc). Also, when it's transparent, the
* whole map has to be repainted each cursor blink just in case
* there is some LWComponent under the transparent text item.
* (Tho with a very small clip region).
*/
private Font preZoomFont = null;
private String mUnchangedText;
private Dimension mUnchangedSize;
private boolean keyWasPressed = false;
/** called by MapViewer before we go active on the map */
void saveCurrentState()
{
mUnchangedText = getText();
mUnchangedSize = getSize();
}
/** deprecated */
void saveCurrentText() {
saveCurrentState();
}
public double getScaledWidth()
{
java.awt.Container parent = getParent();
double zoom = ((MapViewer)parent).getZoomFactor();
zoom *= lwc.getMapScale();
return getWidth() * zoom;
}
public double getScaledHeight()
{
java.awt.Container parent = getParent();
double zoom = ((MapViewer)parent).getZoomFactor();
zoom *= lwc.getMapScale();
return getHeight() * zoom;
}
public double getUnscaledWidth()
{
java.awt.Container parent = getParent();
double zoom = ((MapViewer)parent).getZoomFactor();
zoom *= lwc.getMapScale();
return getSize().width / zoom;
}
public double getUnscaledHeight()
{
java.awt.Container parent = getParent();
double zoom = ((MapViewer)parent).getZoomFactor();
zoom *= lwc.getMapScale();
return getSize().height / zoom;
}
Dimension preAddDimension= null;
@Override
public void addNotify()
{
if (TestDebug||DEBUG.TEXT) out("*** ADDNOTIFY ***");
if (getText().length() < 1)
setText("<label>");
keyWasPressed = false;
//Dimension size = getPreferredSize();
((SHTMLDocument)getDocument()).setEditing(true);
// System.out.println("ZOOM : " + VUE.getActiveViewer().getZoomFactor());
java.awt.Container parent = getParent();
double zoom = ((MapViewer)parent).getZoomFactor();
zoom *= lwc.getMapScale();
((SHTMLDocument)getDocument()).setZoomFactor(zoom);
super.addNotify();
preAddDimension= new Dimension((int)getWidth(),(int)getHeight());
// System.out.println(preAddDimension.toString());
// System.out.println(getPreferredSize().toString());
this.setSize(new Dimension((int)getScaledWidth(),(int)getScaledHeight()));
wasOpaque = isOpaque();
Color background = lwc.getRenderFillColor(null);
//if (c == null && lwc.getParent() != null && lwc.getParent() instanceof LWNode)
final LWContainer nodeParent = lwc.getParent();
if (background == null && nodeParent != null)
{
background = nodeParent.getRenderFillColor(null); // todo: only handles 1 level transparent embed!
}
// todo: could also consider using map background if the node itself
// is transpatent (has no fill color)
// TODO: this workaround until we can recursively find a real fill color
// node that for SLIDES, we'd have to get awfully fancy and
// usually pull the color of the master slide (unless the slide
// happened to have it's own special color). Only super clean
// way to do this would be to have established some kind of
// rendering pipeline record... (yeah, right)
if (background == null) background = Color.gray;
//out("BACKGROUND COLOR " + background);
// TODO: the *selection* color always appears to be gray in for edits
// in the slide viewer on the mac, even if we manually set the selection
// color (which works in the main MapViewer) -- this is an oddity...
if (background != null) {
// note that if we set opaque to false, interaction speed is
// noticably slowed down in edit mode because it has to consider
// repainting the entire map each cursor blink as the object
// is transparent, and thus it's background is the displayed
// map. So if we can guess at a reasonable fill color in edit mode,
// we temporarily set us to opaque.
setOpaque(true);
setBackground(background);
}
//// setSize(getPreferredSize());
}
/*
* Return to the regular transparent state.
*/
@Override
public void removeNotify()
{
if (TestDebug||DEBUG.TEXT) out("*** REMOVENOTIFY ***");
//------------------------------------------------------------------
// We need to clear any text selection here as a workaround
// for an obscure bug where sometimes if the focus change is
// to a pop-up menu, the edit properly goes inactive, but the
// selection within it is still drawn with it's highlighted
// background.
clearSelection();
//------------------------------------------------------------------
super.removeNotify();
((SHTMLDocument)getDocument()).setEditing(false);
java.awt.Container parent = getParent();
double zoom = ((MapViewer)parent).getZoomFactor();
zoom *= lwc.getMapScale();
((SHTMLDocument)getDocument()).setZoomFactor(zoom);
// System.out.println("pref Height : " + getHeight());
// System.out.println("pref : " + getHeight()/zoomFactor);
preAddDimension.height = (int)(getHeight() / zoom);
preAddDimension.width = (int)(getWidth() / zoom);
this.setSize(preAddDimension);
if (mFirstAfterAddNotify == false) {
// if cleared, it was used
out("restoring expanded width");
// setSize(new Dimension(getWidth(), getHeight()));
//out("SKPPING restoring expanded width");
} else
mFirstAfterAddNotify = false;
setBorder(null);
if (preZoomFont != null) {
setDocumentFont(preZoomFont);
preZoomFont = null;
// if (WrapText) {
// adjustSizeDynamically();
//} else {
setSize(getPreferredSize());
// WE MUST DO THIS A SECOND TIME TO MAKE SURE THIS WORKS:
// JTextPane can actually produce inconsistent results
// when getPreferredSize() is called, especially if it's
// results were just use to set the size of the object.
// A second get/set produces more reliable results.
//// setSize(getPreferredSize());
// }
}
if (wasOpaque != isOpaque())
setOpaque(wasOpaque);
if (TestDebug||DEBUG.TEXT) out("*** REMOVENOTIFY end: insets="+getInsets());
}
public void clearSelection() {
// this set's the "mark to the point" -- sets them to the same
// location, thus clearing the selection.
setCaretPosition(getCaretPosition());
}
@Override
public void setText(String text)
{
super.setText(text);
//// setSize(getPreferredSize());
//
if (lwc.getParent() !=null)
lwc.getParent().layoutChildren();
//setSize(getPreferredSize());
}
public void setXMLText(String text)
{
super.setText(text);
}
private void setDocumentFont(Font f)
{
}
private void setDocumentColor(Color c)
{
}
private static void setFontAttributes(MutableAttributeSet a, Font f)
{
}
/***********
*******NOTES FROM FONT EDITOR PANEL TO HELP WITH COPY/PASTE OF STYLES.
globalSizeListener = new ActionListener(){
public void actionPerformed(ActionEvent fe)
{
if (suspendItemListeners)
return;
if (lwtext == null)
return;
lwtext.richLabelBox.selectAll();
final String textSize = mSizeField.getSelectedItem().toString();
SHTMLDocument doc = (SHTMLDocument)lwtext.richLabelBox.getDocument();
SimpleAttributeSet set = new SimpleAttributeSet();
Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_SIZE,
textSize);
set.addAttribute(HTML.Attribute.SIZE, textSize);
lwtext.richLabelBox.applyAttributes(set, false);
lwtext.richLabelBox.select(0,0);
//lwtext.setLabel0(lwtext.richLabelBox.getRichText(), false);
lwtext.richLabelBox.setSize(lwtext.richLabelBox.getPreferredSize());
if (lwtext.getParent() !=null)
lwtext.getParent().layout();
lwtext.notify(lwtext.richLabelBox, LWKey.Repaint);
}
};
globalFaceListener = new ActionListener(){
public void actionPerformed(ActionEvent fe)
{
if (suspendItemListeners)
return;
if (lwtext == null)
return;
lwtext.richLabelBox.selectAll();
SHTMLDocument doc = (SHTMLDocument)lwtext.richLabelBox.getDocument();
SimpleAttributeSet set = new SimpleAttributeSet();
Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_FAMILY,
mFontCombo.getSelectedItem().toString());
set.addAttribute(HTML.Attribute.FACE, mFontCombo.getSelectedItem().toString());
lwtext.richLabelBox.applyAttributes(set, false);
lwtext.richLabelBox.select(0,0);
lwtext.richLabelBox.setSize(lwtext.richLabelBox.getPreferredSize());
if (lwtext.getParent() !=null)
lwtext.getParent().layout();
lwtext.notify(lwtext.richLabelBox, LWKey.Repaint);
}
};
private final PropertyChangeListener RichTextColorChangeListener =
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if (e instanceof LWPropertyChangeEvent == false)
return;
final RichTextBox activeRTB = (RichTextBox) VUE.getActive(RichTextBox.class);
if (activeRTB == null && lwtext == null) {
// no problem: just ignore if there's no active edit
//tufts.Util.printStackTrace("FEP propertyChange: no active RichTextBox: " + e);
return;
}
if (lwtext !=null)
lwtext.richLabelBox.selectAll();
final Color color = (Color) e.getNewValue();
final SimpleAttributeSet set = new SimpleAttributeSet();
final String colorString = "#" + Integer.toHexString(color.getRGB()).substring(2);
toggleBulletsAction.setColor(colorString);
toggleNumbersAction.setColor(colorString);
Util.styleSheet().addCSSAttribute(set, CSS.Attribute.COLOR, colorString);
set.addAttribute(HTML.Attribute.COLOR, colorString);
if (activeRTB != null)
activeRTB.applyAttributes(set, false);
else
lwtext.richLabelBox.applyAttributes(set, false);
if (lwtext !=null)
{
lwtext.richLabelBox.select(0,0);
lwtext.richLabelBox.select(0,0);
//lwtext.setLabel0(lwtext.richLabelBox.getRichText(), false);
lwtext.richLabelBox.setSize(lwtext.richLabelBox.getPreferredSize());
if (lwtext.getParent() !=null)
lwtext.getParent().layout();
lwtext.notify(lwtext.richLabelBox, LWKey.Repaint);
}
}
};
************/
// this called every time setText is called to ensure we get
// the font style encoded in our owning LWComponent
void copyStyle(LWComponent c)
{
if (DEBUG.TEXT)
out("copyStyle " + c);
//Basic Setup
LWText srcText = (LWText)c;
RichTextBox srcBox = srcText.getRichLabelBox();
selectAll();
SHTMLDocument srcDoc = (SHTMLDocument)srcBox.getDocument();
SHTMLDocument doc = (SHTMLDocument)getDocument();
SimpleAttributeSet set = new SimpleAttributeSet();
SimpleAttributeSet alignSet = new SimpleAttributeSet();
//Gather source information
Element paragraphElement = srcDoc.getParagraphElement(1);
if (paragraphElement.getName().equals("p-implied")) //we're in a list item
paragraphElement = paragraphElement.getParentElement();
AttributeSet paragraphAttributeSet = paragraphElement.getAttributes();
Element charElem = srcDoc.getCharacterElement(1);
AttributeSet charSet = charElem.getAttributes();
Enumeration characterAttributeEnum = charSet.getAttributeNames();
Enumeration elementEnum = paragraphAttributeSet.getAttributeNames();
//Apply some attributes
// System.out.println(paragraphElement.toString());
while (elementEnum.hasMoreElements())
{
Object o = elementEnum.nextElement();
boolean isAlignSet = false;
//System.out.println("P :: " +o.toString());
if (o.toString().equals("text-align") && paragraphAttributeSet.getAttribute(o).toString().equals("left") && !isAlignSet)
{
//Left Align
Util.styleSheet().addCSSAttribute(alignSet, CSS.Attribute.TEXT_ALIGN, paragraphAttributeSet.getAttribute(o).toString());
alignSet.addAttribute(HTML.Attribute.ALIGN, paragraphAttributeSet.getAttribute(o).toString());
}
else if (o.toString().equals("text-align") && paragraphAttributeSet.getAttribute(o).toString().equals("center")&& !isAlignSet)
{
//Center Align
Util.styleSheet().addCSSAttribute(alignSet, CSS.Attribute.TEXT_ALIGN, paragraphAttributeSet.getAttribute(o).toString());
alignSet.addAttribute(HTML.Attribute.ALIGN, paragraphAttributeSet.getAttribute(o).toString());
}
else if (o.toString().equals("text-align") && paragraphAttributeSet.getAttribute(o).toString().equals("right")&& !isAlignSet)
{
//Right Align
Util.styleSheet().addCSSAttribute(alignSet, CSS.Attribute.TEXT_ALIGN, paragraphAttributeSet.getAttribute(o).toString());
alignSet.addAttribute(HTML.Attribute.ALIGN, paragraphAttributeSet.getAttribute(o).toString());
}
if ((o.toString().equals("font-size")) ||(o.toString().equals("size")))
{
//Font Size
Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_SIZE, paragraphAttributeSet.getAttribute(o).toString());
set.addAttribute(HTML.Attribute.SIZE, paragraphAttributeSet.getAttribute(o).toString());
}
else if ((o.toString().equals("font-family")) || (o.toString().equals("font-face")) || (o.toString().equals("face")))
{
//Font Face
Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_FAMILY, paragraphAttributeSet.getAttribute(o).toString());
set.addAttribute(HTML.Attribute.FACE, paragraphAttributeSet.getAttribute(o).toString());
}
}
boolean isBold = false;
boolean isItalic = false;
boolean isUnderline = false;
while (characterAttributeEnum.hasMoreElements())
{
Object o = characterAttributeEnum.nextElement();
//System.out.println("C :: " +o.toString() + "XXX " + charSet.getAttribute(o).toString());
//System.out.println("Character element : " + o.toString() + " , " + charSet.getAttribute(o));
if ((o.toString().equals("color")))
{
//Color
Util.styleSheet().addCSSAttribute(set, CSS.Attribute.COLOR, charSet.getAttribute(o).toString());
set.addAttribute(HTML.Attribute.COLOR, charSet.getAttribute(o).toString());
}
if ((o.toString().equals("font-size")) ||(o.toString().equals("size")))
{
//Font Size
Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_SIZE, charSet.getAttribute(o).toString());
set.addAttribute(HTML.Attribute.SIZE, charSet.getAttribute(o).toString());
}
if ((o.toString().equals("font-family")) || (o.toString().equals("font-face")) || (o.toString().equals("face")))
{
//Font Face
Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_FAMILY, charSet.getAttribute(o).toString());
set.addAttribute(HTML.Attribute.FACE, charSet.getAttribute(o).toString());
}
if ((o.toString().equals("font-weight") && charSet.getAttribute(o).toString().equals("bold")) || o.toString().equals("b"))
{
Util.styleSheet().addCSSAttribute(set,CSS.Attribute.FONT_WEIGHT,charSet.getAttribute(o).toString());
}
if ((o.toString().equals("font-style") && charSet.getAttribute(o).toString().equals("italic")) || o.toString().equals("i"))
{
Util.styleSheet().addCSSAttribute(set,CSS.Attribute.FONT_STYLE,charSet.getAttribute(o).toString());
}
if ((o.toString().equals("text-decoration") && charSet.getAttribute(o).toString().equals("underline")) || o.toString().equals("u"))
{
Util.styleSheet().addCSSAttribute(set,CSS.Attribute.TEXT_DECORATION,charSet.getAttribute(o).toString());
}
}//done looking at character attributes
applyAttributes(set, false);
lwc.notify(this, LWKey.Repaint);
applyAttributes(alignSet, true);
lwc.notify(this, LWKey.Repaint);
clearSelection();
try{
setSize(getPreferredSize());
}
catch(NullPointerException npe){}
/* set = new SimpleAttributeSet();
final String colorString = "#" + Integer.toHexString(color.getRGB()).substring(2);
toggleBulletsAction.setColor(colorString);
toggleNumbersAction.setColor(colorString);
Util.styleSheet().addCSSAttribute(set, CSS.Attribute.COLOR, colorString);
set.addAttribute(HTML.Attribute.COLOR, colorString);
if (activeRTB != null)
activeRTB.applyAttributes(set, false);
else
lwtext.richLabelBox.applyAttributes(set, false);
if (lwtext !=null)
{
lwtext.richLabelBox.select(0,0);
lwtext.richLabelBox.select(0,0);
//lwtext.setLabel0(lwtext.richLabelBox.getRichText(), false);
lwtext.richLabelBox.setSize(lwtext.richLabelBox.getPreferredSize());
if (lwtext.getParent() !=null)
lwtext.getParent().layout();
lwtext.notify(lwtext.richLabelBox, LWKey.Repaint);
}*/
}
/** override Container.doLayout: only called when we've been added to a map for interactive editing.
* Is called during interactive edit's after each modification.
*/
public void doLayout()
{
if (getParent() instanceof MapViewer) {
// Automatic layout (e.g. FlowLayout)
// produces two layout passes -- perhaps
// this is why we need to call this TWICE
// here so that the box size doesn't
// temporarily flash bigger on every update.
if (TestDebug || DEBUG.LAYOUT) out("doLayout w/adjustSizeDynamically");
Dimension d = getPreferredSize();
setSize(d);
// super.doLayout();
} else {
if (!TestHarness)
new Throwable(this + " UNPARENTED doLayout").printStackTrace();
}
}
private boolean mFirstAfterAddNotify = false;
public void keyReleased(KeyEvent e) { e.consume(); }
public void keyTyped(KeyEvent e) {
// todo: would be nice if centered labels stayed center as you typed them
//setLocation((int)lwc.getLabelX(), (int)lwc.getLabelY());
// needs something else, plus can't work at zoom because
// width isn't updated till the end (because width at + zoom
// is overstated based on temporarily scaled font)
// Man, it would be REALLY nice if we could paint the
// real component in a scaled GC w/out the font tweaking --
// problems like this would go away.
}
private static boolean isFinishEditKeyPress(KeyEvent e) {
// if we hit return key either on numpad ("enter" key), or
// with any modifier down except a shift alone (in case of
// caps lock) complete the edit.
return e.getKeyCode() == KeyEvent.VK_ENTER &&
(
e.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD ||
(e.getModifiersEx() != 0 && !e.isShiftDown())
)
== true;
//== false; // reversed logic of below description
}
private Container removeAsEdit() {
Container parent = getParent();
if (parent != null)
parent.remove(this);
else
out("FAILED TO FIND PARENT ATTEMPTING TO REMOVE SELF");
return parent;
}
public void keyPressed(KeyEvent e)
{
if (DEBUG.KEYS) out(e.toString());
//System.out.println("TextBox: " + e);
//if (VueUtil.isAbortKey(e)) // check for ESCAPE for CTRL-Z or OPTION-Z if on mac
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
e.consume();
// System.out.println(mUnchangedText);
//setText(mUnchangedText);
revert = true;
getParent().remove(this); // will trigger a save (via focusLost)
return;
// setSize(mUnchangedSize); // todo: won't be good enough if we ever resize the actual node as we type
} else if (isFinishEditKeyPress(e)) {
keyWasPressed = true;
e.consume();
getParent().remove(this); // will trigger a save (via focusLost)
VUE.getFormattingPanel().getTextPropsPane().getFontEditorPanel().updateFormatControlsTB(this);
} else if (e.getKeyCode() == KeyEvent.VK_U && e.isMetaDown()) {
e.consume();
String t = getText();
if (e.isShiftDown())
setText(t.toUpperCase()); // upper whole string
else
setText(Character.toTitleCase(t.charAt(0)) + t.substring(1)); // upper first char
} else
keyWasPressed = true;
// Dimension d = preAddDimension;
// d.height = this.getPreferredSize().height;
// setSize(getPreferredSize());
// action keys will be ignored if we consume this here!
// (e.g., "enter" does nothing)
//e.consume();
}
/**
* This is what triggers the final save of the new text value to the LWComponent,
* and notify's the UndoManager that a user action was completed.
*/
public void focusLost(FocusEvent e)
{
final java.awt.Component opposite = e.getOppositeComponent();
if (opposite != null) {
if ((opposite.getName() != null && opposite.getName().equals(FontEditorPanel.SIZE_FIELD_NAME)) ||
opposite.getClass() == ColorMenuButton.class)
return;
else if (opposite.getClass() == FontEditorPanel.class ||
DockWindow.isDockWindow(opposite) ||
// todo: something more generic than this getName check: set a property on the JComponent tagging it as a tool/editor?
opposite.getClass() == JComboBox.class ||
(opposite.getName() != null && opposite.getName().equals(tufts.vue.gui.ColorMenuButton.COLOR_POPUP_NAME)) ||
//quaqua makes this a bit awkard, this is for quaqua's color chooser.
(opposite.getName() != null && opposite.getName().equals("dialog0")))
{
//Earlier i was just returning here, but this creates a problem
//because the component has already lost the focus...and so it doesn't
//get another focusLost the next time....so re-request the focus if you've lost
//it so that we get the event again when we really want to get rid of the focus
//so we can properly remove the edit control.
requestFocus();
return;
}
}
else if (opposite == null)
{
if (DEBUG.FOCUS)
outc("Focus not lost because opposite component = null");
requestFocus();
return;
}
//System.out.println(e.getComponent().toString());
//System.out.println(e.getOppositeComponent().toString());
if (TestDebug||DEBUG.FOCUS)
outc("focusLost to " + e.getOppositeComponent() + " " + opposite.getName());
if (TestHarness == false && getParent() != null)
{
getParent().remove(this);
// VUE.getFormattingPanel().getTextPropsPane().getFontEditorPanel().updateFormatControlsTB(this);
}
if (keyWasPressed || !keyWasPressed) { // TODO: as per VueTextField, need to handle drag & drop detect
// only do this if they typed something (so we don't wind up with "label"
// for the label on an accidental edit activation)
if (TestDebug||DEBUG.FOCUS) out("key was pressed; setting label to: [" + getText() + "]");
String text = getText();
if (revert)
{
text = mUnchangedText;
revert =false;
// setText(text);
}
lwc.setLabel0(text, false);
VUE.getUndoManager().mark();
}
//// setSize(getPreferredSize());
// lwc.setSize(mBounds.width, mBounds.height);
if (lwc.getParent() !=null && lwc.getParent() instanceof LWNode)
lwc.getParent().layout();
lwc.notify(this, LWKey.Repaint);
}
public void focusGained(FocusEvent e)
{
if (TestDebug||DEBUG.FOCUS) outc("focusGained from " + e.getOppositeComponent());
}
/** do not use, or won't be able to get out actual text height */
@Override
public void setPreferredSize(Dimension preferredSize) {
if (true||TestDebug) out("setPreferred " + preferredSize);
super.setPreferredSize(preferredSize);
}
/*@Override
public Dimension getPreferredSize() {
Dimension s = super.//getPreferredSize();
//getMinimumSize();//debug
//s.width = (int) lwc.getWidth();
//System.out.println("MTP lwcWidth " + lwc.getWidth());
if (getParent() != null)
s.width += 1; // leave room for cursor, which at least on mac gets clipped if at EOL
//if (getParent() == null)
// s.width += 10;//fudge factor for understated string widths (don't do this here -- need best accuracy here)
if (TestDebug) out("getPrefer", s);
//if (TestDebug && DEBUG.META) new Throwable("getPreferredSize").printStackTrace();
return s;
}
*/
/*
* Style style = ((HTMLDocument) getDocument()).getStyleSheet().getStyle("body");
Object a = style.getAttribute(javax.swing.text.html.CSS.Attribute.FONT_SIZE);
if (a !=null)
{ if (DEBUG.TEXT)
out("got style");
int diff =0;
Integer i = new Integer(a.toString());
diff = i.intValue();
minS.height = minS.height - diff;
}
*/
public Dimension getPreferredSize()
{
Dimension s = null;
// Dimension s = super.getPreferredSize();
Dimension minS = getMinimumSize();
//System.out.println(javax.swing.SwingUtilities.getLocalBounds(this));
//if (TestDebug||DEBUG.TEXT) out("getPrefer", s);
// System.out.println("GetPrefSize : " + mBounds.width + " "+ s.width);
// System.out.println("Required Lines : " + s.width/mBounds.width);
Caret c = this.getCaret();
Point position = c.getMagicCaretPosition();
// if (position != null)
// System.out.println("magic caret : " + position.getX());
//HTMLDocument builds a hierarchical Element structure where each Element
//represents a structural block of HTML, and not just a line of text. so I'm not sure
//how to figure out what line you're on.
Dimension min = new Dimension();
final Dimension text = getMinimumSize();
min.width = text.width;
int EdgePadY = 0; // Was 3 in VUE 1.5
int LabelPadLeft = 8; // Was 6 in VUE 1.5; fixed
//System.out.println("Text.height : " + text.height);
// *** set icon Y position in all cases to a centered vertical
// position, but never such that baseline is below bottom of
// first icon -- this is tricky tho, as first icon can move
// down a bit to be centered with the label!
min.width += LabelPadLeft;
min.width = Math.max(min.width,minS.width);
/*
System.out.println("Min.Width =" + min.width);
System.out.println("MinS.Width =" + minS.width);
System.out.println("S.Width =" + s.width);
System.out.println("MBounds.Width = " + mBounds.width);
System.out.println("GetSize.Width = " + getSize().width);
System.out.println("Bounds : " + this.getBounds().width);
System.out.println("LWC width : " + lwc.width);
System.out.println("LWC bounds : " + lwc.getBounds().width);
System.out.println("LWC layoutbounds : " + lwc.getLayoutBounds().width);
System.out.println("LWC min size : " + lwc.getMinimumSize().width);
System.out.println("Get super width : " + super.getWidth());
System.out.println("Selection Eend : " + this.getSelectionEnd());
System.out.println("Selection Start : " + this.getSelectionStart());
System.out.println("Bounds Box Width : " +this.getBoxBounds().getBounds().width);
System.out.println("Visible Rect : " +this.getVisibleRect().width);*/
//** Rectangle p2 = null;
//if (this.getGraphics() != null)
//{
// System.out.println("GRAPHICS : " + this.getGraphics().toString());
// if (this.getGraphics().getClipBounds() != null)
// System.out.println("GRAPHICS : " + this.getGraphics().getClipBounds().width);
// this.getPreferredScrollableViewportSize().width
//
//
View ui = getUI().getRootView(this);
ui = ui.createFragment(this.getSelectionStart(), this.getSelectionEnd());
//System.out.println("min span: " + ui.getMinimumSpan(View.X_AXIS));
//** int start = getSelectionStart();
//** int end = getSelectionEnd();
//** float f = ui.getPreferredSpan(View.X_AXIS);
//System.out.println("Preferred span : " + f);
//** f = ui.getPreferredSpan(View.Y_AXIS);
//System.out.println("Preferred span y : " + f);
//** f = ui.getPreferredSpan(View.X_AXIS);
//System.out.println("{referred span X : " + f);
//ui.breakView(View.X_AXIS, offset, pos, len)
//((HTMLEditorKit)this.getEditorKit()).getViewFactory().create(this.getDocument().getDefaultRootElement()).
// System.out.println("BREAK WEIGHT : " + ui.getBreakWeight(View.X_AXIS, this.getCaretPosition(), this.getSelectionEnd()));
// System.out.println("RESIZE WEIGHT : " + ui.getResizeWeight(View.X_AXIS));
// System.out.println("RESIZE WEIGHTY : " + ui.getResizeWeight(View.Y_AXIS));
//** float align = ui.getAlignment(View.X_AXIS);
// System.out.println("align span : " + align);
//** try {
//** p2 = this.modelToView(this.getSelectionEnd());
//**
//** } catch (BadLocationException e) {
//** //Nothing we can do really.
//** }
//** //if (p2 != null)
//** //System.out.println("Selection end rect : " + p2.x);
//**
if (mBounds.width > 0)
{
//** if (p2 != null && p2.x > mBounds.width)
//** mBounds.width = p2.x;
//int height = 48;
/*if (getFont() != null &&this.getFontMetrics(getFont())!=null)
{
System.out.println("A:"+this.getFontMetrics(getFont()).getMaxAscent());
System.out.println("B:"+this.getFontMetrics(getFont()).getMaxDescent());
}*/
int height = super.getPreferredSize().height;//s.height;//Math.max(s.height, 48);
Style style = ((HTMLDocument) getDocument()).getStyleSheet().getStyle("body");
Object a = style.getAttribute(javax.swing.text.html.CSS.Attribute.FONT_SIZE);
if (a !=null)
{ if (DEBUG.TEXT)
out("got style");
int diff =0;
Integer i = new Integer(a.toString());
diff = i.intValue();
//minS.height = minS.height - diff;
if (VUE.getActiveViewer() !=null)
{
java.awt.Container parent = getParent();
double zoom = 1.0;
if (parent !=null)
zoom = ((MapViewer)parent).getZoomFactor();
else
{
//parent is null during duplicate.
zoom = VUE.getActiveViewer().getZoomFactor();
}
zoom *= lwc.getMapScale();
diff *= zoom;
}
height = height - diff;
}
if (position !=null)
{
float minSpan = ui.getMinimumSpan(View.X_AXIS);
float mins2 = Math.max(minSpan,(float)position.getX());
s = new Dimension((int)(mBounds.width > mins2 ? mBounds.width : mins2),(int)height);
}
else
{
float minSpan = ui.getMinimumSpan(View.X_AXIS);
s = new Dimension((int)(mBounds.width > minSpan ? mBounds.width : minSpan),(int)height);
}
//float widthRatio = ((float)s.width /(float)lwc.width);
// float heightRatio =((float)s.height/(float)lwc.height);
/* if ((widthRatio > 0) && (heightRatio > 0) && (widthRatio != heightRatio))
{
s.height = (int)(lwc.height * widthRatio);
// System.out.print("NEW SHEIGHT : " + s.height);
// prevDim = s;
//
}
System.out.println("width ratio : " + widthRatio);
System.out.println("height ratio : " + heightRatio);
*/
/*if (this.getGraphics() != null)
System.out.println("FONT HEIGHT : " +this.getGraphics().getFontMetrics().getMaxAscent());
System.out.println("===================================");
System.out.println("super pref height : " + s.height);
System.out.println("Min Vertical Span : " +ui.getMinimumSpan(View.Y_AXIS));
System.out.println("MBounds height : " + mBounds.height);
System.out.println("super pref height : " + s.height);
System.out.println("Min Vertical Span : " +ui.getMinimumSpan(View.Y_AXIS));
System.out.println("MBounds height : " + mBounds.height);
System.out.println("Mins height : " + min.height);
System.out.println("Bounds Y : " + this.getBounds().y);
System.out.println("Bounds height :" +this.getBounds().height);
System.out.println("Bounds2D Y : " +this.getBoxBounds().getY());
System.out.println("Bounds2D height : " +this.getBoxBounds().getHeight());
if (this.getCaret().getMagicCaretPosition() != null)
System.out.println("Cursor : " + this.getCaret().getMagicCaretPosition().toString());
//this.getCaret().getMagicCaretPosition().
System.out.println("===================================");*/
/*if (position != null)
{
System.out.println("Y Pos : " + position.getY());
try {
Rectangle r = modelToView(this.getCaretPosition());
System.out.println("model pos : " + r.y);
System.out.println("Model height : " + r.height);
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}*/
}
// s.width = (int)(s.width * VUE.getActiveViewer().getZoomFactor());
// s.height = (int)(s.height * VUE.getActiveViewer().getZoomFactor());
// s.height=(int) this.getBoxBounds().getHeight();
if (this.getBoxBounds() !=null && s.height < this.getBoxBounds().getHeight())
{
s.height = (int)this.getBoxBounds().getHeight();
}
return s;
}
public void setSize(Size s) {
setSize(s.dim());
}
public void setSize(Dimension s) {
if (TestDebug||DEBUG.TEXT) out("setSize", s);
mBounds.width = s.width;
mBounds.height = s.height;
super.setSize(s);
}
/**
* Set the size to the given size, increasing or decreasing height as
* needed to provide a fit around our text
*/
public void setSizeFlexHeight(Size newSize) {
//------------------------------------------------------------------
// Tell the JTextPane to take on the size requested. It will
// then set the preferred height to the minimum height able to
// contain the given text at that width.
//------------------------------------------------------------------
setSize(newSize);
//------------------------------------------------------------------
// Now adjust our height to the new preferred height, which should
// just contain our text.
//------------------------------------------------------------------
final Dimension s = getPreferredSize();
s.width = getWidth();
if (TestDebug||DEBUG.TEXT) out("flexHeigt", s);
super.setSize(s.width, s.height);
}
public void setSize(float w, float h) {
setSize(new Dimension((int)w, (int)h));
}
public void setPreferredSize(float w, float h) {
setPreferredSize(new Dimension((int)w, (int)h));
}
public Dimension getSize() {
Dimension s = super.getSize();
//s.width = (int) lwc.getWidth();
if (TestDebug) out("getSize", s);
//if (DEBUG.TEXT&&DEBUG.META) new Throwable("getSize").printStackTrace();
return s;
}
public void setMaximumSize(Dimension s) {
if (true||TestDebug) out("setMaximumSize", s);
super.setMaximumSize(s);
}
public Dimension getMaximumSize() {
Dimension s = super.getMaximumSize();
if (TestDebug||DEBUG.TEXT) out("getMaximumSize", s);
return s;
}
public void setMinimumSize(Dimension s) {
if (true||TestDebug) out("setMinimumSize", s);
super.setMinimumSize(s);
}
public Dimension getMinimumSize() {
Dimension s = super.getMinimumSize();
Style style = ((HTMLDocument) getDocument()).getStyleSheet().getStyle("body");
Object a = style.getAttribute(javax.swing.text.html.CSS.Attribute.FONT_SIZE);
if (a !=null)
{ if (DEBUG.TEXT)
out("got style");
int diff =0;
Integer i = new Integer(a.toString());
diff = i.intValue();
if (VUE.getActiveViewer()!=null)
{
java.awt.Container parent = getParent();
double zoom = 1.0;
// System.out.println("SHEIGHT : " + s.height);
if (parent !=null)
{
zoom = ((MapViewer)parent).getZoomFactor();
zoom *= lwc.getMapScale();
diff *= zoom;
// System.out.println("if ::: " + diff);
}
else
{
if (VUE.getActiveViewer()!=null)
diff *= VUE.getActiveViewer().getZoomFactor();
diff *=lwc.getMapScale();
// System.out.println("else ::: " + diff);
}
}
/*if (a !=null)
{ if (DEBUG.TEXT)
out("got style");
int diff =0;
Integer i = new Integer(a.toString());
diff = i.intValue();
if (VUE.getActiveViewer()!=null)
diff *= VUE.getActiveViewer().getZoomFactor();
s.height = s.height - diff;
}*/
s.height = s.height - diff;
}
if (TestDebug||DEBUG.TEXT) out("getMinimumSize", s);
return s;
}
@Override
public void reshape(int x, int y, int w, int h) {
if (TestDebug||DEBUG.TEXT) {
boolean change = getX() != x || getY() != y || getWidth() != w || getHeight() != h;
if (change) {
out(" oldshape " + tufts.Util.out(getBounds()));
out(" reshape " + w + "x" + h + " " + x + "," + y);
}
//out(" reshape " + w + "x" + h + " " + x + "," + y + (change ? "" : " (no change)"));
if (DEBUG.META && change)
new Throwable("reshape").printStackTrace();
}
super.reshape(x, y, w, h);
if (TestDebug||DEBUG.TEXT) {
Rectangle b = getBounds();
if (b.x != x || b.y != y || b.width != w || b.height != h)
out("BADBOUNDS " + tufts.Util.out(b));
}
}
public Rectangle2D getBoxBounds() {
return mBounds;
}
public boolean boxContains(float x, float y)
{
return x >= mBounds.x
&& y >= mBounds.y
&& x <= mBounds.x + mBounds.width
&& y <= mBounds.y + mBounds.height;
}
public boolean boxIntersects(Rectangle2D rect)
{
return rect.intersects(mBounds);
}
public void setBoxLocation(float x, float y)
{
mBounds.x = x;
mBounds.y = y;
}
public void setBoxLocation(Point2D p)
{
setBoxLocation((float) p.getX(), (float) p.getY());
}
public void setBoxCenter(float x, float y) {
setBoxLocation(x - getBoxWidth() / 2,
y - getBoxHeight() / 2);
}
public Point2D.Float getBoxPoint()
{
return new Point2D.Float(mBounds.x, mBounds.y);
}
public float getBoxWidth() { return mBounds.width; };
public float getBoxHeight() { return mBounds.height; }
public float getBoxX() { return mBounds.x; };
public float getBoxY() { return mBounds.y; }
/*
void resizeToWidth(float w)
{
int width = (int) (w + 0.5f);
setSize(new Dimension(width, 999)); // can set height to 1 as we're ignore the set-size
// now the preferred height will be set to the real
// total text height at that width -- pull it back out and set actual size to same
Dimension ps = getPreferredSize();
setSize(new Dimension(width, (int)ps.getHeight()));
}
*/
public void Xpaint(Graphics g) {
super.paint(g);
g.setColor(Color.gray);
g.setClip(null);
g.drawRect(0,0, getWidth(), getHeight());
}
/* public Rectangle modelToView(int pos)
throws BadLocationException {
SHTMLDocument doc = (SHTMLDocument)getDocument();
double zoomFactor = doc.getZoomFactor();
Rectangle alloc;
Rectangle s = super.modelToView(pos);
alloc = s.getBounds();
alloc.x*=zoomFactor;
alloc.y*=zoomFactor;
alloc.width*=zoomFactor;
alloc.height*=zoomFactor;
return alloc;
}
public int viewToModel(Point p) {
SHTMLDocument doc = (SHTMLDocument)getDocument();
double zoomFactor = doc.getZoomFactor();
Point alloc = p;
alloc.x/=zoomFactor;
alloc.y/=zoomFactor;
return super.viewToModel(new Point(alloc));
}
*/
/*
public Rectangle getVisibleRect()
{
SHTMLDocument doc = (SHTMLDocument)getDocument();
double zoomFactor = doc.getZoomFactor();
Rectangle r = this.getBounds();
r.width =(int)(r.width * zoomFactor);
r.height = (int)(r.height * zoomFactor);
return r;
}*/
public void paintComponent(Graphics g)
{
if (TestDebug||DEBUG.TEXT) out("paintComponent @ " + getX() + "," + getY() + " parent=" + getParent());
final MapViewer viewer = (MapViewer) javax.swing.SwingUtilities.getAncestorOfClass(MapViewer.class, this);
Graphics2D g2d = (Graphics2D)g;
if (viewer != null)
{
g2d.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, viewer.AA_ON);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
}
// turn on anti-aliasing -- the cursor repaint loop doesn't
// set anti-aliasing, so text goes jiggy around cursor/in selection if we don't do this
//// g.clipRect(0, 0,getWidth(), getAdjustedHeight());
super.paintComponent(g2d);
//super.paintComponent(g);
if (true) {
// draw a border (we don't want to add one because that changes the preferred size at a bad time)
// g.setColor(Color.gray);
g.setClip(null);
final int xpad = 1;
final int ypad = 1;
g.drawRect(-xpad,-ypad, (int)((getWidth())+xpad*2-1), (int)((getHeight())+ypad*2-1));
}
}
private static final BasicStroke MinStroke = new BasicStroke(1/8f);
private static final BasicStroke MinStroke2 = new BasicStroke(1/24f);
/** @return true if hue value of Color is black, ignoring any alpha */
private boolean isBlack(Color c) {
return c != null && (c.getRGB() & 0xFFFFFF) == 0;
}
public void draw(DrawContext dc)
{
if (TestDebug) out("draw");
if (getParent() != null)
System.err.println("Warning: 2nd draw of an AWT drawn component!");
//todo: could try saving current translation or coordinates here,
// so have EXACT last position painted at. Tho we really should
// be able to compute it... problem is may not be at integer
// boundry at current translation, but have to be when we add it
// to the map -- tho hey, LWNode could force integer boundry
// when setting the translation before painting us.
if (DEBUG.BOXES && DEBUG.META) {
if (lwc.getLabel().indexOf('\n') < 0) {
TextRow r = new TextRow(lwc.getLabel(), lwc.getFont(), dc.g.getFontRenderContext());
dc.g.setColor(Color.lightGray);
r.draw(dc, 0, 0);
}
}
boolean restoreTextColor = false;
// if (dc.isBlackWhiteReversed() &&
// (dc.isPresenting() || lwc.isTransparent() /*|| isBlack(lwc.getFillColor())*/) &&
// isBlack(lwc.getTextColor())) {
// //System.out.println("reversing color to white for " + this);
// setDocumentColor(Color.white);
// inverted = true;
// } else
// inverted = false;
if (dc.isPresenting() && lwc.isTransparent()) {
// if the text color equals the background color when in a presentation
// (e.g. the master slide has a black background), and the text box
// has to fill of it's own for contrast, then temporarily swap
// the text color to white or black so it can be seen.
if (lwc.mTextColor.equals(dc.getBackgroundFill())) {
restoreTextColor = true;
if (lwc.mTextColor.brightness() > 0.5) {
setDocumentColor(DEBUG.Enabled ? Color.blue : Color.black);
} else {
setDocumentColor(DEBUG.Enabled ? Color.green : Color.white);
}
}
}
//super.paintBorder(g);
// // As of least Mac OS X 10.4.10 w/JVM 1.5.0_07 on 2007-08-13,
// // it appears there's no way to NOT render anti-aliased text,
// // unless there's some other way to override it in JTextPane/JTextComponent
// // Not a big deal -- we'd only like the option for a slight speed up
// // during animations.
// dc.g.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
// java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
// dc.g.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING,
// java.awt.RenderingHints.VALUE_ANTIALIAS_OFF);
// // Even this doesn't appear to help:
// putClientProperty(com.sun.java.swing.SwingUtilities2.AA_TEXT_PROPERTY_KEY, Boolean.FALSE);
super.paintComponent(dc.g);
//super.paint(g);
if (restoreTextColor) {
// return document color to black
setDocumentColor(lwc.mTextColor.get());
}
// draw a border for links -- why?
// and even if, better to handle in LWLink
/*
if (lwc instanceof LWLink) {
Dimension s = getSize();
if (lwc.isSelected())
g.setColor(COLOR_SELECTION);
else
g.setColor(Color.gray);
g.setStroke(MinStroke);
g.drawRect(0,0, s.width-1, s.height-2);
}
*/
Graphics2D g = dc.g;
if (DEBUG.BOXES) {
Dimension s = getPreferredSize();
g.setColor(Color.red);
dc.setAbsoluteStroke(0.5);
//g.setStroke(MinStroke);
g.drawRect(0,0, s.width, s.height);
//g.drawRect(0,0, s.width-1, s.height);
}
//s = getMinimumSize();
//g.setColor(Color.red);
//g.setStroke(new BasicStroke(1/8f));
//g.drawRect(0,0, s.width, s.height);
if (DEBUG.BOXES || getParent() != null) {
Dimension s = getSize();
g.setColor(Color.blue);
dc.setAbsoluteStroke(0.5);
//g.setStroke(MinStroke);
g.drawRect(0,0, s.width, s.height);
//g.drawRect(0,0, s.width-1, s.height);
}
}
private void handleChange() {
// appears to be happening too late for dynamic size adjust -- current character isnt include
}
public void removeUpdate(DocumentEvent de) {
if (TestDebug||DEBUG.TEXT) out("removeUpdate " + de);
handleChange();
}
public void changedUpdate(DocumentEvent de) {
if (TestDebug||DEBUG.TEXT) out("changeUpdate " + de.getType() + " len=" + de.getLength());
handleChange();
}
public void insertUpdate(DocumentEvent de) {
if (TestDebug||DEBUG.TEXT) out("insertUpdate " + de);
handleChange();
}
public String toString()
{
return "RichTextBox[" + lwc + "]";
}
@Override
public int getHeight()
{
return (int)super.getHeight();
}
private String id() {
return Integer.toHexString(System.identityHashCode(this));
}
private void out(String s) {
System.out.println("TextBox@" + id() + " [" + getText() + "] " + s);
//System.out.println("TextBox@" + id() + " " + s);
}
private void out(String s, Dimension d) {
out(VueUtil.pad(' ', 9, s, true) + " " + tufts.Util.out(d));
}
private void out(String s, Dimension d, String s2) {
out(VueUtil.pad(' ', 9, s, true) + " " + tufts.Util.out(d) + " " + s2);
}
private void outc(String s) {
System.out.println(this + " " + id() + " " + s);
}
public String getRichText()
{
String html = super.getText();
String patternStr = "size=\"(\\d*)\"";
String replacementStr = "size=\"$1\" style=\"font-size:$1;\"";
// Compile regular expression
Pattern pattern = Pattern.compile(patternStr);
// Replace all occurrences of pattern in input
Matcher matcher = pattern.matcher(html);
String output = matcher.replaceAll(replacementStr);
return output;
// return html;
}
public String getText()
{
return stripHTMLTags(super.getText());
}
private String stripHTMLTags( String message )
{
StringBuffer returnMessage = new StringBuffer(message);
int startPosition = message.indexOf("<"); // encountered the first opening brace
int endPosition = message.indexOf(">"); // encountered the first closing braces
while( startPosition != -1 ) {
returnMessage.delete( startPosition, endPosition+1 ); // remove the tag
startPosition = (returnMessage.toString()).indexOf("<"); // look for the next opening brace
endPosition = (returnMessage.toString()).indexOf(">"); // look for the next closing brace
}
return org.apache.commons.lang.StringEscapeUtils.unescapeHtml(returnMessage.toString().trim());
}
public void overrideTextColor(Color c)
{
//System.out.println("OVERRIDE TEXT COLOR : " + c.toString());
SimpleAttributeSet set = new SimpleAttributeSet();
String colorString = "#" + Integer.toHexString(
c.getRGB()).substring(2);
com.lightdev.app.shtm.Util.styleSheet().addCSSAttribute(set,
CSS.Attribute.COLOR, colorString);
set.addAttribute(HTML.Attribute.COLOR, colorString);
this.applyAttributesGlobally(set, true,false);
}
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mousePressed(MouseEvent e)
{
if (GUI.isMenuPopup(e))
{
displayContextMenu(e);
return;
}
}
private void displayContextMenu(MouseEvent e) {
getPopup(e).show(e.getComponent(), e.getX(), e.getY());
}
private JPopupMenu m = null;
private final JMenuItem copyItem = new JMenuItem(VueResources.getString("richtextbox.menu.copy"));
private final JMenuItem pasteItem = new JMenuItem(VueResources.getString("richtextbox.menu.paste"));
private JPopupMenu getPopup(MouseEvent e)
{
if (m == null)
{
m = new JPopupMenu(VueResources.getString("richtextbox.menu.textboxmenu"));
//copyItem.addActionListener(this);
//pasteItem.addActionListener(this);
//If you let this be focusable you'll loose the text box when
//the menu gets raised.
m.setFocusable(false);
m.add(copyItem);
m.add(pasteItem);
copyItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
RichTextBox.this.copy();
}
});
pasteItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
RichTextBox.this.paste();
setSize(getPreferredSize());
}
});
}
return m;
}
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
//if (e.getSource().equals(copyItem))
}
}