View Javadoc

1   package com.melloware.jukes.gui.view.editor;
2   
3   import java.awt.Color;
4   import java.awt.Component;
5   import java.awt.Container;
6   import java.awt.Cursor;
7   import java.awt.Font;
8   import java.awt.KeyboardFocusManager;
9   import java.beans.PropertyChangeEvent;
10  import java.beans.PropertyChangeListener;
11  import java.text.SimpleDateFormat;
12  import java.util.ArrayList;
13  import java.util.Collection;
14  import java.util.Iterator;
15  import java.util.Locale;
16  
17  import javax.swing.BorderFactory;
18  import javax.swing.Icon;
19  import javax.swing.JComboBox;
20  import javax.swing.JComponent;
21  import javax.swing.JLabel;
22  import javax.swing.JPanel;
23  import javax.swing.JTextArea;
24  import javax.swing.JTextField;
25  import javax.swing.JToolBar;
26  import javax.swing.ProgressMonitor;
27  import javax.swing.Timer;
28  import javax.swing.border.Border;
29  import javax.swing.text.JTextComponent;
30  
31  import com.jgoodies.forms.builder.PanelBuilder;
32  import com.jgoodies.forms.layout.CellConstraints;
33  import com.jgoodies.forms.layout.FormLayout;
34  import com.jgoodies.uif.action.ActionManager;
35  import com.jgoodies.uif.application.Application;
36  import com.jgoodies.uif.component.ToolBarButton;
37  import com.jgoodies.uifextras.util.UIFactory;
38  import com.jgoodies.validation.view.ValidationComponentUtils;
39  import com.jgoodies.validation.view.ValidationResultViewFactory;
40  import com.melloware.jukes.db.orm.AbstractJukesObject;
41  import com.melloware.jukes.gui.tool.Actions;
42  import com.melloware.jukes.gui.tool.MainModule;
43  import com.melloware.jukes.gui.tool.Resources;
44  import com.melloware.jukes.gui.tool.Settings;
45  import com.melloware.jukes.gui.view.MainFrame;
46  import com.melloware.jukes.gui.view.tasks.UpdateTagsTask;
47  import com.melloware.jukes.gui.view.validation.AbstractValidationModel;
48  import com.melloware.jukes.util.MessageUtil;
49  
50  /**
51   * The abstract superclass of all <code>Editor</code> implementations.
52   * <p>
53   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
54   * @author Emil A. Lefkof III <info@melloware.com>
55   * @version 4.0
56   * 2010 AZ Development
57   */
58  @SuppressWarnings("PMD")
59  abstract public class AbstractEditor
60      extends JPanel
61      implements Editor {
62  
63      protected static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MMM dd yyyy h:mm a", Locale.US);
64      protected static final Font FONT_ENABLED = new JLabel().getFont();
65      protected static final Font FONT_DISABLED = FONT_ENABLED.deriveFont(FONT_ENABLED.getStyle() ^ Font.BOLD);
66      protected static final Border BORDER_ENABLED = new JTextField().getBorder();
67      protected static final Border BORDER_DISABLED = BorderFactory.createEmptyBorder();
68      protected static final String HINT = "HINT";
69      protected AbstractValidationModel validationModel;
70      protected final Icon icon;
71      protected JComponent hintAreaPane;
72      protected JLabel createdByLabel;
73      protected JLabel createdDateLabel;
74      protected JLabel hintLabel;
75      protected JLabel modifiedByLabel;
76      protected JLabel modifiedDateLabel;
77      protected JTextArea hintArea;
78      protected ProgressMonitor progressMonitor;
79      protected final String titlePrefix;
80      protected Timer timer;
81      protected UpdateTagsTask task;
82      private Object model;
83  
84      /**
85       * Constructs an <code>AbstractEditor</code> with the specified icon.
86       */
87      public AbstractEditor(Icon icon) {
88          this(icon, "");
89      }
90  
91      /**
92       * Constructs an <code>AbstractEditor</code> with the specified title
93       * prefix.
94       */
95      public AbstractEditor(String titlePrefix) {
96          this(null, titlePrefix);
97      }
98  
99      /**
100      * Constructs an <code>AbstractEditor</code> with the specified
101      * <code>Icon</code> and title prefix.
102      */
103     public AbstractEditor(Icon icon, String titlePrefix) {
104         this.icon = icon;
105         this.titlePrefix = titlePrefix;
106         build();
107     }
108 
109     /**
110      * Returns the class used to register this instance in the UpdateManager.
111      */
112     abstract public Class getDomainClass();
113 
114     /**
115      * Answers this <code>Editor</code>'s <code>JToolBar</code>.
116      * The default implementation specifies that no tool bar is used.
117      */
118     public JToolBar getHeaderToolBar() {
119         return null;
120     }
121 
122     /**
123      * Answers this <code>Editor</code>'s <code>Icon</code>.
124      */
125     public Icon getIcon() {
126         return icon;
127     }
128 
129     /**
130      * Returns this editor's underlying model.
131      */
132     public Object getModel() {
133         return model;
134     }
135 
136     /**
137      * Returns this editor's underlying model as an AbstractJukesObject.
138      */
139     public AbstractJukesObject getOrmObject() {
140         return (AbstractJukesObject)model;
141     }
142 
143     /**
144      * Answers this <code>Editor</code>'s title.
145      */
146     public String getTitle() {
147         return titlePrefix + ' ' + getTitleSuffix();
148     }
149 
150     /**
151      * Answers this <code>Editor</code>'s <code>JToolBar</code>.
152      * The default implementation specifies that no tool bar is used.
153      */
154     public JToolBar getToolBar() {
155         return null;
156     }
157 
158     /**
159      * Sets a new model. Does nothing if the old and new model are the same.
160      * If the model changes, invokes <code>#updateView</code>.
161      * AZ 2010: compare object Id also.
162      * @param newModel   the model to set
163      */
164     public void setModel(Object newModel) {
165         Object oldModel = getModel();
166         AbstractJukesObject newObject, oldObject;//AZ
167     	oldObject = (AbstractJukesObject) oldModel;
168     	newObject = (AbstractJukesObject) newModel;
169     	
170     	if ((oldModel != null) && (oldModel.equals(newModel)) && (oldObject.getId() == newObject.getId()) ) {
171         	this.lock();
172             return;
173         }
174         model = newModel;
175         updateView();
176         this.lock();
177     }
178 
179     /**
180      * Activates this viewer.
181      */
182     public void activate() {
183         // Do nothing by default; subclasses may override.
184     }
185 
186     /**
187      * Commits any changes made to this editor.
188      */
189     public void commit() {
190     	this.lock();
191     }
192     
193     /**
194      * Unlocks this viewer and updates all text fields and buttons.
195      */
196     public void unlock() {
197     	ActionManager.get(Actions.UNLOCK_ID).setEnabled(false);
198     	validationModel.updateButtonState(true);
199         final Collection components = getAllComponents(this);
200     	
201     	for (Iterator iter = components.iterator(); iter.hasNext();) {
202 			final Component component = (Component) iter.next();
203 			if (component instanceof JTextComponent) {
204 				final JTextComponent field = (JTextComponent)component;
205 				//skip if this is the hint area
206 				if (field.getClientProperty(HINT) != null) {
207 					continue;
208 				}
209 				field.setEnabled(true);
210 				field.setBorder(BORDER_ENABLED);
211 				field.setOpaque(true);
212 				field.setFont(FONT_ENABLED);
213 			} else if (component instanceof JComboBox) {
214 				final JComboBox field = (JComboBox)component;
215 				field.setEnabled(true);
216 				field.setVisible(true);
217 			} else if (component instanceof JLabel) {
218 				final JLabel field = (JLabel)component;
219 				if ("GENRE".equalsIgnoreCase(field.getName())) {
220 					field.setVisible(false);
221 				}
222 			} else if (component instanceof ToolBarButton) {
223 				final ToolBarButton button = (ToolBarButton)component;
224 				button.setVisible(true);
225 			}
226 		}
227     	
228     	this.updateUI();
229     }
230     
231     /**
232      * Locks this viewer and updates all text fields and buttons.
233      */
234     public void lock() {
235     	ActionManager.get(Actions.UNLOCK_ID).setEnabled(true);
236     	validationModel.updateButtonState(false);
237     	final Collection components = getAllComponents(this);
238     	
239     	for (Iterator iter = components.iterator(); iter.hasNext();) {
240 			final Component component = (Component) iter.next();
241 			if (component instanceof JTextComponent) {
242 				final JTextComponent field = (JTextComponent)component;
243 				//skip if this is the hint area
244 				if (field.getClientProperty(HINT) != null) {
245 					continue;
246 				}
247 				
248 				field.setEnabled(false);
249 				field.setDisabledTextColor(Color.BLACK);
250 				field.setBorder(BORDER_DISABLED);
251 				field.setOpaque(false);
252 				field.setFont(FONT_DISABLED);
253 			}  else if (component instanceof JComboBox) {
254 				final JComboBox field = (JComboBox)component;
255 				field.setEnabled(false);
256 				field.setVisible(false);
257 			} else if (component instanceof JLabel) {
258 				final JLabel field = (JLabel)component;
259 				if ("GENRE".equalsIgnoreCase(field.getName())) {
260 					final String text = field.getText().trim();
261 					field.setText("  " + text);
262 					field.setVisible(true);
263 					field.setFont(FONT_DISABLED);
264 				}
265 			} else if (component instanceof ToolBarButton) {
266 				final ToolBarButton button = (ToolBarButton)component;
267 				button.setVisible(false);
268 			}
269 			
270 		}
271     	
272     	this.updateUI();
273     }
274     
275     /**
276      * Recurse through a component and build a list of all the components 
277      * underneath of it.
278      * <p>
279      * @param aTop the top container
280      * @return the collection of all components under the container
281      */
282     private Collection getAllComponents(Container aTop) {
283     	final Component[] comp = aTop.getComponents();
284     	final ArrayList list = new ArrayList();
285     	
286     	if (comp.length == 0) {
287 			return list;
288 		}
289     	
290     	for (int i = 0; i < comp.length; i++) {
291     		list.add(comp[i]);
292     		if (comp[i] instanceof Container) {
293 				list.addAll(getAllComponents((Container)comp[i]));
294 			}
295 		}
296     	return list;
297     }
298 
299     /**
300      * Deactivates this viewer.
301      */
302     public void deactivate() {
303     	//AZ: Do not refresh the object if no changes were made
304     	/**
305         // refresh the object if there are errors when leaving
306         if (this.hasErrors()) {
307             this.rollback();
308         } else {**/
309     	
310         	//if dirty prompt the user to save changes
311             if (validationModel.isDirty()) {
312                 if (MessageUtil.promptYesNo(getMainFrame(), Resources.getString("messages.promptSaveChanges"))) {
313                     this.commit();
314                 } else {
315                 	//if they don't want to save then refresh this object
316                 	this.rollback();
317                 }
318             }
319         //AZ }
320     }
321 
322     /**
323      * Delete the object and its descendants contained by this editor.
324      */
325     public void delete() {
326         // Do nothing by default; subclasses may override.
327     }
328 
329     /**
330      * Tries to find a new cover for the disc.
331      */
332     public void findCover() {
333         // Do nothing by default; subclasses may override.
334     }
335 
336     /**
337      * Renames any files this editor owns.
338      */
339     public void renameFiles() {
340         // Do nothing by default; subclasses may override.
341     }
342 
343     /**
344      * Rollback any changes made to this editor
345      */
346     public void rollback() {
347         // Do nothing by default; subclasses may override.
348     	this.lock();
349     }
350 
351     /**
352      * Perform the web service search to look for disc info.
353      */
354     public void webSearch() {
355         // Do nothing by default; subclasses may override.
356     }
357     
358     /** AZ
359      * Perform the web service search to look for disc info.
360      */
361     public void freeDBSearch() {
362         // Do nothing by default; subclasses may override.
363     }
364 
365     /**
366      * Returns a suffix for this editor's title.
367      *
368      * @return a suffix for this editor's title
369      */
370     abstract protected String getTitleSuffix();
371 
372     /**
373      * Builds this panel.
374      */
375     abstract protected void build();
376 
377     /**
378      * Writes the view contents to the underlying model.
379      */
380     abstract protected void updateModel();
381 
382     /**
383      * Reads the view contents from the underlying model.
384      */
385     abstract protected void updateView();
386 
387     /**
388      * Gets the MainFrame for the application.
389      * <p>
390      * @return the MainFrame object
391      */
392     protected MainFrame getMainFrame() {
393         return (MainFrame)Application.getDefaultParentFrame();
394     }
395 
396     /**
397      * Gets the MainModule for the application.
398      * <p>
399      * @return the MainModule object
400      */
401     protected MainModule getMainModule() {
402         return getMainFrame().getMainModule();
403     }
404 
405     /**
406      * Gets the settings for the application.
407      * <p>
408      * @return the Settings object of user defined settings
409      */
410     protected Settings getSettings() {
411         return MainModule.SETTINGS;
412     }
413 
414     /**
415      * Gets the validationModel.
416      * <p>
417      * @return Returns the validationModel.
418      */
419     protected AbstractValidationModel getValidationModel() {
420         return this.validationModel;
421     }
422 
423     /**
424      * Sets the cursor to hourglass for true and default for false.  Used for
425      * long operations such as saves.
426      * <p>
427      * @param aBusy true for busy cursor, false for default
428      */
429     protected void setBusyCursor(boolean aBusy) {
430         if (aBusy) {
431             getMainFrame().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
432         } else {
433             getMainFrame().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
434         }
435     }
436 
437     /**
438      * Builds the audit information panel which displays the created by, and
439      * modified by info of a ORM record.
440      * <p>
441      * @return the panel to display the audit info
442      */
443     protected JComponent buildAuditInfoPanel() {
444         // create the labels as BOLD
445         createdDateLabel = UIFactory.createBoldLabel("");
446         createdByLabel = UIFactory.createBoldLabel("");
447         modifiedDateLabel = UIFactory.createBoldLabel("");
448         modifiedByLabel = UIFactory.createBoldLabel("");
449 
450         FormLayout layout = new FormLayout("right:max(40dlu;pref), 3dlu, 100dlu, 7dlu, "
451                                            + "right:max(40dlu;pref), 3dlu, 100dlu", "p, 3dlu, p, 3dlu, p, 3dlu, p");
452         PanelBuilder builder = new PanelBuilder(layout);
453         CellConstraints cc = new CellConstraints();
454 
455         int row = 1;
456         builder.addLabel(Resources.getString("label.createdby") + ": ", cc.xy(1, row));
457         builder.add(createdByLabel, cc.xy(3, row));
458         builder.addLabel(Resources.getString("label.createddate") + ": ", cc.xy(5, row));
459         builder.add(createdDateLabel, cc.xy(7, row++));
460         row++;
461 
462         builder.addLabel(Resources.getString("label.modifiedby") + ": ", cc.xy(1, row));
463         builder.add(modifiedByLabel, cc.xy(3, row));
464         builder.addLabel(Resources.getString("label.modifieddate") + ": ", cc.xy(5, row));
465         builder.add(modifiedDateLabel, cc.xy(7, row++));
466         row++;
467         
468         JComponent component = builder.getPanel();
469         component.setVisible(this.getSettings().isAuditInfo());
470 
471         return component;
472     }
473 
474     /**
475      * Builds the hint area panel where validation hints are displayed.
476      * <p>
477      * @return the panel to display the hints
478      */
479     protected JComponent buildHintAreaPane() {
480         hintLabel = new JLabel(ValidationResultViewFactory.getInfoIcon());
481         hintArea = new JTextArea(1, 38);
482         hintArea.putClientProperty(HINT, HINT);
483         hintArea.setEditable(false);
484         hintArea.setOpaque(false);
485 
486         FormLayout layout = new FormLayout("pref, 2dlu, default", "pref");
487         PanelBuilder builder = new PanelBuilder(layout);
488         CellConstraints cc = new CellConstraints();
489         builder.add(hintLabel, cc.xy(1, 1));
490         builder.add(hintArea, cc.xy(3, 1));
491 
492         hintAreaPane = builder.getPanel();
493         hintAreaPane.setVisible(false);
494         return hintAreaPane;
495     }
496 
497     /**
498      * Does this editor pass validation right now. True if so false otherwise.
499      * <p>
500      * @return true if passes validation
501      */
502     protected boolean hasErrors() {
503         return getValidationModel().getValidationResultModel().getResult().hasErrors();
504     }
505 
506     /**
507      * Initializes any event handling.
508      */
509     protected void initEventHandling() {
510         KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(new FocusChangeHandler());
511     }
512 
513 
514     /**
515      * Displays an input hint for components that get the focus permanently.
516      */
517     private final class FocusChangeHandler
518         implements PropertyChangeListener {
519 
520         public void propertyChange(PropertyChangeEvent evt) {
521             String propertyName = evt.getPropertyName();
522             if (!"permanentFocusOwner".equals(propertyName)) {
523                 return;
524             }
525             Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
526             String focusHint = (focusOwner instanceof JComponent)
527                                ? (String)ValidationComponentUtils.getInputHint((JComponent)focusOwner) : null;
528                         
529             hintArea.setText(focusHint);
530             hintAreaPane.setVisible(focusHint != null);
531         }
532     }
533 
534 }