View Javadoc

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