001package jmri.jmrit.display;
002
003import java.awt.Color;
004import java.awt.Dimension;
005import java.awt.image.BufferedImage;
006
007import jmri.util.JmriJFrame;
008import jmri.util.swing.DrawSquares;
009
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013/**
014 * Extended JmriJFrame that allows to add an InitEventListener for display of
015 * a tabbed frame in the CPE Add Item {@link jmri.jmrit.display.palette.ItemPalette} pane.
016 * <p>
017 * <a href="doc-files/DisplayFrame-ClassDiagram.png"><img src="doc-files/DisplayFrame-ClassDiagram.png" alt="UML Class diagram" height="50%" width="50%"></a>
018 *
019 * @author Egbert Broerse Copyright (c) 2017, 2021
020 **/
021/*
022@startuml jmri/jmrit/display/doc-files/DisplayFrame-ClassDiagram.png
023
024class jmri.util.JmriJFrame
025class jmri.util.swing.ImagePanel {
026-BufferedImage back
027+setImage()
028+paintComponent()
029}
030class jmri.jmrit.DisplayFrame  #88dddd {
031-previewBgIndex
032#SetInitListener()
033#setPreviewBg(i)
034#getPreviewBg()
035}
036class jmri.jmrit.display.IconEditor
037
038object AddItem_TabbedPane
039AddItem_TabbedPane : Tab[1] = TurnoutTab
040AddItem_TabbedPane : Tab[n] = IndicatorTab
041object TurnoutItemPanel
042TurnoutItemPanel : type = "Turnout"
043object SignalMastItemPanel
044SignalMastItemPanel : type = "SignalMast"
045object xItemPanel
046xItemPanel : type = "x"
047object viewOnCombo
048viewOnCombo : -int choice
049viewOnCombo : +EventListener InitListener
050object preview
051preview : -image = 1
052preview : +EventListener comboListener
053
054AddItem_TabbedPane --> TurnoutItemPanel : show()
055AddItem_TabbedPane --> SignalMastItemPanel : show()
056AddItem_TabbedPane --> xItemPanel : show()
057jmri.util.JmriJFrame --|> jmri.jmrit.DisplayFrame
058jmri.jmrit.DisplayFrame *-- jmri.jmrit.display.IconEditor
059SignalMastItemPanel *-- viewOnCombo
060TurnoutItemPanel *-- viewOnCombo
061xItemPanel *-- viewOnCombo
062AddItem_TabbedPane ..> viewOnCombo: TabShown(i)
063viewOnCombo ..> preview: SetImage[n]
064jmri.jmrit.display.IconEditor *-- viewOnCombo
065jmri.jmrit.DisplayFrame *-- AddItem_TabbedPane
066jmri.util.swing.ImagePanel -- preview
067
068@enduml
069*/
070public class DisplayFrame extends JmriJFrame {
071
072    static Color _grayColor = new Color(235, 235, 235);
073    static Color _darkGrayColor = new Color(150, 150, 150);
074    static protected Color[] colorChoice = new Color[]{Color.white, _grayColor, _darkGrayColor}; // panel bg color picked up directly
075
076    // Array of BufferedImage backgrounds loaded as background image in Preview, shared across tabs
077    private BufferedImage[] _backgrounds;
078    private Editor _editor;          // current panel editor using this frame
079    private Color _panelBackground;  // current background
080    private int previewBgIndex = 0;    // Shared index setting for preview pane background color combo choice.
081
082
083    /**
084     * Create a JmriJFrame with standard settings, optional save/restore of size
085     * and position.
086     *
087     * @param saveSize     set true to save the last known size
088     * @param savePosition set true to save the last known location
089     */
090    public DisplayFrame(boolean saveSize, boolean savePosition) {
091        super(saveSize, savePosition);
092    }
093
094    /**
095     * Create a JmriJFrame with with given name plus standard settings, including
096     * optional save/restore of size and position.
097     *
098     * @param name         title of the Frame
099     * @param saveSize     set true to save the last knowm size
100     * @param savePosition set true to save the last known location
101     */
102    public DisplayFrame(String name, boolean saveSize, boolean savePosition) {
103        super(name, saveSize, savePosition);
104    }
105
106    /**
107     * Create a JmriJFrame for ItemPalette or for edit popups of a given editor panel.
108     * Such child classes need to provide backgrounds for their panes and panels.
109     *
110     * @param name         title of the Frame
111     * @param editor       editor of panel items 
112     */
113    public DisplayFrame(String name, Editor editor) {
114        super(name, false, false);
115        _editor = editor;
116        makeBackgrounds();
117    }
118
119    /**
120     * Create a JmriJFrame with standard settings, including saving/restoring of
121     * size and position.
122     */
123    public DisplayFrame() {
124        this(true, true);
125    }
126
127    /**
128     * Create a JmriJFrame with with given name plus standard settings, including
129     * saving/restoring of size and position.
130     *
131     * @param name title of the JFrame
132     */
133    public DisplayFrame(String name) {
134        this(name, true, true);
135    }
136
137    /**
138     * This may be used as a callback to notify children of this class 
139     * when the preview color has changed.
140     * Children of this class should override if there are several other
141     * members with separate preview panels.  e.g. ItemPalette
142     * But prevent a loop when calling super in that process (bug in 4.21.3; fixed in 4.21.4)
143     * 
144     * @param index index of selection in _backgrounds array
145     */
146    public void setPreviewBg(int index) {
147        previewBgIndex = index;
148    }
149
150    public int getPreviewBg() {
151        return previewBgIndex;
152    }
153
154    public BufferedImage getPreviewBackground() {
155        return _backgrounds[previewBgIndex];
156    }
157
158    /**
159     * 
160     * @return the color of the background of editor display panel
161     */
162    public Color getCurrentColor() {
163        // should be _editor.getTargetPanel().getBackground()
164        return _panelBackground;
165    }
166
167    public BufferedImage getBackground(int index) {
168        return _backgrounds[index];
169    }
170
171    /**
172     * Called when the background of the display panel is changed.
173     * @param ed the editor of the display panel
174     */
175    public void updateBackground(Editor ed) {
176        if (ed == null) {
177            log.error("updateBackground called for a null editor!");
178            return; 
179        }
180        _editor = ed;
181        Color color = ed.getTargetPanel().getBackground();
182        if (!color.equals(_panelBackground)) {
183            _backgrounds[0] = DrawSquares.getImage(500, 400, 10, color, color);
184            _panelBackground = color;
185            if (previewBgIndex == 0) {
186                setPreviewBg(0);    // notify children
187            }
188        }
189    }
190
191    public Editor getEditor() {
192        return _editor;
193    }
194
195    /**
196     * Make an array of background BufferedImages for the PreviewPanels
197     */
198    private void makeBackgrounds() {
199        _panelBackground = _editor.getTargetPanel().getBackground(); // start using Panel background color
200        if (_backgrounds == null) { // reduces load but will not redraw for new size
201            _backgrounds = new BufferedImage[5];
202            for (int i = 1; i <= 3; i++) {
203                _backgrounds[i] = DrawSquares.getImage(500, 400, 10, colorChoice[i - 1], colorChoice[i - 1]);
204                // [i-1] because choice 0 is not in colorChoice[]
205            }
206            _backgrounds[4] = DrawSquares.getImage(500, 400, 10, Color.white, _grayColor);
207        }
208        // always update background from Panel Editor
209        _backgrounds[0] = DrawSquares.getImage(500, 400, 10, _panelBackground, _panelBackground);
210        log.debug("makeBackgrounds backgrounds[0] = {}", _backgrounds[0]);
211    }
212
213    /**
214     * Resizes this frame to accommodate the size of the tab panel when tab is changed.
215     * Otherwise it may force the tab panel to use scrollbars or be far oversized.
216     * As a trade off to keep right mouse arrow in same place for ItemPalette accept frame is wider in few cases.
217     * 
218     * @param container Container to be resized
219     * @param deltaDim Size difference of container with old contents
220     * @param newDim Size of the new contents
221     */
222    public void reSize(java.awt.Container container, Dimension deltaDim, Dimension newDim) {
223        Dimension dim = new Dimension(deltaDim.width + newDim.width, deltaDim.height + newDim.height);
224        container.setPreferredSize(dim);
225        container.invalidate();
226        if (log.isDebugEnabled())
227            log.debug(" deltaDim= ({}, {}) NewDim= ({}, {}) setPreferredSize to ({}, {})", 
228                deltaDim.width, deltaDim.height, newDim.width, newDim.height, dim.width, dim.height);
229        pack();
230        if (log.isDebugEnabled()) {
231            dim = container.getSize();
232            log.debug(" Resized to ({}, {})", dim.width, dim.height);
233        }
234    }
235
236    private final static Logger log = LoggerFactory.getLogger(DisplayFrame.class);
237
238}