View Javadoc

1   package com.melloware.jukes.gui.view;
2   
3   import java.awt.Color;
4   import java.awt.Dimension;
5   import java.awt.Image;
6   import java.io.File;
7   import java.text.MessageFormat;
8   import java.util.prefs.Preferences;
9   
10  import javax.swing.JComponent;
11  
12  import org.apache.commons.logging.Log;
13  import org.apache.commons.logging.LogFactory;
14  import org.hibernate.FetchMode;
15  
16  import com.jgoodies.uif.AbstractFrame;
17  import com.jgoodies.uif.action.ActionManager;
18  import com.jgoodies.uif.application.Application;
19  import com.jgoodies.uif.util.WindowUtils;
20  import com.jgoodies.uifextras.convenience.SetupManager;
21  import com.melloware.jintellitype.HotkeyListener;
22  import com.melloware.jintellitype.IntellitypeListener;
23  import com.melloware.jintellitype.JIntellitype;
24  import com.melloware.jukes.db.HibernateDao;
25  import com.melloware.jukes.db.orm.Artist;
26  import com.melloware.jukes.db.orm.Catalog;
27  import com.melloware.jukes.file.Playlist;
28  import com.melloware.jukes.gui.tool.Actions;
29  import com.melloware.jukes.gui.tool.MainModule;
30  import com.melloware.jukes.gui.tool.Player;
31  import com.melloware.jukes.gui.tool.Resources;
32  import com.melloware.jukes.gui.view.component.SpectrumTimeAnalyzer;
33  import com.melloware.jukes.gui.view.dialogs.DiscFindDialog;
34  import com.melloware.jukes.tray.ITrayIcon;
35  import com.melloware.jukes.util.MessageUtil;
36  
37  /**
38   * The main frame of the Skeleton Pro application.
39   * It creates the menus, menu bar, tool bar and all subpanels.
40   * <p>
41   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
42   * @author Emil A. Lefkof III <info@melloware.com>
43   * @version 4.0
44   */
45  public final class MainFrame
46      extends AbstractFrame
47      implements HotkeyListener,
48                 IntellitypeListener {
49  
50      private static final Log LOG = LogFactory.getLog(MainFrame.class);
51      private static final Dimension MINIMUM_SIZE = new Dimension(800, 600);
52      private static final int WINDOWS_J = 1;
53      private ITrayIcon trayIcon;
54      private JIntellitype jintellitype;
55      private final MainMenuBuilder mainMenuBuilder;
56      private final MainModule mainModule;
57      private MainPageBuilder mainPageBuilder;
58      private Player player;
59      private final SpectrumTimeAnalyzer analyzer = new SpectrumTimeAnalyzer();
60  
61      /**
62       * Constructs an instance of the Skeleton Pro app's main frame.
63       *
64       * @param mainModule   provides bound properties and high-level models
65       */
66      public MainFrame(MainModule mainModule) {
67          super(Application.getDescription().getWindowTitle());
68          this.mainModule = mainModule;
69          this.mainMenuBuilder = new MainMenuBuilder();
70          this.player = new Player();
71  
72          // create and setup the Spectrum Analyzer
73          final int[] visualLocation = { 24, 44 };
74          final int[] visualSize = { 76, 15 };
75          analyzer.setDisplayMode(MainModule.SETTINGS.getAnalyzerMode());
76          analyzer.setSpectrumAnalyserBandCount(19);
77          analyzer.setLocation(visualLocation[0], visualLocation[1]);
78          analyzer.setSize(visualSize[0], visualSize[1]);
79          analyzer.setSpectrumAnalyserDecay(0.05f);
80          analyzer.setPeakColor(Color.BLACK);
81          analyzer.setPeakDelay((int)(SpectrumTimeAnalyzer.DEFAULT_FPS
82                                      * SpectrumTimeAnalyzer.DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY_FPS_RATIO));
83          analyzer.setBackground(this.getBackground());
84  
85          LOG.info(Application.getUserPreferences().toString());
86      }
87  
88      /**
89       * Gets the analyzer.
90       * <p>
91       * @return Returns the analyzer.
92       */
93      public SpectrumTimeAnalyzer getAnalyzer() {
94          return this.analyzer;
95      }
96  
97      /**
98       * Gets the jintellitype.
99       * <p>
100      * @return Returns the jintellitype.
101      */
102     public JIntellitype getJintellitype() {
103         return this.jintellitype;
104     }
105 
106     /**
107      * Gets the mainModule.
108      * <p>
109      * @return Returns the mainModule.
110      */
111     public MainModule getMainModule() {
112         return this.mainModule;
113     }
114 
115     /**
116      * Gets the mainPageBuilder.
117      * <p>
118      * @return Returns the mainPageBuilder.
119      */
120     public MainPageBuilder getMainPageBuilder() {
121         return this.mainPageBuilder;
122     }
123 
124     /**
125      * Gets the player.
126      * <p>
127      * @return Returns the player.
128      */
129     public Player getPlayer() {
130         return this.player;
131     }
132 
133     /**
134      * Gets the playlist.
135      * <p>
136      * @return Returns the playlist.
137      */
138     public Playlist getPlaylist() {
139         return this.getMainPageBuilder().getPlaylist();
140     }
141 
142     /**
143      * Gets the trayIcon.
144      * <p>
145      * @return Returns the trayIcon.
146      */
147     public ITrayIcon getTrayIcon() {
148         return this.trayIcon;
149     }
150     
151     /**
152      * Change the tray icon if necessary.
153      * <p>
154      * @param aImage the Image to change the icon to.
155      */
156     public void updateTrayIcon(final Image aImage) {
157        LOG.debug("Tray Icon image update");
158        if (getTrayIcon() != null) {
159           getTrayIcon().changeImage(aImage);
160        }
161     }
162 
163     /**
164     * The UI Framework needs some kind of ID to tell windows apart.
165     * Since we only have one window, it doesn't matter what it is.
166     *
167     * @return ID of the window.
168     */
169     public String getWindowID() {
170         return "JukesMainWindow";
171     }
172 
173     /**
174      * Returns the frame's minimum size. It is used by the WindowUtils
175      * to resize the window if the user has shrinked the window below
176      * this given size.
177      *
178      * @return the frame's minimum size
179      * @see com.jgoodies.swing.AbstractFrame#getWindowMinimumSize()
180      */
181     public Dimension getWindowMinimumSize() {
182         return MINIMUM_SIZE;
183     }
184 
185     /**
186      * Sets the jintellitype.
187      * <p>
188      * @param aJintellitype The jintellitype to set.
189      */
190     public void setJintellitype(JIntellitype aJintellitype) {
191         this.jintellitype = aJintellitype;
192         this.jintellitype.addHotKeyListener(this);
193         this.jintellitype.addIntellitypeListener(this);
194 
195         // assign the WINDOWS+J key to the unique id 1 for identification
196         LOG.info("Registering WINDOWS+J hotkey to show and hide Jukes");
197         jintellitype.registerHotKey(WINDOWS_J, JIntellitype.MOD_WIN, (int)'J');
198     }
199 
200     /**
201      * Sets the player.
202      * <p>
203      * @param aPlayer The player to set.
204      */
205     public void setPlayer(Player aPlayer) {
206         this.player = aPlayer;
207     }
208 
209     /**
210      * Sets the trayIcon.
211      * <p>
212      * @param aTrayIcon The trayIcon to set.
213      */
214     public void setTrayIcon(ITrayIcon aTrayIcon) {
215         this.trayIcon = aTrayIcon;
216     }
217 
218     /* (non-Javadoc)
219      * @see com.jgoodies.swing.application.AbstractMainFrame#aboutToExitApplication()
220      */
221     public void aboutToExitApplication() {
222         LOG.info("Exiting application.");
223         this.storeState();
224         this.player.stop();
225         // Force clean application exit
226         // If we will not do this the Application will continue with useless
227         // frames and windows disposal procedure which in some cases gets stuck
228         // making exit not available. As we don't have anything else to do with
229         // application we simply do clean exit here.
230         System.exit(0);
231     }
232 
233     /**
234      * Builds this <code>MainFrame</code>. Firstly, it executes the
235      * superclass behavior, then sets the menu bar, registers help sets,
236      * and finally makes the help view invisible.
237      */
238     public void build() {
239         super.build();
240         setJMenuBar(mainMenuBuilder.build());
241 
242         // load all artists and discs first so they are in the cache, slow up front hit
243         LOG.debug("START Loading all discs");
244         HibernateDao.createCriteria(Artist.class).setFetchMode("discs", FetchMode.JOIN).list();
245         LOG.debug("END Loading all discs");
246 
247         final Catalog catalog = new Catalog("");
248         mainModule.setCatalog(catalog);
249         
250         // if this is the first use look for ~/Music to load music
251         if (SetupManager.usageCount() <= 1) {
252            final File musicDir = new File("./Music");
253            if (musicDir.exists()) {
254               MainModule.SETTINGS.setStartInDirectory(musicDir);
255               new DiscFindDialog(this, MainModule.SETTINGS, true);
256            }
257         }
258 
259         // if number of uses = 30 or 100 then ask for donation
260         if ((SetupManager.usageCount() == 2) || (SetupManager.usageCount() == 30) || (SetupManager.usageCount() == 100)) {
261             String donateMsg = Resources.getString("messages.donate");
262             donateMsg = MessageFormat.format(donateMsg,
263                      new Object[] {
264                      SetupManager.usageCount()
265                  });
266             MessageUtil.showwarn(this, donateMsg);
267             ActionManager.get(Actions.HELP_DONATE_ID).actionPerformed(null);
268         }
269     }
270 
271     /**
272      * When a registered hotkey is received this method is invoked.
273      * <p>
274      * @param aHotKeyId the unique HotKeyId combination that was pressed
275      */
276     public void onHotKey(int aHotKeyId) {
277         switch (aHotKeyId) {
278             case WINDOWS_J: {
279                 LOG.debug("WINDOWS+J hotkey message received ");
280                 if (this.isVisible()) {
281                     trayIcon.hideWindow();
282                 } else {
283                     trayIcon.showWindow();
284                 }
285                 break;
286             }
287             default: {
288                 if (LOG.isDebugEnabled()) {
289                     LOG.debug("Undefined HOTKEY message caught " + Integer.toString(aHotKeyId));
290                 }
291                 break;
292             }
293         }
294     }
295 
296     /**
297      * When any Intellitype commands are received.
298      * <p>
299      * @param aCommandId the constant Intellitype command received.
300      */
301     public void onIntellitype(int aCommandId) {
302         switch (aCommandId) {
303             case JIntellitype.APPCOMMAND_MEDIA_NEXTTRACK: {
304                 LOG.debug("MEDIA_NEXTTRACK message received ");
305                 ActionManager.get(Actions.PLAYER_NEXT_ID).actionPerformed(null);
306                 break;
307             }
308             case JIntellitype.APPCOMMAND_MEDIA_PLAY_PAUSE: {
309                 LOG.debug("MEDIA_PLAY_PAUSE message received ");
310                 ActionManager.get(Actions.PLAYER_PAUSE_ID).actionPerformed(null);
311                 break;
312             }
313             case JIntellitype.APPCOMMAND_MEDIA_PREVIOUSTRACK: {
314                 LOG.debug("MEDIA_PREVIOUSTRACK message received ");
315                 ActionManager.get(Actions.PLAYER_PREVIOUS_ID).actionPerformed(null);
316                 break;
317             }
318             case JIntellitype.APPCOMMAND_MEDIA_STOP: {
319                 LOG.debug("MEDIA_STOP message received ");
320                 ActionManager.get(Actions.PLAYER_STOP_ID).actionPerformed(null);
321                 break;
322             }
323             default: {
324                 if (LOG.isDebugEnabled()) {
325                     LOG.debug("Undefined INTELLITYPE message caught " + Integer.toString(aCommandId));
326                 }
327                 break;
328             }
329         }
330 
331     }
332 
333     /**
334      * Stores the frame's state in the user preferences.<p>
335      */
336     public void storeState() {
337         super.storeState();
338         Preferences userPrefs = Application.getUserPreferences();
339         WindowUtils.storeBounds(userPrefs, this);
340         WindowUtils.storeState(userPrefs, this);
341         mainModule.storeState();
342         mainPageBuilder().storeIn(userPrefs);
343     }
344 
345     /**
346      * Builds this frame's content pane.
347      */
348     protected JComponent buildContentPane() {
349         mainPageBuilder = new MainPageBuilder(mainModule);
350         player.getPlayer().addBasicPlayerListener(mainPageBuilder);
351         return mainPageBuilder.build();
352 
353     }
354 
355     /* (non-Javadoc)
356      * @see com.jgoodies.uif.AbstractFrame#configureCloseOperation()
357      */
358     @Override 
359     protected void configureCloseOperation() {
360         setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
361         addWindowListener(new java.awt.event.WindowAdapter() {
362                 public void windowClosing(java.awt.event.WindowEvent evt) {
363                     LOG.debug("Window Close");
364                     if (trayIcon == null) {
365                         aboutToExitApplication();
366                     } else {
367                         trayIcon.hideWindow();
368                     }
369                 }
370             });
371     }
372 
373     /**
374      * Restores the frame's state from the user preferences.<p>
375      */
376     protected void restoreState() {
377         super.restoreState();
378         mainModule.restoreState();
379         Preferences userPrefs = Application.getUserPreferences();
380         mainPageBuilder().restoreFrom(userPrefs);
381     }
382 
383     /*
384      * Returns the main page.
385      */
386     private MainPageBuilder mainPageBuilder() {
387         return mainPageBuilder;
388     }
389 
390 }