/* * 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. *

* 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 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 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 KeyStroke 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 KeyStroke 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 Object associated with the specified key. * * @param key a string containing the specified key * @return the binding Object stored with this key; if there * are no keys, it will return null * @see Action#getValue */ @Override public Object getValue(String key) { if (componentMap == null) { return null; } return componentMap.get(key); } /** * Sets the Value associated with the specified key. * * @param key the String that identifies the stored object * @param newValue the Object to store using this key * @see Action#putValue */ @Override public void putValue(String key, Object newValue) { if (componentMap == null) { componentMap = new HashMap(); } 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; } public JComponent createToolBarComponentByName(String componentName) { Object object = this.getValue(componentName); if (!(object instanceof AbstractButton)) { UIButton button = null; button = new UIButton(); object = initButton(button, componentName); } 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); } } }