帆软报表设计器源代码。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

673 lines
20 KiB

/*
* Copyright(c) 2001-2010, FineReport Inc, All Rights Reserved.
*/
package com.fr.design.actions;
import com.fr.base.NameStyle;
import com.fr.base.ScreenResolution;
import com.fr.base.Style;
import com.fr.base.svg.SVGIcon;
import com.fr.base.svg.IconUtils;
import com.fr.design.actions.core.ActionFactory;
import com.fr.design.constants.UIConstants;
import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.imenu.UICheckBoxMenuItem;
import com.fr.design.gui.imenu.UIMenuEastAttrItem;
import com.fr.design.gui.imenu.UIMenuItem;
import com.fr.design.gui.imenu.UIPopupEastAttrMenu;
import com.fr.design.mainframe.toolbar.UpdateActionManager;
import com.fr.design.menu.ShortCut;
import com.fr.design.selection.SelectionListener;
import com.fr.general.ComparatorUtils;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import com.fr.stable.pinyin.PinyinFormat;
import com.fr.stable.pinyin.PinyinHelper;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ComponentEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* An UpdateAction functions allows actions to have an efficient transient state,
* it's update method is called every time the action is about to be displayed
* in UI - allowing the action to poll whatever information it wishes to update its state
* before being displayed
* As a regular Swing Action object - it stores the name, icon, and enabled state for a single action.
* <p/>
* For example, a 'cut' action my be enabled or disabled based on the state of the selection
* in a text component. Just before the edit menu is displayed, the cut action is given the
* opportunity to update itself, and will appear in the menu with the correct state.
* august:不需要考虑UpdateAction的持久化,难道需要持久化吗? 又不像以前那样保存docking的大小布局
* 如果是ToggleButton,就额外继承ToggleButtonUpdateAction接口
*/
public abstract class UpdateAction extends ShortCut implements Action {
/**
* Specifies whether action is enabled; the default is true.
*/
private boolean enabled = true;
/**
* Contains the array of key bindings.
* august:关键词key,是Action里面的final常量,如:Action.NAME、Action.SMALL_ICON等等
*/
private Map<String, Object> componentMap;
private String searchText = StringUtils.EMPTY;
/**
* 按钮在按压状态的图标key
*/
public static final String PRESSED_ICON = "pressedIcon";
/**
* 按钮在灰化状态的图标key
*/
public static final String DISABLED_ICON = "disabledIcon";
/**
* Constructor
*/
public UpdateAction() {
//设置默认的small icon,必须有一个默认的图片,这样菜单整体美观.
this.putValue(Action.SMALL_ICON, UIConstants.BLACK_ICON);
}
/**
* Returns true if the action is enabled.
*
* @return true if the action is enabled, false otherwise
* @see Action#isEnabled
*/
@Override
public boolean isEnabled() {
return enabled;
}
/**
* Enables or disables the action.
*
* @param newValue true to enable the action, false to
* disable it
* @see Action#setEnabled
*/
@Override
public void setEnabled(boolean newValue) {
boolean oldValue = this.enabled;
if (oldValue != newValue) {
this.enabled = newValue;
//循环遍历所有的Enable属性.
Iterator<Object> valueIt = this.componentMap.values().iterator();
while (valueIt.hasNext()) {
Object valueObject = valueIt.next();
if (valueObject instanceof JComponent) {
((JComponent) valueObject).setEnabled(this.enabled);
}
}
}
}
/**
* Returns the name for the action, used for a menu or button.
*
* @return the name for the action.
*/
public String getName() {
return (String) this.getValue(Action.NAME);
}
/**
* Sets the name for the action, used for a menu or button.
*
* @param name the name for the action.
*/
public void setName(String name) {
this.putValue(Action.NAME, name);
}
/**
* Returns the smallIcon property setting.
*
* @return The small icon for the action.
*/
public Icon getSmallIcon() {
return (Icon) this.getValue(Action.SMALL_ICON);
}
/**
* Sets the smallIcon property. The smallIcon may be displayed as a menu item's icon or
* possibly as the icon for a button. This icon should be 16x16 pixels in size.
*
* @param smallIcon The small icon for the action.
*/
public void setSmallIcon(Icon smallIcon) {
this.putValue(Action.SMALL_ICON, smallIcon);
}
/**
* 使用传入资源url的方式设置Icon,可以自动设置对应的"_normal.svg"、"_disabled.svg"
* 但是要保证文件名的统一,例如a.png对应a_normal.svg、a_disabled.svg
* 如果a_disabled.svg或者a_pressed.svg缺失,则不会被设置成灰化状态或者按压状态图标
* 如果a_normal.svg缺失,则会读到a.png,这也就不是一个SVGIcon了
* 如果连 a.png 也不存在,那么这个action的图标就是空白的
*
* 如果想让这个Action没有图标,可以传入""
* @param resource 图标资源路径
*/
public void setSmallIcon(String resource) {
setSmallIcon(resource, true);
}
/**
* 使用传入资源url的方式设置Icon,会自动设置_normal.svg,然后通过needDisable参数判断是否需要自动设置_disable.svg
* 因为有些地方是不需要设置灰化图标的,所以不存在灰化图标资源,这边如果一并设置,就会报错文件找不到
* @param resource
* @param needDisable
*/
public void setSmallIcon(String resource, boolean needDisable) {
if (StringUtils.equals(resource, StringUtils.EMPTY)) {
this.putValue(Action.SMALL_ICON, null);
return;
}
this.putValue(Action.SMALL_ICON, IconUtils.readIcon(resource));
if (needDisable) {
this.putValue(UpdateAction.DISABLED_ICON, IconUtils.readSVGIcon(resource, IconUtils.ICON_TYPE_DISABLED));
}
}
public void setSmallIcon(Icon[] smallIcon, boolean white) {
this.putValue(Action.SMALL_ICON, smallIcon);
}
/**
* Returns the mnemonic property setting.
*
* @return The mnemonic character for the action.
*/
public char getMnemonic() {
Integer n = (Integer) this.getValue(Action.MNEMONIC_KEY);
return n == null ? '\0' : (char) n.intValue();
}
/**
* Sets the mnemonic property. The mnemonic character is the 'hotkey' for the menu item
* or button displaying the action. This *must* be a character in the 'name' property,
* or it will have no effect.
*
* @param mnemonic The mnemonic character for the action
*/
public void setMnemonic(char mnemonic) {
this.putValue(Action.MNEMONIC_KEY, new Integer(mnemonic));
}
/**
* Returns the key used for storing a <code>KeyStroke</code> to be used as the
* accelerator for the action.
*
* @return the key as the accelerator for the action.
*/
public KeyStroke getAccelerator() {
return (KeyStroke) this.getValue(Action.ACCELERATOR_KEY);
}
/**
* Sets the key used for storing a <code>KeyStroke</code> to be used as the
* accelerator for the action.
*
* @param accelerator the key as the accelerator for the action.
*/
public void setAccelerator(KeyStroke accelerator) {
this.putValue(Action.ACCELERATOR_KEY, accelerator);
}
/**
* update enable
*/
public void update() {
}
/**
* Gets the <code>Object</code> associated with the specified key.
*
* @param key a string containing the specified <code>key</code>
* @return the binding <code>Object</code> stored with this key; if there
* are no keys, it will return <code>null</code>
* @see Action#getValue
*/
@Override
public Object getValue(String key) {
if (componentMap == null) {
return null;
}
return componentMap.get(key);
}
/**
* Sets the <code>Value</code> associated with the specified key.
*
* @param key the <code>String</code> that identifies the stored object
* @param newValue the <code>Object</code> to store using this key
* @see Action#putValue
*/
@Override
public void putValue(String key, Object newValue) {
if (componentMap == null) {
componentMap = new HashMap<String, Object>();
}
if (newValue == null) {
componentMap.remove(key);
} else {
componentMap.put(key, newValue);
}
}
public void setPressedIcon(Icon pressedIcon) {
this.putValue(UpdateAction.PRESSED_ICON, pressedIcon);
}
public void setDisabledIcon(Icon disabledIcon) {
this.putValue(UpdateAction.DISABLED_ICON, disabledIcon);
}
@Override
public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
}
@Override
public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
}
/**
* Gets menu item.
*
* @return the created menu item.
*/
public UIMenuItem createMenuItem() {
Object object = this.getValue(UIMenuItem.class.getName());
if (object == null && !(object instanceof UIMenuItem)) {
UIMenuItem menuItem = new UIMenuItem(this);
// 设置名字用作单元测
menuItem.setName(getName());
setPressedIcon4Button(menuItem);
setDisabledIcon4Button(menuItem);
object = menuItem;
this.putValue(UIMenuItem.class.getName(), object);
}
return (UIMenuItem) object;
}
public UIMenuEastAttrItem createMenuItemEastAttr() {
UIMenuEastAttrItem menuItem = new UIMenuEastAttrItem(this);
// 设置名字用作单元测
menuItem.setName(getName());
setPressedIcon4Button(menuItem);
setDisabledIcon4Button(menuItem);
this.putValue(UIMenuItem.class.getName(), menuItem);
return menuItem;
}
/**
* Gets component on toolbar.
*
* @return the created components on toolbar.
*/
public JComponent createToolBarComponent() {
Object object = this.getValue(UIButton.class.getName());
if (!(object instanceof AbstractButton)) {
UIButton button = null;
button = new UIButton();
object = initButton(button, UIButton.class.getName());
}
return (JComponent) object;
}
protected JComponent initButton(UIButton button, String name) {
// 添加一个名字作为自动化测试用
button.setName(getName());
button.set4ToolbarButton();
//设置属性.
Integer mnemonicInteger = (Integer) this.getValue(Action.MNEMONIC_KEY);
if (mnemonicInteger != null) {
button.setMnemonic((char) mnemonicInteger.intValue());
}
button.setIcon((Icon) this.getValue(Action.SMALL_ICON));
setPressedIcon4Button(button);
setDisabledIcon4Button(button);
button.addActionListener(this);
button.registerKeyboardAction(this, this.getAccelerator(), JComponent.WHEN_IN_FOCUSED_WINDOW);
this.putValue(name, button);
button.setText(StringUtils.EMPTY);
button.setEnabled(this.isEnabled());
//peter:产生tooltip
button.setToolTipText(getToolTipText());
return button;
}
/**
* 重写此方法,可以自定义 action 的提示文字
*/
protected String getToolTipText() {
return ActionFactory.createButtonToolTipText(this);
}
/**
* Equals
*/
@Override
public boolean equals(Object object) {
if (object == this) {
return true;
}
if (!(object instanceof UpdateAction)) {
return false;
}
return ComparatorUtils.equals(((UpdateAction) object).getName(), this.getName());
}
@Override
public int hashCode() {
int hash = 5;
hash = 59 * hash + (this.enabled ? 1 : 0);
return hash;
}
/*
* Add this ShortCut into JPopupMenu
*/
@Override
public void intoJPopupMenu(JPopupMenu menu) {
update();
if (menu instanceof UIPopupEastAttrMenu){
menu.add(this.createMenuItemEastAttr());
return;
}
menu.add(this.createMenuItem());
}
/*
* Add this ShortCut into JToolBar
*/
@Override
public void intoJToolBar(JToolBar toolBar) {
update();
toolBar.add(this.createToolBarComponent());
}
public abstract static class ComponentRemoveEvent extends ComponentEvent {
private static int EVENT_DELETE= 3001;
public ComponentRemoveEvent(Component source) {
super(source, EVENT_DELETE);
}
public abstract void release(SelectionListener listener);
}
/**
* 全局style的菜单
*/
public static class UseMenuItem extends UIMenuItem {
private NameStyle nameStyle;
private SelectionListener listener;
public UseMenuItem(Action action) {
super(action);
setPreferredSize(new Dimension(80, 20));
}
public UseMenuItem(String text, Icon icon) {
super(text, icon);
}
protected void processEvent(AWTEvent e) {
if (e instanceof ComponentRemoveEvent) {
((ComponentRemoveEvent) e).release(listener);
return;
}
super.processEvent(e);
}
public void setSelectionListener(SelectionListener listener) {
this.listener = listener;
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (nameStyle == null) {
return;
}
Style.paintBackground((Graphics2D) g, nameStyle, getWidth() - 1, getHeight() - 1);
Style.paintContent((Graphics2D) g, nameStyle.getName(), nameStyle, getWidth() - 1, getHeight() - 1, ScreenResolution.getScreenResolution());
Style.paintBorder((Graphics2D) g, nameStyle, getWidth() - 1, getHeight() - 1);
}
public NameStyle getNameStyle() {
return this.nameStyle;
}
public void setNameStyle(NameStyle nameStyle) {
this.nameStyle = nameStyle;
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
}
/**
* Gets menu item.
*
* @return the created menu item.
*/
public UseMenuItem createUseMenuItem() {
Object object = this.getValue(UseMenuItem.class.getName());
if (object == null && !(object instanceof UseMenuItem)) {
object = new UseMenuItem(this);
this.putValue(UseMenuItem.class.getName(), object);
}
setPressedIcon4Button((UseMenuItem) object);
setDisabledIcon4Button((UseMenuItem) object);
return (UseMenuItem) object;
}
public static UICheckBoxMenuItem createCheckBoxMenuItem(UpdateAction action) {
UICheckBoxMenuItem menuItem = new UICheckBoxMenuItem(action.getName());
// 设置属性.
Integer mnemonicInteger = (Integer) action
.getValue(Action.MNEMONIC_KEY);
if (mnemonicInteger != null) {
menuItem.setMnemonic((char) mnemonicInteger.intValue());
}
menuItem.setIcon((Icon) action.getValue(Action.SMALL_ICON));
// if(menuItem.isSelected()){
// menuItem.setIcon(FRSwingConstants.YES_ICON);
// }
menuItem.addActionListener(action);
menuItem.setToolTipText((String) action.getValue(Action.LONG_DESCRIPTION));
menuItem.setAccelerator((KeyStroke) action.getValue(Action.ACCELERATOR_KEY));
return menuItem;
}
public void generateAndSetSearchText(String paneClass) {
UpdateActionManager.getUpdateActionManager().dealWithSearchText(paneClass, this);
}
public void setSearchText(String text) {
this.searchText = text;
}
/**
* 获取搜索匹配字符串
* @return
*/
public String getSearchText() {
return searchText;
}
/**
* 遍历面板中所有控件,获取text用于alphafine的action搜索,考虑分词,拼音,首字母检索
* @param panel
* @param separator
* @param text
* @param pinyin
* @param shortPinyin
* @return
*/
public String getComponentTexts(JPanel panel, String separator, StringBuffer text, StringBuffer pinyin, StringBuffer shortPinyin) {
Border border = panel.getBorder();
if (border instanceof TitledBorder) {
String title = ((TitledBorder) border).getTitle();
text.append(title).append(separator);
pinyin.append(PinyinHelper.convertToPinyinString(title, "", PinyinFormat.WITHOUT_TONE)).append(separator);
shortPinyin.append(PinyinHelper.getShortPinyin(title)).append(separator);
}
Component[] components = panel.getComponents();
for (Component component : components) {
if (component instanceof JPanel) {
getComponentTexts((JPanel) component, separator, text, pinyin, shortPinyin);
} else if (component instanceof JScrollPane) {
Component childComponent = ((JScrollPane) component).getViewport().getView();
if (childComponent instanceof JPanel) {
getComponentTexts((JPanel) childComponent, separator, text, pinyin, shortPinyin);
}
} else if (component instanceof JLabel) {
String title = ((JLabel) component).getText();
handleSearchText(separator, text, pinyin, shortPinyin, title);
} else if (component instanceof JCheckBox) {
String title = ((JCheckBox) component).getText();
handleSearchText(separator, text, pinyin, shortPinyin, title);
} else if (component instanceof JButton) {
String title = ((JButton) component).getText();
handleSearchText(separator, text, pinyin, shortPinyin, title);
} else if (component instanceof JRadioButton) {
String title = ((JRadioButton) component).getText();
handleSearchText(separator, text, pinyin, shortPinyin, title);
} else if (component instanceof JComboBox) {
for (int i = 0; i < ((JComboBox) component).getItemCount(); i++) {
Object componentName = ((JComboBox) component).getItemAt(i);
if (componentName instanceof String && StringUtils.isNotBlank(String.valueOf(componentName))) {
String title = String.valueOf(componentName);
text.append(title);
handleSearchText(separator, text, pinyin, shortPinyin, title);
}
}
} else if (component instanceof JTabbedPane) {
getTabPaneTexts((JTabbedPane) component, separator, text, pinyin, shortPinyin);
}
}
return String.valueOf(text.append(pinyin).append(shortPinyin));
}
/**
* 递归遍历tabbedPane
* @param component
* @param separator
* @param text
* @param pinyin
* @param shortPinyin
*/
private synchronized void getTabPaneTexts(JTabbedPane component, String separator, StringBuffer text, StringBuffer pinyin, StringBuffer shortPinyin) {
for (int i = 0; i < component.getTabCount(); i++) {
String title = component.getTitleAt(i);
handleSearchText(separator, text, pinyin, shortPinyin, title);
Component tabComponent = null;
try {
tabComponent = component.getComponentAt(i);
} catch (Exception ignore) {
FineLoggerFactory.getLogger().info("AlphaFine index tabPane end");
}
if (tabComponent instanceof JPanel) {
getComponentTexts((JPanel) tabComponent, separator, text, pinyin, shortPinyin);
} else if (tabComponent instanceof JTabbedPane) {
getTabPaneTexts((JTabbedPane) tabComponent, separator, text, pinyin, shortPinyin);
}
}
}
/**
* 将text,pinyin,pinyin首字母拼接到一起
* @param separator
* @param text
* @param pinyin
* @param shortPinyin
* @param title
*/
private void handleSearchText(String separator, StringBuffer text, StringBuffer pinyin, StringBuffer shortPinyin, String title) {
if (StringUtils.isBlank(title)) {
return;
}
text.append(title).append(separator);
pinyin.append(PinyinHelper.convertToPinyinString(title, "", PinyinFormat.WITHOUT_TONE));
shortPinyin.append(PinyinHelper.getShortPinyin(title)).append(separator);
}
protected void setPressedIcon4Button(AbstractButton button) {
Icon pressedIcon = (Icon) this.getValue(UpdateAction.PRESSED_ICON);
if (pressedIcon != null && pressedIcon instanceof SVGIcon) {
button.setPressedIcon(pressedIcon);
}
}
protected void setDisabledIcon4Button(AbstractButton button) {
Icon disabledIcon = (Icon) this.getValue(UpdateAction.DISABLED_ICON);
if (disabledIcon != null && disabledIcon instanceof SVGIcon) {
button.setDisabledIcon(disabledIcon);
}
}
}