/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.smart;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import javax.swing.Action;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JEditorPane;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.StyledEditorKit.StyledTextAction;
import javax.swing.text.html.CSS;
import javax.swing.text.html.HTML;
import com.servoy.j2db.ISmartClientApplication;
import com.servoy.j2db.Messages;
import com.servoy.j2db.gui.CustomColorChooserDialog;
import com.servoy.j2db.gui.JFontChooser;
import com.servoy.j2db.persistence.ISupportTextSetup;
import com.servoy.j2db.util.PersistHelper;
import com.servoy.j2db.util.Utils;
import com.servoy.j2db.util.gui.FixedHTMLEditorKit;
import com.servoy.j2db.util.rtf.FixedRTFEditorKit;
import com.servoy.j2db.util.toolbar.Toolbar;
import com.servoy.j2db.util.toolbar.ToolbarButton;
import com.servoy.j2db.util.toolbar.ToolbarToggleButton;
/**
* Swing texttool bar for use in windows and non modal dialogs
* @author jblok
*/
public class TextToolbar extends Toolbar implements ActionListener
{
private HashMap actions = new HashMap();
private final ISmartClientApplication application;
private final Font[] allFonts;
private final Vector allFontNames;
private final JComboBox styleCombo;
private final JComboBox heightCombo;
private final ToolbarToggleButton bold;
private final ToolbarToggleButton italic;
private final ToolbarToggleButton underline;
private final ToolbarToggleButton left;
private final ToolbarToggleButton hcenter;
private final ToolbarToggleButton right;
private final ToolbarButton fcolor;
private final ToolbarButton moreCmds;
private final JPopupMenu moreCmdsMenu;
public TextToolbar(ISmartClientApplication app)
{
this(app, null);
}
public TextToolbar(ISmartClientApplication app, Map sourceActions)
{
super("text", Messages.getString("servoy.texttoolbar.label"), true); //$NON-NLS-1$ //$NON-NLS-2$
application = app;
allFontNames = new Vector();
allFonts = JFontChooser.getAvailableFonts(allFontNames);
JButton mi = null;
Action action = null;
if (sourceActions != null)
{
action = (Action)sourceActions.get("cmdspell"); //$NON-NLS-1$
if (action != null)
{
mi = new ToolbarButton(action);
add(mi);
addSeparator();
}
}
styleCombo = new JComboBox(allFontNames);
styleCombo.setPreferredSize(new Dimension(150, ToolbarButton.PREF_HEIGHT));
styleCombo.setMaximumSize(new Dimension(150, ToolbarButton.PREF_HEIGHT));
styleCombo.addActionListener(this);
styleCombo.setRequestFocusEnabled(false);
add(styleCombo);
heightCombo = new JComboBox();
heightCombo.setMaximumSize(new Dimension(50, ToolbarButton.PREF_HEIGHT));
heightCombo.setPreferredSize(new Dimension(50, ToolbarButton.PREF_HEIGHT));
heightCombo.addActionListener(this);
heightCombo.setRequestFocusEnabled(false);
add(heightCombo);
addSeparator();
bold = new ToolbarToggleButton(application.loadImage("bold.gif")); //$NON-NLS-1$
bold.setToolTipText(Messages.getString("servoy.texttoolbar.tooltip.bold")); //$NON-NLS-1$
bold.setActionCommand("bold-font"); //$NON-NLS-1$
bold.addActionListener(this);
add(bold);
italic = new ToolbarToggleButton(application.loadImage("italic.gif")); //$NON-NLS-1$
italic.setToolTipText(Messages.getString("servoy.texttoolbar.tooltip.italic")); //$NON-NLS-1$
italic.setActionCommand("italic-font"); //$NON-NLS-1$
italic.addActionListener(this);
add(italic);
underline = new ToolbarToggleButton(application.loadImage("underline.gif")); //$NON-NLS-1$
underline.setToolTipText(Messages.getString("servoy.texttoolbar.tooltip.underline")); //$NON-NLS-1$
underline.setActionCommand("underline-font"); //$NON-NLS-1$
underline.addActionListener(this);
add(underline);
addSeparator();
ButtonGroup bg = new ButtonGroup();
left = new ToolbarToggleButton(application.loadImage("left.gif")); //$NON-NLS-1$
left.setToolTipText(Messages.getString("servoy.texttoolbar.tooltip.alignleft")); //$NON-NLS-1$
left.setActionCommand("left-justify"); //$NON-NLS-1$
left.addActionListener(this);
bg.add(left);
add(left);
hcenter = new ToolbarToggleButton(application.loadImage("center.gif")); //$NON-NLS-1$
hcenter.setToolTipText(Messages.getString("servoy.texttoolbar.tooltip.aligncenter")); //$NON-NLS-1$
hcenter.setActionCommand("center-justify"); //$NON-NLS-1$
hcenter.addActionListener(this);
bg.add(hcenter);
add(hcenter);
right = new ToolbarToggleButton(application.loadImage("right.gif")); //$NON-NLS-1$
right.setToolTipText(Messages.getString("servoy.texttoolbar.tooltip.alignright")); //$NON-NLS-1$
right.setActionCommand("right-justify"); //$NON-NLS-1$
right.addActionListener(this);
bg.add(right);
add(right);
addSeparator();
fcolor = new ToolbarButton(application.loadImage("font_color.gif")); //$NON-NLS-1$
fcolor.setToolTipText(Messages.getString("servoy.texttoolbar.tooltip.fontcolor")); //$NON-NLS-1$
fcolor.addActionListener(this);
add(fcolor);
moreCmds = new ToolbarButton(application.loadImage("view_menu.gif")); //$NON-NLS-1$
moreCmds.setToolTipText(Messages.getString("servoy.texttoolbar.tooltip.more")); //$NON-NLS-1$
moreCmds.setActionCommand("more"); //$NON-NLS-1$
moreCmds.addActionListener(this);
add(moreCmds);
moreCmdsMenu = new JPopupMenu();
setEnabled(false);
JMenuItem InsertSup = new JMenuItem(Messages.getString("servoy.texttoolbar.menuitem.superscript")); //$NON-NLS-1$
InsertSup.setActionCommand("InsertSup"); //$NON-NLS-1$
InsertSup.addActionListener(this);
moreCmdsMenu.add(InsertSup);
JMenuItem InsertSub = new JMenuItem(Messages.getString("servoy.texttoolbar.menuitem.subscript")); //$NON-NLS-1$
InsertSub.setActionCommand("InsertSub"); //$NON-NLS-1$
InsertSub.addActionListener(this);
moreCmdsMenu.add(InsertSub);
JMenuItem pre = new JMenuItem(Messages.getString("servoy.texttoolbar.menuitem.preformatted")); //$NON-NLS-1$
pre.setActionCommand("InsertPre"); //$NON-NLS-1$
pre.addActionListener(this);
moreCmdsMenu.add(pre);
JMenuItem InsertOrderedList = new JMenuItem(Messages.getString("servoy.texttoolbar.menuitem.orderedlist"), app.loadImage("numbering.gif")); //$NON-NLS-1$//$NON-NLS-2$
InsertOrderedList.setActionCommand("InsertOrderedList"); //$NON-NLS-1$
InsertOrderedList.addActionListener(this);
moreCmdsMenu.add(InsertOrderedList);//
JMenuItem InsertUnorderedList = new JMenuItem(Messages.getString("servoy.texttoolbar.menuitem.unorderedlist"), app.loadImage("bulleting.gif")); //$NON-NLS-1$//$NON-NLS-2$
InsertUnorderedList.setActionCommand("InsertUnorderedList"); //$NON-NLS-1$
InsertUnorderedList.addActionListener(this);
moreCmdsMenu.add(InsertUnorderedList);//
JMenuItem InsertHR = new JMenuItem(Messages.getString("servoy.texttoolbar.menuitem.horizontalline")); //$NON-NLS-1$
InsertHR.setActionCommand("InsertHR"); //$NON-NLS-1$
InsertHR.addActionListener(this);
moreCmdsMenu.add(InsertHR);//
}
/*
* _____________________________________________________________ Methods/attribs for styled text components
*/
private boolean isAdjusting = false;
private DefaultComboBoxModel defaultFontSizes;
public void setTextComponent(JEditorPane editor)
{
setupable = null;
try
{
isAdjusting = true;
actions = new HashMap();
left.setEnabled(false);
hcenter.setEnabled(false);
right.setEnabled(false);
bold.setEnabled(false);
italic.setEnabled(false);
underline.setEnabled(false);
heightCombo.setEnabled(false);
styleCombo.setEnabled(false);
moreCmds.setEnabled(false);
fcolor.setEnabled(false);
if (editor == null) return;
moreCmds.setEnabled(editor.getEditorKit() instanceof FixedHTMLEditorKit);
if (defaultFontSizes == null)
{
defaultFontSizes = new DefaultComboBoxModel();
defaultFontSizes.addElement(new Integer(3));
defaultFontSizes.addElement(new Integer(4));
defaultFontSizes.addElement(new Integer(5));
defaultFontSizes.addElement(new Integer(6));
defaultFontSizes.addElement(new Integer(7));
defaultFontSizes.addElement(new Integer(8));
defaultFontSizes.addElement(new Integer(9));
defaultFontSizes.addElement(new Integer(10));
defaultFontSizes.addElement(new Integer(11));
defaultFontSizes.addElement(new Integer(12));
defaultFontSizes.addElement(new Integer(13));
defaultFontSizes.addElement(new Integer(14));
defaultFontSizes.addElement(new Integer(15));
defaultFontSizes.addElement(new Integer(16));
defaultFontSizes.addElement(new Integer(17));
defaultFontSizes.addElement(new Integer(18));
defaultFontSizes.addElement(new Integer(19));
defaultFontSizes.addElement(new Integer(20));
defaultFontSizes.addElement(new Integer(21));
defaultFontSizes.addElement(new Integer(22));
defaultFontSizes.addElement(new Integer(23));
defaultFontSizes.addElement(new Integer(24));
defaultFontSizes.addElement(new Integer(36));
defaultFontSizes.addElement(new Integer(48));
}
heightCombo.setModel(defaultFontSizes);
// heightCombo.setEditable(true); does not work looses focus
Action[] actionArray = editor.getActions();
for (Action element : actionArray)
{
String name = (String)element.getValue(Action.NAME);
actions.put(name, element);
if ("left-justify".equals(name)) //$NON-NLS-1$
{
left.setEnabled(true);
}
else if ("right-justify".equals(name)) //$NON-NLS-1$
{
right.setEnabled(true);
}
else if ("center-justify".equals(name)) //$NON-NLS-1$
{
hcenter.setEnabled(true);
}
else if ("font-bold".equals(name)) //$NON-NLS-1$
{
bold.setEnabled(true);
}
else if ("font-italic".equals(name)) //$NON-NLS-1$
{
italic.setEnabled(true);
}
else if ("font-underline".equals(name)) //$NON-NLS-1$
{
underline.setEnabled(true);
}
}
heightCombo.setEnabled(true);
styleCombo.setEnabled(true);
fcolor.setEnabled(true);
setSelectedFont(editor.getFont());
}
finally
{
isAdjusting = false;
}
}
public void actionRuntimePerformed(ActionEvent event)
{
Action a = null;
Object source = event.getSource();
if (source == heightCombo)
{
Object val = heightCombo.getSelectedItem();
int size = Utils.getAsInteger(val);
if (size <= 0) size = 10;
a = (Action)actions.get("font-size-" + size); //$NON-NLS-1$
if (a == null)
{
a = new StyledEditorKit.FontSizeAction("font-size-custom", size); //$NON-NLS-1$
}
}
else if (source == styleCombo)
{
a = new StyledEditorKit.FontFamilyAction("set-fontfamily", allFonts[styleCombo.getSelectedIndex()].getFamily()); //$NON-NLS-1$
}
else if (source == fcolor)
{
Window parent = SwingUtilities.getWindowAncestor(this);
if (parent == null) parent = application.getMainApplicationFrame();
CustomColorChooserDialog ccd = (CustomColorChooserDialog)application.getWindow("CustomColorChooserDialog"); //$NON-NLS-1$
if (ccd == null || ccd.getOwner() != parent)
{
if (parent instanceof Frame)
{
ccd = new CustomColorChooserDialog((Frame)parent, application);
}
else if (parent instanceof Dialog)
{
ccd = new CustomColorChooserDialog((Dialog)parent, application);
}
application.registerWindow("CustomColorChooserDialog", ccd); //$NON-NLS-1$
}
Color c = ccd.showDialog(Color.black);
if (c != null)
{
a = new ForegroundSetWithAttributeFilteringAction("set-foreground", c);//$NON-NLS-1$
}
}
else if (source == moreCmds)
{
moreCmdsMenu.show(moreCmds, 0, moreCmds.getHeight());
}
else
{
String cmd = event.getActionCommand();
if (cmd != null)
{
a = (Action)actions.get(cmd);
}
}
if (a != null)
{
a.actionPerformed(event);
}
}
/*
* _____________________________________________________________ Methods/attribs for designer
*/
private ISupportTextSetup setupable;
private ChangeListener changeListener;
private void setSelectedFont(Font f)
{
if (f == null)
{
f = UIManager.getFont("Label.font"); //$NON-NLS-1$
}
if (f == null) return;
heightCombo.setSelectedItem(new Integer(f.getSize()));
bold.setSelected((f.getStyle() & Font.BOLD) == Font.BOLD);
italic.setSelected((f.getStyle() & Font.ITALIC) == Font.ITALIC);
for (int i = 0; i < allFontNames.size(); i++)
{
String name = (String)allFontNames.elementAt(i);
if (name.equals(f.getFamily()))
{
styleCombo.setSelectedIndex(i);
break;
}
}
}
public void actionPerformed(ActionEvent event)
{
if (!isAdjusting)
{
if (setupable != null)
{
actionDesignPerformed(event);
}
else
{
actionRuntimePerformed(event);
}
}
}
public void actionDesignPerformed(ActionEvent event)
{
if (setupable != null)
{
Object source = event.getSource();
if (source == bold || source == italic || source == heightCombo || source == styleCombo)
{
int style = Font.PLAIN;
if (bold.isSelected()) style = style | Font.BOLD;
if (italic.isSelected()) style = style | Font.ITALIC;
int index = styleCombo.getSelectedIndex();
if (index > 0)
{
String name = (String)allFontNames.elementAt(index);
Integer size = (Integer)heightCombo.getSelectedItem();
if (size != null)
{
Font constructedFont = new Font(name, style, size.intValue());
if (setupable != null)
{
setupable.setFontType(PersistHelper.createFontString(constructedFont));
}
}
}
}
if (source == left && left.isSelected()) setupable.setHorizontalAlignment(SwingConstants.LEFT);
else if (source == hcenter && hcenter.isSelected()) setupable.setHorizontalAlignment(SwingConstants.CENTER);
else if (source == right && right.isSelected()) setupable.setHorizontalAlignment(SwingConstants.RIGHT);
if (changeListener != null) changeListener.stateChanged(new ChangeEvent(this));
}
}
public void setSetupable(ChangeListener cl, ISupportTextSetup s)
{
changeListener = cl;
setSetupable(s);
}
public void setSetupable(ISupportTextSetup s)
{
if (s == setupable) return;
setSetupableEx(s);
}
//we do this becouse super.setEnabled does all and we want this toolbar usable from plugins (there buttons should not enable/disable)
@Override
public void setEnabled(boolean enabled)
{
styleCombo.setEnabled(enabled);
heightCombo.setEnabled(enabled);
bold.setEnabled(enabled);
italic.setEnabled(enabled);
underline.setEnabled(enabled);
left.setEnabled(enabled);
hcenter.setEnabled(enabled);
right.setEnabled(enabled);
fcolor.setEnabled(enabled);
moreCmds.setEnabled(enabled);
moreCmdsMenu.setEnabled(enabled);
}
private void setSetupableEx(ISupportTextSetup s)
{
isAdjusting = true;
try
{
setupable = s;
setEnabled(setupable != null);
if (setupable == null) return;
moreCmds.setEnabled(false);
heightCombo.setModel(JFontChooser.createFontSizes());
underline.setEnabled(false);
fcolor.setEnabled(false);
if (setupable != null)
{
setSelectedFont(PersistHelper.createFont(setupable.getFontType()));
switch (setupable.getHorizontalAlignment())
{
case SwingConstants.LEFT :
left.setSelected(true);
break;
case SwingConstants.CENTER :
hcenter.setSelected(true);
break;
case SwingConstants.RIGHT :
right.setSelected(true);
break;
}
}
else
{
changeListener = null;
}
}
finally
{
isAdjusting = false;
}
}
public void updateFont()
{
if (isEnabled() && setupable != null)
{
setSetupableEx(setupable);
}
}
/**
* The modified version of the StyledEditorKit.ForegroundAction class from the swing text package.
* This is our private way of setting the foreground action.
* We filter out the old foreground attributes in order to overwrite them and not have two
* settings (HTML.Tag and CSS.Attribute) for the same purpose (HTML.Tag would overwrite CSS.Attribute
* which causes text no to change color after first setting of color).
*
* @author acostache
*
*/
private class ForegroundSetWithAttributeFilteringAction extends StyledTextAction
{
private final Color fg;
/**
* Creates a new ForegroundSetWithAttributeFilteringAction.
*
* @param nm the action name
* @param fg the foreground color
*/
public ForegroundSetWithAttributeFilteringAction(String nm, Color fg)
{
super(nm);
this.fg = fg;
}
/**
* Sets the foreground color.
* The main difference from StyledEditorKit.ForegroundAction is the filtering of attributes
* before the setCharacterAttributes.
*
* @param e the action event
*/
public void actionPerformed(ActionEvent e)
{
JEditorPane editor = getEditor(e);
if (editor != null)
{
if (editor.getEditorKit() instanceof FixedRTFEditorKit)
{
new StyledEditorKit.ForegroundAction("set-foreground", fg).actionPerformed(e); //$NON-NLS-1$
return;
}
Color fg = this.fg;
if ((e != null) && (e.getSource() == editor))
{
String s = e.getActionCommand();
try
{
fg = Color.decode(s);
}
catch (NumberFormatException nfe)
{
}
}
if (fg != null)
{
// remove the (old) foreground attributes, but, at the same time,
// do not lose the other (old) formatting settings.
MutableAttributeSet oldInputAttributes = getStyledEditorKit(editor).getInputAttributes();
for (Enumeration eold = oldInputAttributes.getAttributeNames(); eold.hasMoreElements();)
{
Object key = eold.nextElement();
if (key instanceof HTML.Tag && key.toString().equalsIgnoreCase("font"))
{
oldInputAttributes.removeAttribute(key);
}
if (key instanceof CSS.Attribute && key.toString().equalsIgnoreCase("color"))
{
oldInputAttributes.removeAttribute(key);
}
}
// adding the old attributes without any foreground settings to new
// foreground attributes and replacing the old attributes with the new set.
MutableAttributeSet attr = new SimpleAttributeSet();
StyleConstants.setForeground(attr, fg);
attr.addAttributes(oldInputAttributes);
setCharacterAttributes(editor, attr, true);
}
else
{
UIManager.getLookAndFeel().provideErrorFeedback(editor);
}
}
}
}
}