// Near Infinity - An Infinity Engine Browser and Editor // Copyright (C) 2001 - 2005 Jon Olav Hauglid // See LICENSE.txt for license information package org.infinity.resource.chu; import java.awt.AlphaComposite; import java.awt.CardLayout; import java.awt.Color; import java.awt.Composite; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Locale; import javax.swing.AbstractListModel; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.ListSelectionModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import org.infinity.datatype.Bitmap; import org.infinity.datatype.ColorPicker; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.StringRef; import org.infinity.datatype.TextString; import org.infinity.gui.RenderCanvas; import org.infinity.gui.StructViewer; import org.infinity.gui.ViewerUtil; import org.infinity.resource.Profile; import org.infinity.resource.ResourceFactory; import org.infinity.resource.graphics.BamDecoder; import org.infinity.resource.graphics.MosDecoder; import org.infinity.resource.graphics.MosV1Decoder; import org.infinity.resource.graphics.BamDecoder.BamControl; import org.infinity.resource.graphics.BamDecoder.FrameEntry; import org.infinity.resource.graphics.BamV1Decoder.BamV1Control; import org.infinity.util.StringResource; final class Viewer extends JPanel implements ActionListener, TableModelListener, ListSelectionListener, ChangeListener, MouseListener { /** Supported control types. */ public enum ControlType { UNKNOWN, BUTTON, SLIDER, TEXT_FIELD, TEXT_AREA, LABEL, SCROLL_BAR } private final ChuResource chu; private RenderCanvas rcMain; private ListPanelsModel panelsModel; private ListControlsModel controlsModel; private JList<Panel> panelsList; private JList<BaseControl> controlsList; private JCheckBox cbTransparentPanel, cbOutlineControls; private PropertiesPanel pProperties; /** Converts a control id into a ControlType enum. */ public static ControlType getControlType(int type) { switch (type) { case 0: return ControlType.BUTTON; case 2: return ControlType.SLIDER; case 3: return ControlType.TEXT_FIELD; case 5: return ControlType.TEXT_AREA; case 6: return ControlType.LABEL; case 7: return ControlType.SCROLL_BAR; default: return ControlType.UNKNOWN; } } /** Converts a ControlType enum into a control id. */ public static int getControlId(ControlType type) { switch (type) { case BUTTON: return 0; case SLIDER: return 2; case TEXT_FIELD: return 3; case TEXT_AREA: return 5; case LABEL: return 6; case SCROLL_BAR: return 7; default: return -1; } } /** Returns a control object of the given type. */ public static BaseControl createControl(Viewer viewer, Control control) { ControlType type = (control != null) ? getControlType(control.getControlType()) : ControlType.UNKNOWN; switch (type) { case BUTTON: return new ButtonControl(viewer, control); case SLIDER: return new SliderControl(viewer, control); case TEXT_FIELD: return new TextFieldControl(viewer, control); case TEXT_AREA: return new TextAreaControl(viewer, control); case LABEL: return new LabelControl(viewer, control); case SCROLL_BAR: return new ScrollBarControl(viewer, control); default: return new UnknownControl(viewer, control); } } public Viewer(ChuResource chu) { this.chu = chu; this.chu.addTableModelListener(this); initControls(); } //--------------------- Begin Interface ActionListener --------------------- @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == cbOutlineControls) { // updating main display Panel p = getSelectedPanel(); p.repaint(); rcMain.repaint(); } else if (e.getSource() == cbTransparentPanel) { // updating main display Panel p = getSelectedPanel(); p.repaint(); rcMain.repaint(); } } //--------------------- End Interface ActionListener --------------------- //--------------------- Begin Interface TableModelListener --------------------- @Override public void tableChanged(TableModelEvent e) { if (e.getSource() == getResource()) { // updating viewer elements Panel p = getSelectedPanel(); p.reset(); setPreview(p.getImage()); } } //--------------------- End Interface TableModelListener --------------------- //--------------------- Begin Interface ListSelectionListener --------------------- @Override public void valueChanged(ListSelectionEvent e) { if (e.getSource() == panelsList) { Panel p = getSelectedPanel(); if (p != null) { // updating controls list controlsModel.setResource(p.getResource()); controlsList.setSelectedIndex(0); // updating main window p.repaint(); setPreview(p.getImage()); } } else if (e.getSource() == controlsList) { BaseControl c = (BaseControl)controlsList.getSelectedValue(); if (c != null) { // updating properties panel getProperties().updateProperties(c); // updating selected control in main window Panel p = getSelectedPanel(); p.repaint(); rcMain.repaint(); } } } //--------------------- End Interface ListSelectionListener --------------------- //--------------------- Begin Interface ChangeListener --------------------- @Override public void stateChanged(ChangeEvent e) { if (e.getSource() == pProperties) { // updating main display to reflect changes in control properties Panel p = getSelectedPanel(); BaseControl c = getSelectedControl(); if (c != null) { c.updateState(); c.updateImage(); } p.repaint(); rcMain.repaint(); } } //--------------------- End Interface ChangeListener --------------------- //--------------------- Begin Interface MouseListener --------------------- @Override public void mouseClicked(MouseEvent e) { if (e.getSource() == panelsList) { if (e.getClickCount() == 2 && !e.isConsumed()) { Panel p = (Panel)panelsList.getSelectedValue(); if (p != null) { StructViewer v = getResource().getViewer(); if (v != null) { v.getViewFrame(p.getResource()); } } } } else if (e.getSource() == controlsList) { if (e.getClickCount() == 2 && !e.isConsumed()) { Panel p = (Panel)panelsList.getSelectedValue(); if (p != null) { BaseControl c = (BaseControl)controlsList.getSelectedValue(); if (c != null && !c.isEmpty()) { StructViewer v = getResource().getViewer(); if (v != null) { v.getViewFrame(p.getResource()); v.getViewFrame(c.getResource()); } } } } } } @Override public void mousePressed(MouseEvent e) { } @Override public void mouseReleased(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } //--------------------- End Interface MouseListener --------------------- /** Returns the associated ChuResource instance. */ public ChuResource getResource() { return chu; } /** Returns whether to make the panel background transparent if no bg image is available. */ boolean isTransparentBackground() { return cbTransparentPanel.isSelected(); } /** Returns the currently active panel. */ Panel getSelectedPanel() { Object o = panelsModel.getElementAt(panelsList.getSelectedIndex()); if (o instanceof Panel) { return (Panel)o; } else { return null; } } /** Returns the currently active control. */ BaseControl getSelectedControl() { Object o = controlsModel.getElementAt(controlsList.getSelectedIndex()); if (o instanceof BaseControl) { return (BaseControl)o; } else { return null; } } /** Returns the current properties panel instance. */ PropertiesPanel getProperties() { return pProperties; } /** Returns the controls model. */ ListControlsModel getControls() { return controlsModel; } /** Returns whether the specified control is currently selected. */ boolean isControlSelected(BaseControl control) { return (control == controlsModel.getElementAt(controlsList.getSelectedIndex())); } /** Returns whether to draw boxes around controls. */ boolean isControlOutlined() { return cbOutlineControls.isSelected(); } // Update preview image private void setPreview(Image image) { rcMain.setImage(image); rcMain.revalidate(); } private void initControls() { setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); // creating main panel JPanel pMain = new JPanel(new GridBagLayout()); JScrollPane spMain = new JScrollPane(pMain); spMain.getHorizontalScrollBar().setUnitIncrement(16); spMain.getVerticalScrollBar().setUnitIncrement(16); rcMain = new RenderCanvas(); rcMain.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY)); gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0); pMain.add(rcMain, gbc); // creating side panel JPanel pSideBar = new JPanel(new GridBagLayout()); JLabel lPanels = new JLabel("Panels:"); panelsModel = new ListPanelsModel(this); panelsList = new JList<>(panelsModel); panelsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); panelsList.addListSelectionListener(this); panelsList.addMouseListener(this); JScrollPane spPanels = new JScrollPane(panelsList); spPanels.getVerticalScrollBar().setUnitIncrement(16); cbTransparentPanel = new JCheckBox("Transparent background", true); cbTransparentPanel.addActionListener(this); JLabel lControls = new JLabel("Controls:"); controlsModel = new ListControlsModel(this, null); // assigning resource later controlsList = new JList<>(controlsModel); controlsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); controlsList.addListSelectionListener(this); controlsList.addMouseListener(this); JScrollPane spControls = new JScrollPane(controlsList); spControls.getVerticalScrollBar().setUnitIncrement(16); cbOutlineControls = new JCheckBox("Outline selection", true); cbOutlineControls.addActionListener(this); pProperties = new PropertiesPanel(); pProperties.addChangeListener(this); gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 8, 0, 8), 0, 0); pSideBar.add(lPanels, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 1, 1, 1, 1.0, 0.75, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(4, 8, 0, 8), 0, 0); pSideBar.add(spPanels, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 8, 0, 8), 0, 0); pSideBar.add(cbTransparentPanel, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 3, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(16, 8, 0, 8), 0, 0); pSideBar.add(lControls, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 4, 1, 1, 1.0, 1.25, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(4, 8, 0, 8), 0, 0); pSideBar.add(spControls, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 5, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 8, 0, 8), 0, 0); pSideBar.add(cbOutlineControls, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 6, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(16, 8, 0, 8), 0, 0); pSideBar.add(pProperties, gbc); // putting all together gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(8, 8, 8, 0), 0, 0); add(spMain, gbc); gbc = ViewerUtil.setGBC(gbc, 1, 0, 1, 1, 0.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(8, 8, 8, 8), 0, 0); add(pSideBar, gbc); // selecting first available panel if (panelsModel.getSize() > 0) { panelsList.setSelectedIndex(0); } } //----------------------------- INNER CLASSES ----------------------------- // Data model for the panels list private class ListPanelsModel extends AbstractListModel<Panel> { private final List<Panel> listPanels = new ArrayList<Panel>(); private final Viewer viewer; public ListPanelsModel(Viewer viewer) { super(); this.viewer = viewer; for (int i = 0; i < getResource().getPanelCount(); i++) { listPanels.add(new Panel(getViewer(), getResource().getPanel(i))); } } //--------------------- Begin Interface ListModel --------------------- @Override public int getSize() { return listPanels.size(); } @Override public Panel getElementAt(int index) { if (index >= 0 && index < listPanels.size()) { return listPanels.get(index); } else { return null; } } //--------------------- End Interface ListModel --------------------- public Viewer getViewer() { return viewer; } public ChuResource getResource() { return getViewer().getResource(); } } // Data model for the controls list private class ListControlsModel extends AbstractListModel<BaseControl> { private final List<BaseControl> listControls = new ArrayList<BaseControl>(); private final Viewer viewer; private Window panel; public ListControlsModel(Viewer viewer, Window panel) { super(); this.viewer = viewer; setResource(panel); } //--------------------- Begin Interface ListModel --------------------- @Override public int getSize() { return listControls.size(); } @Override public BaseControl getElementAt(int index) { return getEntry(index); } //--------------------- End Interface ListModel --------------------- public Viewer getViewer() { return viewer; } /** Apply new Window object and reset model data. */ public void setResource(Window panel) { if (panel != this.panel) { int oldSize = getSize(); clearList(); this.panel = panel; // populating list with available controls for (int i = 0; i < getResource().getControlCount(); i++) { listControls.add(createControl(getViewer(), getResource().getControl(i))); } // notifying changed items int numChanged = Math.min(oldSize, getSize()); if (numChanged > 1) { fireContentsChanged(this, 1, numChanged-1); } // notifying added/removed items int numAdded = getSize() - oldSize; if (numAdded > 0) { fireIntervalAdded(this, numChanged, numChanged+numAdded-1); } else if (numAdded < 0) { numAdded = -numAdded; fireIntervalRemoved(this, numChanged, numChanged+numAdded-1); } } } /** Return currently assigned Window object. */ public Window getResource() { return panel; } // use cache to return given control private BaseControl getEntry(int index) { if (index >= 0 && index < getSize()) { return listControls.get(index); } else { return null; } } private void clearList() { BaseControl empty; if (listControls.isEmpty()) { empty = new UnknownControl(getViewer(), null); } else { empty = listControls.get(0); } listControls.clear(); listControls.add(empty); // first entry is special } } // Manages the Control Properties panel private static class PropertiesPanel extends JPanel implements ActionListener { // Format strings used to display common properties of a control private static final String FMT_POSITION = "X: %1$d, Y: %2$d"; private static final String FMT_SIZE = "W: %1$d, H: %2$d"; private final List<ChangeListener> listeners = new ArrayList<ChangeListener>(); private final JRadioButton[] rbButtonState = new JRadioButton[4]; private JLabel lPosition, lSize; private JPanel pControl; private CardLayout clControl; private JCheckBox cbVisible, cbSliderGrabbed, cbTextFieldCaret, cbScrollBarUpState, cbScrollBarDownState; public PropertiesPanel() { super(); init(); } //--------------------- Begin Interface ActionListener --------------------- @Override public void actionPerformed(ActionEvent e) { fireStateChanged(); } //--------------------- End Interface ActionListener --------------------- public void addChangeListener(ChangeListener l) { if (l != null) { if (listeners.indexOf(l) < 0) { listeners.add(l); } } } // public void removeChangeListener(ChangeListener l) // { // if (l != null) { // int idx = listeners.indexOf(l); // if (idx >= 0) { // listeners.remove(idx); // } // } // } /** Display the properties panel for the given control type. */ public void showPanel(ControlType type) { switch (type) { case BUTTON: case SLIDER: case TEXT_FIELD: case TEXT_AREA: case LABEL: case SCROLL_BAR: cbVisible.setEnabled(true); clControl.show(pControl, type.name()); break; default: cbVisible.setEnabled(false); clControl.show(pControl, ControlType.UNKNOWN.name()); } } public void updateProperties(BaseControl control) { if (control != null && control.getResource() != null) { // setting common fields lPosition.setText(String.format(FMT_POSITION, control.getPosition().x, control.getPosition().y)); lSize.setText(String.format(FMT_SIZE, control.getDimension().width, control.getDimension().height)); setControlVisible(control.isVisible()); showPanel(control.getType()); // setting specialized fields if (control instanceof ButtonControl) { ButtonControl c = (ButtonControl)control; if (c.isUnpressed()) { setButtonUnpressed(); } else if (c.isPressed()) { setButtonPressed(); } else if (c.isSelected()) { setButtonSelected(); } else if (c.isDisabled()) { setButtonDisabled(); } } else if (control instanceof SliderControl) { SliderControl c = (SliderControl)control; setSliderGrabbed(c.isGrabbed()); } else if (control instanceof TextFieldControl) { TextFieldControl c = (TextFieldControl)control; setTextFieldCaretEnabled(c.isCaretEnabled()); } else if (control instanceof ScrollBarControl) { ScrollBarControl c = (ScrollBarControl)control; setScrollBarUpArrowPressed(c.isUpArrowPressed()); setScrollBarDownArrowPressed(c.isDownArrowPressed()); } } else { lPosition.setText(""); lSize.setText(""); showPanel(ControlType.UNKNOWN); } } public void setControlVisible(boolean b) { cbVisible.setSelected(b); } public boolean isControlVisible() { return cbVisible.isSelected(); } public void setButtonUnpressed() { rbButtonState[0].setSelected(true); } public void setButtonPressed() { rbButtonState[1].setSelected(true); } public void setButtonSelected() { rbButtonState[2].setSelected(true); } public void setButtonDisabled() { rbButtonState[3].setSelected(true); } public boolean isButtonUnpressed() { return rbButtonState[0].isSelected(); } public boolean isButtonPressed() { return rbButtonState[1].isSelected(); } public boolean isButtonSelected() { return rbButtonState[2].isSelected(); } public boolean isButtonDisabled() { return rbButtonState[3].isSelected(); } public void setSliderGrabbed(boolean b) { cbSliderGrabbed.setSelected(b); } public boolean isSliderGrabbed() { return cbSliderGrabbed.isSelected(); } public void setTextFieldCaretEnabled(boolean b) { cbTextFieldCaret.setSelected(b); } public boolean isTextFieldCaretEnabled() { return cbTextFieldCaret.isSelected(); } public void setScrollBarUpArrowPressed(boolean b) { cbScrollBarUpState.setSelected(b); } public void setScrollBarDownArrowPressed(boolean b) { cbScrollBarDownState.setSelected(b); } public boolean isScrollBarUpArrowPressed() { return cbScrollBarUpState.isSelected(); } public boolean isScrollBarDownArrowPressed() { return cbScrollBarDownState.isSelected(); } private void fireStateChanged() { ChangeEvent e = new ChangeEvent(this); for (int i = listeners.size() - 1; i >= 0; i--) { listeners.get(i).stateChanged(e); } } private void init() { setLayout(new GridBagLayout()); setBorder(BorderFactory.createTitledBorder("Control Properties ")); GridBagConstraints gbc = new GridBagConstraints(); // constructing panel commonly used by all control types JPanel pCommon = new JPanel(new GridBagLayout()); JLabel lPosTitle = new JLabel("Position:"); JLabel lSizeTitle = new JLabel("Size:"); lPosition = new JLabel("X: 123, Y: 456"); lSize = new JLabel("W: 64, H: 28"); cbVisible = new JCheckBox("Control visible", true); cbVisible.addActionListener(this); gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pCommon.add(lPosTitle, gbc); gbc = ViewerUtil.setGBC(gbc, 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 8, 0, 0), 0, 0); pCommon.add(lPosition, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0); pCommon.add(lSizeTitle, gbc); gbc = ViewerUtil.setGBC(gbc, 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 8, 0, 0), 0, 0); pCommon.add(lSize, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 2, 2, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0); pCommon.add(cbVisible, gbc); // constructing specific control panels clControl = new CardLayout(); pControl = new JPanel(clControl); // empty panel pControl.add(new JPanel(), ControlType.UNKNOWN.name()); // button panel JPanel pButton = new JPanel(new GridBagLayout()); JLabel lButtonState = new JLabel("Button state:"); ButtonGroup bg = new ButtonGroup(); rbButtonState[0] = new JRadioButton("Unpressed", true); rbButtonState[1] = new JRadioButton("Pressed", false); rbButtonState[2] = new JRadioButton("Selected", false); rbButtonState[3] = new JRadioButton("Disabled", false); bg.add(rbButtonState[0]); bg.add(rbButtonState[1]); bg.add(rbButtonState[2]); bg.add(rbButtonState[3]); rbButtonState[0].addActionListener(this); rbButtonState[1].addActionListener(this); rbButtonState[2].addActionListener(this); rbButtonState[3].addActionListener(this); gbc = ViewerUtil.setGBC(gbc, 0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pButton.add(lButtonState, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pButton.add(rbButtonState[0], gbc); gbc = ViewerUtil.setGBC(gbc, 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pButton.add(rbButtonState[1], gbc); gbc = ViewerUtil.setGBC(gbc, 0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pButton.add(rbButtonState[2], gbc); gbc = ViewerUtil.setGBC(gbc, 1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pButton.add(rbButtonState[3], gbc); pControl.add(pButton, ControlType.BUTTON.name()); // slider panel JPanel pSlider = new JPanel(new GridBagLayout()); cbSliderGrabbed = new JCheckBox("Slider grabbed", false); cbSliderGrabbed.addActionListener(this); gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pSlider.add(cbSliderGrabbed, gbc); pControl.add(pSlider, ControlType.SLIDER.name()); // text field panel JPanel pTextField = new JPanel(new GridBagLayout()); cbTextFieldCaret = new JCheckBox("Show caret", false); cbTextFieldCaret.addActionListener(this); gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pTextField.add(cbTextFieldCaret, gbc); pControl.add(pTextField, ControlType.TEXT_FIELD.name()); // text area panel (nothing to do) pControl.add(new JPanel(), ControlType.TEXT_AREA.name()); // label panel (nothing to do) pControl.add(new JPanel(), ControlType.LABEL.name()); // scroll bar panel JPanel pScrollBar = new JPanel(new GridBagLayout()); cbScrollBarUpState = new JCheckBox("Up arrow pressed", false); cbScrollBarUpState.addActionListener(this); cbScrollBarDownState = new JCheckBox("Down arrow pressed", false); cbScrollBarDownState.addActionListener(this); gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pScrollBar.add(cbScrollBarUpState, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pScrollBar.add(cbScrollBarDownState, gbc); pControl.add(pScrollBar, ControlType.SCROLL_BAR.name()); // putting all together gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(8, 8, 0, 8), 0, 0); add(pCommon, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(16, 8, 8, 8), 0, 0); add(pControl, gbc); // showing empty control by default showPanel(ControlType.UNKNOWN); } } // Manages a single CHU panel private static class Panel { private final Viewer viewer; private final Window panel; private BufferedImage image, bg; public Panel(Viewer viewer, Window panel) { this.viewer = viewer; this.panel = panel; } public Viewer getViewer() { return viewer; } public Window getResource() { return panel; } /** Returns the position of the background image for this panel. */ public Point getPosition() { return getResource().getWindowPosition(); } /** Returns width and height of the background image for this panel. */ public Dimension getDimension() { return getResource().getWindowDimension(); } /** Returns the total width and height of the panel. */ public Dimension getPanelDimension() { Point p = getPosition(); Dimension dim = getDimension(); dim.width += p.x; dim.height += p.y; return dim; } /** * Returns the background image for this panel. Use getPosition/getDimension to correctly * place it on the panel. */ public Image getImage() { if (image == null) { updateImage(); } return image; } /** Recreates the panel. */ public void reset() { image = null; bg = null; // recreating controls int numControls = getViewer().getControls().getSize(); for (int i = 0; i < numControls; i++) { BaseControl control = (BaseControl)getViewer().getControls().getElementAt(i); if (control != null) { control.updateImage(); } } repaint(); } /** Forces a repaint of the panel and its controls. */ public void repaint() { updateImage(); } @Override public String toString() { if (getResource() != null) { StringBuilder sb = new StringBuilder("ID: "); sb.append(getResource().getWindowId()); sb.append(" (Background: "); if (getResource().hasBackgroundImage()) { sb.append(getResource().getBackgroundImage()); } else { sb.append("none"); } sb.append(")"); return sb.toString(); } else { return "(none)"; } } private void updateImage() { if (image == null) { Dimension dim = getPanelDimension(); if (dim.width == 0) dim.width = 1; if (dim.height == 0) dim.height = 1; image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); } Graphics2D g = image.createGraphics(); if (g != null) { try { Composite comp = g.getComposite(); g.setComposite(AlphaComposite.Src); g.setColor(new Color(0, getViewer().isTransparentBackground())); g.fillRect(0, 0, image.getWidth(null), image.getHeight(null)); g.setComposite(comp); Point pPanel = getPosition(); // 1. drawing background image (if available) if (getResource().hasBackgroundImage()) { if (bg == null) { String resName = getResource().getBackgroundImage(); MosDecoder mos = MosDecoder.loadMos(ResourceFactory.getResourceEntry(resName)); if (mos != null) { if (mos instanceof MosV1Decoder) { ((MosV1Decoder)mos).setTransparencyEnabled(true); } bg = (BufferedImage)mos.getImage(); } } if (bg != null) { g.drawImage(bg, pPanel.x, pPanel.y, null); } } // 2. drawing control elements onto the panel int numControls = getViewer().getControls().getSize(); for (int i = 0; i < numControls; i++) { BaseControl control = (BaseControl)getViewer().getControls().getElementAt(i); if (control != null) { Image ctrlImage = control.getImage(); if (ctrlImage != null) { Point pCtrl = control.getPosition(); g.drawImage(ctrlImage, pPanel.x + pCtrl.x, pPanel.y + pCtrl.y, null); } } } // 3. drawing outline if needed if (getViewer().isControlOutlined()) { BaseControl control = getViewer().getSelectedControl(); if (control != null) { Point pCtrl = control.getPosition(); Dimension dim = control.getDimension(); g.setColor(control.getOutlinedColor()); g.drawRect(pPanel.x + pCtrl.x, pPanel.y + pCtrl.y, dim.width-1, dim.height-1); } } } finally { g.dispose(); g = null; } } } } // Common base for control specific classes private static abstract class BaseControl { private final Viewer viewer; private final Control control; private Color outlinedColor; private boolean visible; protected BaseControl(Viewer viewer, Control control, ControlType type) { if (viewer == null) { throw new NullPointerException("viewer is null"); } if (control != null && getControlType(control.getControlType()) != type) { throw new IllegalArgumentException("Control type does not match."); } this.viewer = viewer; this.control = control; outlinedColor = Color.GREEN; visible = true; } /** Returns the viewer object. */ public Viewer getViewer() { return viewer; } /** Returns the control object. */ public Control getResource() { return control; } /** Returns whether a valid control is associated with this instance. */ public boolean isEmpty() { return (control == null); } /** Returns the control type. */ public ControlType getType() { if (!isEmpty()) { return getControlType(getResource().getControlType()); } else { return ControlType.UNKNOWN; } } /** Returns the position of the control relative to the parent panel. */ public Point getPosition() { if (!isEmpty()) { return getResource().getControlPosition(); } else { return new Point(); } } /** Returns width and height of the control. */ public Dimension getDimension() { if (!isEmpty()) { Dimension dim = getResource().getControlDimensions(); if (dim.width <= 0) dim.width = 1; if (dim.height <= 0) dim.height = 1; return dim; } else { return new Dimension(1, 1); } } /** Set whether the control should be drawn. */ public void setVisible(boolean set) { visible = set; } /** Returns whether the control is drawn. */ public boolean isVisible() { return visible; } /** Returns the visual representation of the current control. */ public abstract Image getImage(); // /** Specify the outlined color. */ // public void setOutlinedColor(Color color) // { // if (color != null) { // if (!color.equals(outlinedColor)) { // outlinedColor = color; // updateImage(); // } // } else { // outlinedColor = Color.GREEN; // } // } /** Returns the outlined color. */ public Color getOutlinedColor() { return outlinedColor; } @Override public String toString() { if (getResource() != null) { StringBuilder sb = new StringBuilder("ID: "); sb.append(getResource().getControlId()); sb.append(" (Type: "); sb.append(getControlType(getResource().getControlType()).name()); sb.append(")"); return sb.toString(); } else { return "(none)"; } } /** Apply settings from properties panel. Note: Override to add more functionality. */ public void updateState() { PropertiesPanel panel = getViewer().getProperties(); setVisible(panel.isControlVisible()); } /** Use this method to repaint the control. */ public abstract void updateImage(); // Convert cases of the text controlled by flags. // (flags: 0=first letter upper case, 1=all letters upper case, 2=all letters lower case) protected String convertText(String text, int flags) { if (text != null && !text.isEmpty()) { StringBuilder sb = new StringBuilder(); // ctrl: 0=no change, 1=force upper, 2=force lower int ctrl = (flags == 2) ? 2 : 1; for (int i = 0; i < text.length(); i++) { char ch = text.charAt(i); if (ctrl == 1) { ch = Character.toUpperCase(ch); if (flags == 0 && !Character.isWhitespace(ch)) ctrl = 0; } else if (ctrl == 2) { ch = Character.toLowerCase(ch); } if (flags == 0 && (ch == '.' || ch == '?' || ch == '!')) { ctrl = 1; } sb.append(ch); } return sb.toString(); } return ""; } /** * Returns the dimension and base line for fully rendered the text with the given font BAM. * @param text The text to render. * @param fntBam The font BAM. * @return Required dimension and baseline to fully render the text. Dimension is specified by * width and height, vertical baseline position is specified by y. */ protected static Rectangle getTextDimension(String text, BamDecoder fntBam) { Rectangle rect = new Rectangle(); if (text != null && !text.isEmpty() && fntBam != null) { // 1. determine text height and baseline position int numFrames = fntBam.frameCount(); for (int i = 0; i < numFrames; i++) { FrameEntry info = fntBam.getFrameInfo(i); rect.y = Math.max(rect.y, info.getCenterY()); int lower = Math.max(0, info.getHeight() - info.getCenterY()); rect.height = Math.max(rect.height, rect.y+lower); } // 2. determine text width BamControl ctrl = fntBam.createControl(); ctrl.setMode(BamControl.Mode.INDIVIDUAL); for (int i = 0; i < text.length(); i++) { int ch = text.charAt(i); if (ch < 1 || ch > 255) ch = 32; int frameIdx = ctrl.cycleGetFrameIndexAbsolute(ch-1, 0); if (frameIdx >= 0) { rect.width += fntBam.getFrameInfo(frameIdx).getWidth(); } } } // don't return empty dimension if (rect.height == 0) rect.height = 1; if (rect.width == 0) rect.width = 1; return rect; } /** * Renders the given text message into an Image object. * @param text The text to render. * @param fntBam The Font BAM. * @param fntColor Text color (can be null). * @param trueColor Don't postprocess text if set. */ protected static Image drawText(String text, BamDecoder fntBam, Color fntColor, boolean trueColor) { BufferedImage image = null; if (text == null) text = ""; if (fntBam != null) { BamControl ctrl = fntBam.createControl(); ctrl.setMode(BamControl.Mode.INDIVIDUAL); Rectangle rect = getTextDimension(text, fntBam); BufferedImage letter = new BufferedImage(rect.width, rect.height, BufferedImage.TYPE_INT_ARGB); image = new BufferedImage(rect.width, rect.height, BufferedImage.TYPE_INT_ARGB); Graphics2D g = image.createGraphics(); try { int curX = 0; for (int i = 0; i < text.length(); i++) { int ch = text.charAt(i); if (ch < 1 || ch > 255) ch = 32; ctrl.cycleSet(ch-1); ctrl.cycleSetFrameIndex(0); int frameIdx = ctrl.cycleGetFrameIndexAbsolute(); if (frameIdx >= 0) { ctrl.cycleGetFrame(letter); FrameEntry info = fntBam.getFrameInfo(frameIdx); int width = info.getWidth(); int height = info.getHeight(); int baseline = info.getCenterY(); int dx = curX; int dy = rect.y - baseline; g.drawImage(letter, dx, dy, dx+width, dy+height, 0, 0, width, height, null); curX += width; } } } finally { g.dispose(); g = null; } // postprocessing text // Dirty hack: the correct information is most likely stored in the FNT resource directly if ((Profile.isEnhancedEdition() || trueColor == false) && ctrl instanceof BamV1Control) { int[] palette = ((BamV1Control)ctrl).getPalette(); // Hack: determining whether palette contains alpha values boolean isAlpha = (palette.length == 256); int intensity = palette[1] & 0xff; for (int i = 1; i < 255 && isAlpha; i++, intensity--) { int alpha = intensity | (intensity << 8) | (intensity << 16); isAlpha &= (palette[i] & 0xffffff) == alpha; } if (isAlpha) { int rgb; if (fntColor != null) { rgb = (fntColor.getBlue() << 16) | (fntColor.getGreen() << 8) | fntColor.getRed(); } else { rgb = palette[1] & 0x00ffffff; } int[] buffer = ((DataBufferInt)image.getRaster().getDataBuffer()).getData(); if (buffer != null) { for (int i = 0; i < buffer.length; i++) { if ((buffer[i] & 0xff000000) != 0) { buffer[i] = rgb | ((255 - (buffer[i] & 255)) << 24); } } } } } } return image; } } // Manages the visual appearance of unrecognized control types private static class UnknownControl extends BaseControl { private BufferedImage image; public UnknownControl(Viewer viewer, Control control) { super(viewer, control, ControlType.UNKNOWN); updateImage(); } @Override public Image getImage() { return image; } @Override public void updateImage() { if (image == null) { Dimension dim = getDimension(); image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); } if (!isEmpty()) { Graphics2D g = image.createGraphics(); if (g != null) { try { // 1. clearing image g.setComposite(AlphaComposite.Src); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); // 2. drawing control // nothing to do... } finally { g.dispose(); g = null; } } } } } // Manages the visual appearance of buttons private static class ButtonControl extends BaseControl { private static final int UNPRESSED = 0; private static final int PRESSED = 1; private static final int SELECTED = 2; private static final int DISABLED = 3; private static final HashSet<String> ignoreResourceSet = new HashSet<String>(); static { // XXX: ignore a set of known background BAMs with cycle and frame indices ignoreResourceSet.add("BIGBUTT.BAM:0:0"); ignoreResourceSet.add("BIGBUTT.BAM:0:1"); ignoreResourceSet.add("BIGBUTT.BAM:0:2"); ignoreResourceSet.add("CIFF4INV.BAM:0:0"); ignoreResourceSet.add("CIFF4INV.BAM:0:1"); ignoreResourceSet.add("CIFF4INV.BAM:0:2"); ignoreResourceSet.add("CIFF4INV.BAM:0:3"); ignoreResourceSet.add("GUICTRL.BAM:0:0"); ignoreResourceSet.add("GUICTRL.BAM:0:1"); ignoreResourceSet.add("GUICTRL.BAM:0:2"); ignoreResourceSet.add("GUICTRL.BAM:0:3"); } private BufferedImage image; private int buttonState; public ButtonControl(Viewer viewer, Control control) { super(viewer, control, ControlType.BUTTON); buttonState = UNPRESSED; updateImage(); } // Required for properties panel public void setUnPressed() { buttonState = UNPRESSED; } public void setPressed() { buttonState = PRESSED; } public void setSelected() { buttonState = SELECTED; } public void setDisabled() { buttonState = DISABLED; } public boolean isUnpressed() { return (buttonState == UNPRESSED); } public boolean isPressed() { return (buttonState == PRESSED); } public boolean isSelected() { return (buttonState == SELECTED); } public boolean isDisabled() { return (buttonState == DISABLED); } @Override public Image getImage() { if (image == null) { updateImage(); } return image; } @Override public void updateState() { super.updateState(); PropertiesPanel panel = getViewer().getProperties(); if (panel.isButtonUnpressed()) { setUnPressed(); } else if (panel.isButtonPressed()) { setPressed(); } else if (panel.isButtonSelected()) { setSelected(); } else if (panel.isButtonDisabled()) { setDisabled(); } } @Override public void updateImage() { if (image == null) { Dimension dim = getDimension(); image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); } if (!isEmpty()) { Graphics2D g = image.createGraphics(); if (g != null) { try { // 1. clearing image Composite comp = g.getComposite(); g.setComposite(AlphaComposite.Src); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.setComposite(comp); // 2. drawing control if (isVisible()) { // loading BAM String resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_BTN_RESREF)).getResourceName(); // getting specified cycle index int cycleIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_BTN_ANIMATION_INDEX)).getValue(); int frameIdx = 0; // getting specified cycle frame index if (isUnpressed()) { frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_UNPRESSED)).getValue(); } else if (isPressed()) { frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_PRESSED)).getValue(); } else if (isSelected()) { frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_SELECTED)).getValue(); } else if (isDisabled()) { frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_DISABLED)).getValue(); } if (!isResourceIgnored(resName, cycleIdx, frameIdx)) { BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); if (bam != null) { BamControl bamCtrl = bam.createControl(); bamCtrl.setMode(BamControl.Mode.INDIVIDUAL); if (cycleIdx >= 0 && cycleIdx < bamCtrl.cycleCount()) { bamCtrl.cycleSet(cycleIdx); if (frameIdx >= 0 && frameIdx < bamCtrl.cycleFrameCount()) { bamCtrl.cycleSetFrameIndex(frameIdx); // drawing BAM Image bamImage = bamCtrl.cycleGetFrame(); g.drawImage(bamImage, 0, 0, null); } } } } } } finally { g.dispose(); g = null; } } } } // Indicates whether to ignore the resource specified by the given filename private static boolean isResourceIgnored(String resName, int cycleIdx, int frameIdx) { if (resName != null && !resName.isEmpty()) { return ignoreResourceSet.contains(String.format("%1$s:%2$d:%3$d", resName.toUpperCase(Locale.ENGLISH), cycleIdx, frameIdx)); } else { return true; } } } // Manages the visual appearance of sliders private static class SliderControl extends BaseControl { private BufferedImage image; private boolean sliderGrabbed; public SliderControl(Viewer viewer, Control control) { super(viewer, control, ControlType.SLIDER); sliderGrabbed = false; updateImage(); } // required for properties panel public void setGrabbed(boolean b) { sliderGrabbed = b; } public boolean isGrabbed() { return sliderGrabbed; } @Override public Image getImage() { if (image == null) { updateImage(); } return image; } @Override public void updateState() { super.updateState(); PropertiesPanel panel = getViewer().getProperties(); setGrabbed(panel.isSliderGrabbed()); } @Override public void updateImage() { if (image == null) { Dimension dim = getDimension(); image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); } if (!isEmpty()) { Graphics2D g = image.createGraphics(); if (g != null) { try { // 1. clearing image Composite comp = g.getComposite(); g.setComposite(AlphaComposite.Src); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.setComposite(comp); // 2. drawing control // 2.1. drawing background image String resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_SLD_BACKGROUND)).getResourceName(); MosDecoder mos = MosDecoder.loadMos(ResourceFactory.getResourceEntry(resName)); if (mos != null) { if (mos instanceof MosV1Decoder) { ((MosV1Decoder)mos).setTransparencyEnabled(true); } mos.getImage(image); } // 2.2. drawing control elements if (isVisible()) { resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_SLD_KNOB)).getResourceName(); BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); if (bam != null) { BamControl bamCtrl = bam.createControl(); bamCtrl.setMode(BamControl.Mode.INDIVIDUAL); // getting specified cycle index int cycleIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SLD_ANIMATION_INDEX)).getValue(); cycleIdx = Math.min(bamCtrl.cycleCount()-1, Math.max(0, cycleIdx)); bamCtrl.cycleSet(cycleIdx); int frameIdx; // getting specified cycle frame index if (isGrabbed()) { frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SLD_FRAME_INDEX_GRABBED)).getValue(); } else { frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SLD_FRAME_INDEX_UNGRABBED)).getValue(); } frameIdx = Math.min(bamCtrl.cycleFrameCount()-1, Math.max(0, frameIdx)); bamCtrl.cycleSetFrameIndex(frameIdx); // drawing frame Image knob = bamCtrl.cycleGetFrame(); int knobX = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SLD_KNOB_POSITION_X)).getValue(); int knobY = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SLD_KNOB_POSITION_Y)).getValue(); g.drawImage(knob, knobX, knobY, knob.getWidth(null), knob.getHeight(null), null); } } } finally { g.dispose(); g = null; } } } } } // Manages the visual appearance of text fields private static class TextFieldControl extends BaseControl { private static final HashSet<String> ignoreResourceSet = new HashSet<String>(); static { // XXX: ignore a set of known background MOS resources ignoreResourceSet.add("XXXXGRBG.MOS"); } private BufferedImage image; private boolean caretEnabled; public TextFieldControl(Viewer viewer, Control control) { super(viewer, control, ControlType.TEXT_FIELD); caretEnabled = false; updateImage(); } // required for properties panel public void setCaretEnabled(boolean b) { caretEnabled = b; } public boolean isCaretEnabled() { return caretEnabled; } @Override public Image getImage() { if (image == null) { updateImage(); } return image; } @Override public void updateState() { super.updateState(); PropertiesPanel panel = getViewer().getProperties(); setCaretEnabled(panel.isTextFieldCaretEnabled()); } @Override public void updateImage() { if (image == null) { Dimension dim = getDimension(); image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); } if (!isEmpty()) { Graphics2D g = image.createGraphics(); if (g != null) { try { // 1. clearing image Composite comp = g.getComposite(); g.setComposite(AlphaComposite.Src); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.setComposite(comp); // 2. drawing control // 2.1. drawing background image (is it actually used?) String resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_TF_BACKGROUND_1)).getResourceName(); if (!isResourceIgnored(resName)) { MosDecoder mos = MosDecoder.loadMos(ResourceFactory.getResourceEntry(resName)); if (mos != null) { if (mos instanceof MosV1Decoder) { ((MosV1Decoder)mos).setTransparencyEnabled(true); } mos.getImage(image); } } // 2.2. drawing text if (isVisible()) { int caretX = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_TF_CARET_POSITION_X)).getValue(); int caretY = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_TF_CARET_POSITION_Y)).getValue(); String text = ((TextString)getResource().getAttribute(Control.CHU_CONTROL_TF_TEXT)).toString(); if (!text.isEmpty()) { resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_TF_FONT)).getResourceName(); resName = resName.toUpperCase(Locale.ENGLISH).replace(".FNT", ".BAM"); BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); if (bam != null) { int maxLen = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_TF_FIELD_LENGTH)).getValue(); if (text.length() > maxLen) text = text.substring(0, maxLen); int flags = ((Bitmap)getResource().getAttribute(Control.CHU_CONTROL_TF_ALLOWED_CSE)).getValue(); text = convertText(text, flags); Image textImage = drawText(text, bam, null, false); if (textImage != null) { g.drawImage(textImage, caretX, caretY, textImage.getWidth(null), textImage.getHeight(null), null); } } } // 2.3. drawing caret if (isCaretEnabled()) { resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_TF_CARET)).getResourceName(); BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); if (bam != null) { BamControl bamCtrl = bam.createControl(); bamCtrl.setMode(BamControl.Mode.INDIVIDUAL); // getting specified cycle index int cycleIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_TF_ANIMATION_INDEX)).getValue(); cycleIdx = Math.min(bamCtrl.cycleCount()-1, Math.max(0, cycleIdx)); bamCtrl.cycleSet(cycleIdx); // getting specified cycle frame index int frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_TF_FRAME_INDEX)).getValue(); frameIdx = Math.min(bamCtrl.cycleFrameCount()-1, Math.max(0, frameIdx)); bamCtrl.cycleSetFrameIndex(frameIdx); // drawing frame Image caret = bamCtrl.cycleGetFrame(); g.drawImage(caret, caretX, caretY, null); } } } } finally { g.dispose(); g = null; } } } } // Indicates whether to ignore the resource specified by the given filename private static boolean isResourceIgnored(String resName) { if (resName != null && !resName.isEmpty()) { return ignoreResourceSet.contains(resName.toUpperCase(Locale.ENGLISH)); } else { return true; } } } // Manages the visual appearance of text areas private static class TextAreaControl extends BaseControl { private BufferedImage image; public TextAreaControl(Viewer viewer, Control control) { super(viewer, control, ControlType.TEXT_AREA); updateImage(); } @Override public Image getImage() { if (image == null) { updateImage(); } return image; } @Override public void updateImage() { if (image == null) { Dimension dim = getDimension(); image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); } if (!isEmpty()) { Graphics2D g = image.createGraphics(); if (g != null) { try { // 1. clearing image Composite comp = g.getComposite(); g.setComposite(AlphaComposite.Src); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.setComposite(comp); // 2. drawing control // nothing to do? } finally { g.dispose(); g = null; } } } } } // Manages the visual appearance of labels private static class LabelControl extends BaseControl { private BufferedImage image; public LabelControl(Viewer viewer, Control control) { super(viewer, control, ControlType.LABEL); updateImage(); } @Override public Image getImage() { if (image == null) { updateImage(); } return image; } @Override public void updateImage() { if (image == null) { Dimension dim = getDimension(); image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); } if (!isEmpty()) { Graphics2D g = image.createGraphics(); if (g != null) { try { // 1. clearing image Composite comp = g.getComposite(); g.setComposite(AlphaComposite.Src); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.setComposite(comp); // 2. drawing control if (isVisible()) { String text = StringResource.getStringRef(((StringRef)getResource().getAttribute(Control.CHU_CONTROL_LBL_TEXT)).getValue()); if (text != null) { String resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_LBL_FONT)).getResourceName(); resName = resName.toUpperCase(Locale.ENGLISH).replace(".FNT", ".BAM"); BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); if (bam != null) { Flag flags = (Flag)getResource().getAttribute(Control.CHU_CONTROL_LBL_FLAGS); Color col = null; if (flags.isFlagSet(0)) { col = new Color(((ColorPicker)getResource().getAttribute(Control.CHU_CONTROL_LBL_COLOR_1)).getValue()); } Image textImage = drawText(text, bam, col, flags.isFlagSet(1)); if (textImage != null) { // calculating text alignment int srcWidth = textImage.getWidth(null); int srcHeight = textImage.getHeight(null); int dstWidth = image.getWidth(); int dstHeight = image.getHeight(); int x = 0, y = 0; if (flags.isFlagSet(3)) { // left x = 0; } else if (flags.isFlagSet(4)) { // right x = dstWidth - srcWidth; } else { // hcenter x = (dstWidth - srcWidth) / 2; } if (flags.isFlagSet(5)) { // top y = 0; } else if (flags.isFlagSet(7)) { // bottom y = dstHeight - srcHeight; } else { // vcenter y = (dstHeight - srcHeight) / 2; } // drawing text g.drawImage(textImage, x, y, srcWidth, srcHeight, null); } } } } } finally { g.dispose(); g = null; } } } } } // Manages the visual appearance of scroll bars private static class ScrollBarControl extends BaseControl { private BufferedImage image; private boolean upPressed, downPressed; public ScrollBarControl(Viewer viewer, Control control) { super(viewer, control, ControlType.SCROLL_BAR); upPressed = downPressed = false; updateImage(); } // required for properties panel public void setUpArrowPressed(boolean b) { upPressed = b; } public boolean isUpArrowPressed() { return upPressed; } public void setDownArrowPressed(boolean b) { downPressed = b; } public boolean isDownArrowPressed() { return downPressed; } @Override public Image getImage() { if (image == null) { updateImage(); } return image; } @Override public void updateState() { super.updateState(); PropertiesPanel panel = getViewer().getProperties(); setUpArrowPressed(panel.isScrollBarUpArrowPressed()); setDownArrowPressed(panel.isScrollBarDownArrowPressed()); } @Override public void updateImage() { if (image == null) { Dimension dim = getDimension(); image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); } if (!isEmpty()) { Graphics2D g = image.createGraphics(); if (g != null) { try { // 1. clearing image Composite comp = g.getComposite(); g.setComposite(AlphaComposite.Src); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.setComposite(comp); // 2. drawing control if (isVisible()) { String resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_SB_GRAPHICS)).getResourceName(); BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); if (bam != null) { int cycleIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_ANIMATION_INDEX)).getValue(); int frameTroughIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_TROUGH)).getValue(); int frameSliderIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_SLIDER)).getValue(); int frameUpIdx, frameDownIdx; if (isUpArrowPressed()) { frameUpIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_UP_PRESSED)).getValue(); } else { frameUpIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_UP_UNPRESSED)).getValue(); } if (isDownArrowPressed()) { frameDownIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_DOWN_PRESSED)).getValue(); } else { frameDownIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_DOWN_UNPRESSED)).getValue(); } BamControl ctrl = bam.createControl(); ctrl.cycleSet(cycleIdx); // drawing trough Image image = ctrl.cycleGetFrame(frameTroughIdx); if (image != null) { int idx = ctrl.cycleGetFrameIndexAbsolute(frameUpIdx); int top = bam.getFrameInfo(idx).getHeight(); idx = ctrl.cycleGetFrameIndexAbsolute(frameDownIdx); int bottom = getDimension().height - bam.getFrameInfo(idx).getHeight(); int w = image.getWidth(null); int h = image.getHeight(null); int segments = (bottom-top) / h; int lastSegmentHeight = (bottom-top) % h; idx = ctrl.cycleGetFrameIndexAbsolute(frameTroughIdx); int curY = top; for (int i = 0; i < segments; i++) { g.drawImage(image, 0, curY, null); curY += h; } if (lastSegmentHeight > 0) { g.drawImage(image, 0, curY, w, curY+lastSegmentHeight, 0, 0, w, lastSegmentHeight, null); } } // drawing up arrow image = ctrl.cycleGetFrame(frameUpIdx); if (image != null) { g.drawImage(image, 0, 0, null); } // drawing down arrow image = ctrl.cycleGetFrame(frameDownIdx); if (image != null) { int curY = getDimension().height - image.getHeight(null); g.drawImage(image, 0, curY, null); } // drawing slider knob image = ctrl.cycleGetFrame(frameSliderIdx); if (image != null) { int curY = bam.getFrameInfo(ctrl.cycleGetFrameIndexAbsolute(frameUpIdx)).getHeight(); g.drawImage(image, 0, curY, null); } } } } finally { g.dispose(); g = null; } } } } } }