View Javadoc
1   /*
2   Copyright (C) 2000 - 2007 Grid Systems, S.A.
3   
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2, as
6   published by the Free Software Foundation.
7   
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12  
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17  
18  /*
19   * Project: KernelConfigurator
20   * Created on 24-feb-2004
21   *
22   * Copyright (c)2003 Grid Systems
23   */
24  package com.gridsystems.config.app;
25  
26  import java.awt.BorderLayout;
27  import java.awt.Color;
28  import java.awt.Component;
29  import java.awt.Dimension;
30  import java.awt.FlowLayout;
31  import java.awt.Font;
32  import java.awt.GridBagConstraints;
33  import java.awt.GridBagLayout;
34  import java.awt.Insets;
35  import java.awt.event.ActionEvent;
36  import java.text.MessageFormat;
37  import java.util.Stack;
38  
39  import javax.swing.AbstractAction;
40  import javax.swing.Action;
41  import javax.swing.BorderFactory;
42  import javax.swing.Icon;
43  import javax.swing.InputVerifier;
44  import javax.swing.JButton;
45  import javax.swing.JComponent;
46  import javax.swing.JLabel;
47  import javax.swing.JPanel;
48  import javax.swing.JProgressBar;
49  import javax.swing.event.ChangeEvent;
50  import javax.swing.event.ChangeListener;
51  
52  import com.gridsystems.config.Configurator;
53  import com.gridsystems.config.ConfiguratorView;
54  import com.gridsystems.config.ErrorListener;
55  import com.gridsystems.config.SwingConfiguratorView;
56  import com.gridsystems.config.tools.swing.SwingTools;
57  
58  /**
59   * Specialized JPanel that handles the active configurator and its swing view.
60   *
61   * @author <a href="mailto:rruiz@gridsystems.com">Rodrigo Ruiz Aguayo</a>
62   * @version 1.0
63   */
64  public class ConfigPanel extends JPanel implements ChangeListener, ErrorListener {
65    /**
66     * Configurator instance.
67     */
68    private Configurator cfg = null;
69  
70    /**
71     * View instance.
72     */
73    private JComponent component = null;
74  
75    /**
76     * Head banner.
77     */
78    private JPanel header = null;
79  
80    /**
81     * Button panel.
82     */
83    private JPanel footer = null;
84  
85    /**
86     * Configurator name.
87     */
88    private JLabel lblName = null;
89  
90    /**
91     * Configurator description.
92     */
93    private JLabel lblDescription = null;
94  
95    /**
96     * Configurator icon.
97     */
98    private JLabel lblIcon = null;
99  
100   /**
101    * Apply button.
102    */
103   private JButton btnApply = new JButton();
104 
105   /**
106    * Discard button.
107    */
108   private JButton btnDiscard = new JButton();
109 
110   /**
111    * Apply action instance.
112    */
113   private Action actApply;
114 
115   /**
116    * Discard action instance.
117    */
118   private Action actDiscard;
119 
120   /**
121    * Progress bar to tell to wait.
122    */
123   private JProgressBar progressBar = null;
124 
125   /**
126    * Stores the previous message (from a label) it it has to be temporally changed.
127    */
128   private String oldMessage = null;
129 
130   /**
131    * Default constructor.
132    */
133   public ConfigPanel() {
134     super(new BorderLayout());
135     setPreferredSize(new Dimension(400, 400));
136     JLabel label = new JLabel(UI.getString("lblSelectConfig"));
137     label.setHorizontalAlignment(JLabel.CENTER);
138     this.add(label, BorderLayout.CENTER);
139     this.component = label;
140 
141     // Ensures Swing Mode is active
142     Configurator.setViewMode(Configurator.MODE_SWING);
143   }
144 
145   /**
146    * Changes the configurator managed by this instance.
147    *
148    * @param c A configurator instance
149    */
150   public void setConfigurator(Configurator c) {
151     if (this.cfg != null) {
152       this.cfg.setErrorListener(null);
153     }
154 
155     if (c != null) {
156       this.cfg = c;
157       this.cfg.setErrorListener(this);
158       setComponent(cfg);
159       updateHeader();
160       updateFooter();
161 
162       this.validate();
163       this.repaint();
164     }
165   }
166 
167   /**
168    * Gets the current edition component.
169    *
170    * @return The current component
171    */
172   public JComponent getComponent() {
173     return this.component;
174   }
175 
176   /**
177    * Adds an error to the component error stack.
178    *
179    * @param id  The error id
180    * @param msg The error message
181    * @param add The error action
182    */
183   public void updateError(String id, String msg, boolean add) {
184     Stack<String> stack = getErrorStack();
185     stack.remove(msg);
186     if (add) {
187       stack.push(msg);
188     }
189     updateHeader();
190     updateFooter();
191   }
192 
193   /**
194    * @see ErrorListener#clearErrors()
195    */
196   public void clearErrors() {
197     Stack stack = getErrorStack();
198     stack.clear();
199     updateHeader();
200     updateFooter();
201   }
202 
203   /**
204    * Gets the error stack from the current component.
205    *
206    * @return The current component stack, or null if there is no current component
207    */
208   private Stack<String> getErrorStack() {
209     Stack<String> stack = null;
210     if (component != null) {
211       stack = (Stack<String>)component.getClientProperty("error.messages");
212       if (stack == null) {
213         stack = new Stack<String>();
214         component.putClientProperty("error.messages", stack);
215       }
216     }
217     return stack;
218   }
219 
220   /**
221    * Changes the active view to the one returned by the specified configurator.
222    *
223    * @param c The configurator
224    */
225   private void setComponent(Configurator c) {
226     SwingConfiguratorView view = (SwingConfiguratorView) c.getView();
227 
228     remove(component);
229     if (component == this.progressBar) {
230       progressBar = null;
231     }
232     component = view.getComponent();
233     add(component, BorderLayout.CENTER);
234 
235     view.addChangeListener(this);
236   }
237 
238   /**
239    * Refreshes the header displayed information with data obtained from the
240    * active view.
241    */
242   private void updateHeader() {
243     if (header == null) {
244       header = new JPanel(new GridBagLayout());
245       header.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.GRAY));
246       header.setBackground(Color.WHITE);
247       header.setPreferredSize(new Dimension(100, 60));
248       this.add(header, BorderLayout.NORTH);
249 
250       lblName = new JLabel();
251       header.add(lblName, new GridBagConstraints(0, 0, 1, 1, 1.0, 0,
252           GridBagConstraints.WEST, GridBagConstraints.BOTH,
253           new Insets(2, 2, 2, 2), 0, 0));
254 
255       lblName.setFont(Font.decode("Sans Serif-BOLD-22"));
256 
257       lblDescription = new JLabel();
258       header.add(lblDescription, new GridBagConstraints(0, 1, 1, 1, 1.0, 0,
259           GridBagConstraints.WEST, GridBagConstraints.BOTH,
260           new Insets(2, 2, 2, 2), 0, 0));
261 
262       lblDescription.setFont(Font.decode("Sans Serif-ITALIC-12"));
263 
264       lblIcon = new JLabel();
265       header.add(lblIcon, new GridBagConstraints(1, 0, 1, 2, 0, 0,
266           GridBagConstraints.CENTER, GridBagConstraints.BOTH,
267           new Insets(2, 2, 2, 2), 0, 0));
268     }
269 
270     String name = cfg.getView().getTitle();
271     lblName.setText(name == null ? "" : name);
272 
273     Stack stack = getErrorStack();
274     if (stack == null || stack.isEmpty()) {
275       String description = cfg.getView().getSubtitle();
276       lblDescription.setText(description == null ? "" : description);
277       lblDescription.setIcon(null);
278       lblDescription.setForeground(Color.BLACK);
279     } else {
280       String msg = (String)stack.peek();
281       lblDescription.setText(msg);
282       lblDescription.setForeground(Color.RED);
283     }
284 
285     ConfiguratorView view = cfg.getView();
286     if (view != null && view instanceof SwingConfiguratorView) {
287       Icon icon = ((SwingConfiguratorView) view).getIcon();
288       lblIcon.setIcon(icon);
289     }
290   }
291 
292   /**
293    * Refreshes the bottom buttons depending on the "modified" state of the
294    * active view.
295    */
296   private void updateFooter() {
297     if (footer == null) {
298       footer = new JPanel(new FlowLayout(FlowLayout.TRAILING));
299       footer.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.BLACK));
300 
301       footer.add(btnApply);
302       footer.add(btnDiscard);
303 
304       actApply = new ApplyAction();
305       btnApply.setAction(actApply);
306 
307       actDiscard = new DiscardAction();
308       btnDiscard.setAction(actDiscard);
309 
310       this.add(footer, BorderLayout.SOUTH);
311     }
312 
313     ConfiguratorView view = cfg.getView();
314     if (view != null && view instanceof SwingConfiguratorView) {
315       Stack stack = getErrorStack();
316       boolean modified = true;
317       boolean error = stack != null && !stack.isEmpty();
318       actApply.setEnabled(modified && !error);
319       actDiscard.setEnabled(modified || error);
320     }
321   }
322 
323   /**
324    * Action to be performed on "Apply" button press.
325    *
326    * @author <a href="mailto:rruiz@gridsystems.com">Rodrigo Ruiz Aguayo</a>
327    * @version 1.0
328    */
329   private class ApplyAction extends AbstractAction {
330     /**
331      * Creates an instance.
332      */
333     public ApplyAction() {
334       super(UI.getString("btnApply.text"));
335     }
336 
337     /**
338      * {@inheritDoc}
339      */
340     public void actionPerformed(ActionEvent event) {
341       Configurator config = ConfigPanel.this.cfg;
342       try {
343         if (config != null) {
344           config.apply();
345           SwingTools.showInformation(UI.getString("lblChangesApplied"));
346         }
347       } catch (Exception e) {
348         String pattern = UI.getString("errors.apply_error");
349         Object[] params = { config.getName()};
350         String message = MessageFormat.format(pattern, params);
351         SwingTools.showError(message, e);
352       }
353     }
354   }
355 
356   /**
357    * Action to be performed on "Discard" button press.
358    *
359    * @author <a href="mailto:rruiz@gridsystems.com">Rodrigo Ruiz Aguayo</a>
360    * @version 1.0
361    */
362   private class DiscardAction extends AbstractAction {
363     /**
364      * Default constructor.
365      */
366     public DiscardAction() {
367       super(UI.getString("btnDiscard.text"));
368     }
369 
370     /**
371      * {@inheritDoc}
372      */
373     public void actionPerformed(ActionEvent event) {
374       ConfigPanel.this.cfg.discard();
375       verifyAll(ConfigPanel.this.component);
376     }
377   }
378 
379   /**
380    * {@inheritDoc}
381    */
382   public void stateChanged(ChangeEvent e) {
383     updateFooter();
384   }
385 
386   /**
387    * Forces a verify on all components in the specified component.
388    *
389    * @param c  The component to verify
390    */
391   private void verifyAll(JComponent c) {
392     if (c.isEnabled()) {
393       InputVerifier verifier = c.getInputVerifier();
394       if (verifier != null) {
395         verifier.verify(c);
396       }
397 
398       Component[] children = c.getComponents();
399       int count = children == null ? 0 : children.length;
400       for (int i = 0; i < count; i++) {
401         if (children[i] instanceof JComponent) {
402           verifyAll((JComponent)children[i]);
403         }
404       }
405     }
406   }
407 
408   /**
409    * Shows a message telling that the configurator tree is being expanded.
410    *
411    * Shows a message telling that the configurator tree is being expanded. The
412    * previous message is stored at <code>oldMessage</code> to be shown again
413    * when the expansion finishes.
414    */
415   public void showExpansionMessage() {
416     if (this.component instanceof JLabel) {
417       JLabel jlabel = (JLabel) this.component;
418       this.oldMessage = jlabel.getText();
419       jlabel.setText(UI.getString("lblExpandTree"));
420       jlabel.validate();
421       jlabel.paintImmediately(this.getVisibleRect());
422     } else {
423       this.oldMessage = null;
424     }
425   }
426 
427   /**
428    * Hides the message showed by <code>showExpansionMessage</code>.
429    *
430    * Hides the message showed by <code>showExpansionMessage</code> and restores
431    * the message stored at <code>oldMessage</code>.
432    */
433   public void hideExpansionMessage() {
434     if ((this.oldMessage != null) && (this.component instanceof JLabel)) {
435       JLabel jlabel = (JLabel) this.component;
436       jlabel.setText(this.oldMessage);
437       jlabel.validate();
438       jlabel.paintImmediately(this.getVisibleRect());
439     }
440   }
441 }