1 package com.melloware.jukes.gui.view;
2
3 import java.awt.BorderLayout;
4 import java.awt.Dimension;
5 import java.awt.EventQueue;
6 import java.awt.FlowLayout;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.beans.PropertyChangeEvent;
10 import java.beans.PropertyChangeListener;
11 import java.util.Map;
12 import java.util.prefs.Preferences;
13
14 import javax.sound.sampled.SourceDataLine;
15 import javax.swing.BorderFactory;
16 import javax.swing.JComponent;
17 import javax.swing.JLabel;
18 import javax.swing.JPanel;
19 import javax.swing.JProgressBar;
20 import javax.swing.JSplitPane;
21 import javax.swing.JToolBar;
22 import javax.swing.Timer;
23
24 import javazoom.jlgui.basicplayer.BasicController;
25 import javazoom.jlgui.basicplayer.BasicPlayer;
26 import javazoom.jlgui.basicplayer.BasicPlayerEvent;
27 import javazoom.jlgui.basicplayer.BasicPlayerListener;
28
29 import org.apache.commons.lang.StringUtils;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.log4j.Level;
33 import org.apache.log4j.Logger;
34
35 import com.jgoodies.looks.LookUtils;
36 import com.jgoodies.uif.action.ActionManager;
37 import com.jgoodies.uif.application.Application;
38 import com.jgoodies.uif.panel.SimpleInternalFrame;
39 import com.jgoodies.uif.util.ComponentTreeUtils;
40 import com.jgoodies.uifextras.util.ActionLabel;
41 import com.jgoodies.uifextras.util.UIFactory;
42 import com.melloware.jukes.db.HibernateUtil;
43 import com.melloware.jukes.db.orm.Track;
44 import com.melloware.jukes.file.Playlist;
45 import com.melloware.jukes.file.image.ImageFactory;
46 import com.melloware.jukes.gui.tool.Actions;
47 import com.melloware.jukes.gui.tool.MainModule;
48 import com.melloware.jukes.gui.tool.Resources;
49 import com.melloware.jukes.gui.tool.Settings;
50 import com.melloware.jukes.gui.view.component.SpectrumTimeAnalyzer;
51 import com.melloware.jukes.gui.view.editor.ArtistEditor;
52 import com.melloware.jukes.gui.view.editor.DiscEditor;
53 import com.melloware.jukes.gui.view.editor.EditorPanel;
54 import com.melloware.jukes.gui.view.editor.EmptyPanel;
55 import com.melloware.jukes.gui.view.editor.TrackEditor;
56 import com.melloware.jukes.gui.view.editor.WelcomePanel;
57 import com.melloware.jukes.util.TimeSpan;
58
59
60
61
62
63
64
65
66
67
68 public final class MainPageBuilder
69 implements BasicPlayerListener {
70
71 private static final Log LOG = LogFactory.getLog(MainPageBuilder.class);
72 private static final Dimension PREFERRED_SIZE = LookUtils.IS_LOW_RESOLUTION ? new Dimension(800, 600)
73 : new Dimension(1024, 768);
74 private static final String MAIN_DIVIDER_LOCATION_KEY = "mainDividerLocation";
75 private static final String VIEWER_DIVIDER_LOCATION_KEY = "viewerDividerLocation";
76 private static final String NAVIGATOR_DIVIDER_LOCATION_KEY = "navigatorDividerLocation";
77 private static final String NAVIGATOR_VISIBLE_KEY = "navigatorVisible";
78 private static final String PLAYLIST_VISIBLE_KEY = "playlistVisible";
79 private static final TimeSpan TIMESPAN = new TimeSpan(0);
80 private ActionLabel trackField;
81 private EditorPanel editorPanel;
82 private FilterPanel filterPanel;
83 private JLabel bitrateField;
84 private JLabel durationField;
85 private JPanel mainPage;
86 private JProgressBar elapsedBar;
87 private JSplitPane editorPlaylistSplitPane;
88 private JSplitPane mainSplitPane;
89 private JSplitPane navigatorSplitPane;
90 private long elapsedTime;
91 private final MainFrame mainframe;
92 private final MainModule mainModule;
93 private Map audioInfo;
94 private NavigationPanelBuilder navigationPanel;
95 private final Playlist playlist;
96 private PlaylistPanel playlistPanel;
97 private SimpleInternalFrame navigator;
98 private Timer timer;
99
100
101
102
103
104
105 MainPageBuilder(MainModule mainModule) {
106 this.mainModule = mainModule;
107 this.playlist = new Playlist();
108 this.mainModule.addPropertyChangeListener(this.playlist);
109 this.mainframe = (MainFrame)Application.getDefaultParentFrame();
110 MainModule.SETTINGS.addPropertyChangeListener(new PresentationSettingsChangeHandler());
111 }
112
113
114
115
116
117
118 public Playlist getPlaylist() {
119 return this.playlist;
120 }
121
122
123
124
125 public void setController(BasicController controller) {
126 if (LOG.isDebugEnabled()) {
127 LOG.debug("Controller " + controller);
128 }
129 }
130
131
132
133
134 public void setFilterVisible(boolean b) {
135 if (isFilterVisible() == b) {
136 return;
137 }
138 if (b) {
139 navigatorSplitPane.setTopComponent(navigator);
140 navigatorSplitPane.setBottomComponent(filterPanel);
141 int navigatorDividerLocation = Application.getUserPreferences().getInt(NAVIGATOR_DIVIDER_LOCATION_KEY, -1);
142 if ((navigatorDividerLocation > 50) && (navigatorDividerLocation < (mainPage.getHeight() - 50))) {
143 navigatorSplitPane.setDividerLocation(navigatorDividerLocation);
144 }
145 } else {
146 navigatorSplitPane.setTopComponent(navigator);
147 navigatorSplitPane.setBottomComponent(null);
148 }
149 }
150
151
152
153
154
155
156 public void setPlaylistVisible(boolean visible) {
157 if (isPlaylistVisible() == visible) {
158 return;
159 }
160 if (visible) {
161 editorPlaylistSplitPane.setTopComponent(editorPanel);
162 editorPlaylistSplitPane.setBottomComponent(playlistPanel);
163 int verticalDividerLocation = Application.getUserPreferences().getInt(VIEWER_DIVIDER_LOCATION_KEY, -1);
164 if ((verticalDividerLocation > 100) && (verticalDividerLocation < (mainPage.getHeight() - 50))) {
165 editorPlaylistSplitPane.setDividerLocation(verticalDividerLocation);
166 }
167 } else {
168 editorPlaylistSplitPane.setTopComponent(editorPanel);
169 editorPlaylistSplitPane.setBottomComponent(null);
170 }
171 }
172
173
174
175
176 public boolean isFilterVisible() {
177 return navigatorSplitPane.getBottomComponent() != null;
178 }
179
180
181
182
183 public boolean isPlaylistVisible() {
184 return editorPlaylistSplitPane.getBottomComponent() != null;
185 }
186
187
188
189
190 public void opened(Object stream, Map properties) {
191 audioInfo = properties;
192
193
194
195
196
197
198
199
200
201
202
203 final Track track = playlist.getCurrentTrack();
204 final String title = track.getName();
205 final String disc = track.getDisc().getName();
206 final String artist = track.getDisc().getArtist().getName();
207 final Long duration = Long.valueOf(track.getDuration() * 1000);
208 final String bitrate = (track.getBitrate().intValue()) + " kbps";
209 final Runnable updateList = new Runnable() {
210 public void run() {
211 trackField.setText(artist + " - " + disc + " - " + title);
212 bitrateField.setText(bitrate);
213 final int ms = (int)(duration.longValue());
214 TIMESPAN.setTime(duration.longValue());
215 durationField.setText(TIMESPAN.getMusicDuration());
216 elapsedBar.setValue(0);
217 elapsedBar.setMaximum(ms);
218
219
220 if (mainframe.getTrayIcon() != null) {
221 mainframe.getTrayIcon().setToolTip(artist + " - " + title);
222 }
223
224
225 refreshUI();
226 }
227 };
228 EventQueue.invokeLater(updateList);
229
230
231 }
232
233
234
235
236 public void progress(int bytesread, long microseconds, byte[] pcmdata, Map properties) {
237 mainframe.getAnalyzer().writeDSP(pcmdata);
238 this.setElapsedTime(microseconds);
239 }
240
241
242
243
244 public void refreshUI() {
245
246 navigator.updateUI();
247 editorPanel.updateUI();
248 }
249
250
251
252
253 public void restoreFrom(Preferences userPrefs) {
254 int mainDividerLocation = userPrefs.getInt(MAIN_DIVIDER_LOCATION_KEY, -1);
255 int verticalDividerLocation = userPrefs.getInt(VIEWER_DIVIDER_LOCATION_KEY, -1);
256 int navigatorDividerLocation = userPrefs.getInt(NAVIGATOR_DIVIDER_LOCATION_KEY, -1);
257 setPlaylistVisible(userPrefs.getBoolean(PLAYLIST_VISIBLE_KEY, true));
258 setFilterVisible(userPrefs.getBoolean(NAVIGATOR_VISIBLE_KEY, true));
259
260 if ((mainDividerLocation > 100) && (mainDividerLocation < (mainPage.getWidth() - 50))) {
261 mainSplitPane.setDividerLocation(mainDividerLocation);
262 }
263 if ((verticalDividerLocation > 100) && (verticalDividerLocation < (mainPage.getHeight() - 50))) {
264 editorPlaylistSplitPane.setDividerLocation(verticalDividerLocation);
265 }
266 if ((navigatorDividerLocation > 50) && (navigatorDividerLocation < (mainPage.getHeight() - 50))) {
267 navigatorSplitPane.setDividerLocation(navigatorDividerLocation);
268 }
269 }
270
271
272
273
274 public void stateUpdated(BasicPlayerEvent event) {
275 if (LOG.isDebugEnabled()) {
276 LOG.debug("stateUpdated : " + event.toString());
277 }
278 final SpectrumTimeAnalyzer analyzer = mainframe.getAnalyzer();
279 switch (event.getCode()) {
280 case BasicPlayerEvent.PLAYING: {
281 break;
282 }
283 case BasicPlayerEvent.OPENED:
284 case BasicPlayerEvent.RESUMED: {
285 mainframe.updateTrayIcon(ImageFactory.ICO_TRAYPLAY.getImage());
286 if ((audioInfo.containsKey("basicplayer.sourcedataline")) && (analyzer != null)) {
287 analyzer.setupDSP((SourceDataLine)audioInfo.get("basicplayer.sourcedataline"));
288 analyzer.startDSP((SourceDataLine)audioInfo.get("basicplayer.sourcedataline"));
289 }
290 timer.start();
291 break;
292 }
293 case BasicPlayerEvent.PAUSED: {
294 timer.stop();
295 mainframe.updateTrayIcon(ImageFactory.ICO_TRAYPAUSE.getImage());
296 break;
297 }
298 case BasicPlayerEvent.STOPPED: {
299 timer.stop();
300 mainframe.updateTrayIcon(ImageFactory.ICO_TRAYSTOP.getImage());
301 if (analyzer != null) {
302 analyzer.stopDSP();
303 analyzer.repaint();
304 }
305 final Runnable updateList = new Runnable() {
306 public void run() {
307 elapsedBar.setValue(0);
308 TIMESPAN.setTime(0);
309 elapsedBar.setString(TIMESPAN.getMusicDuration());
310 }
311 };
312 EventQueue.invokeLater(updateList);
313 break;
314 }
315 case BasicPlayerEvent.EOM: {
316 timer.stop();
317 final Track track = (Track)getPlaylist().getNext();
318 if (track != null) {
319 mainframe.getPlayer().play(track.getTrackUrl());
320 }
321 break;
322 }
323 default: {
324 break;
325 }
326 }
327 }
328
329
330
331
332
333
334 public void storeIn(Preferences userPrefs) {
335 if (isPlaylistVisible()) {
336 int verticalDividerLocation = editorPlaylistSplitPane.getDividerLocation();
337 userPrefs.putInt(VIEWER_DIVIDER_LOCATION_KEY, verticalDividerLocation);
338 }
339 if (isFilterVisible()) {
340 int navigatorDividerLocation = navigatorSplitPane.getDividerLocation();
341 userPrefs.putInt(NAVIGATOR_DIVIDER_LOCATION_KEY, navigatorDividerLocation);
342 }
343 int mainDividerLocation = mainSplitPane.getDividerLocation();
344 userPrefs.putInt(MAIN_DIVIDER_LOCATION_KEY, mainDividerLocation);
345 userPrefs.putBoolean(NAVIGATOR_VISIBLE_KEY, isFilterVisible());
346 userPrefs.putBoolean(PLAYLIST_VISIBLE_KEY, isPlaylistVisible());
347 }
348
349
350
351
352
353 JComponent build() {
354 initComponents();
355
356 mainPage = new RefreshedPanel();
357 mainPage.setLayout(new BorderLayout());
358 mainPage.add(new MainToolBarBuilder().build(), BorderLayout.NORTH);
359 mainPage.add(buildMainSplitPane(), BorderLayout.CENTER);
360 mainPage.add(buildStatusBar(), BorderLayout.SOUTH);
361 mainPage.setPreferredSize(PREFERRED_SIZE);
362
363 return mainPage;
364 }
365
366
367
368
369
370 private JComponent buildEditorHelpPanel() {
371 editorPlaylistSplitPane = UIFactory.createStrippedSplitPane(JSplitPane.VERTICAL_SPLIT, buildEditorPanel(),
372 playlistPanel, 0.667);
373 return editorPlaylistSplitPane;
374 }
375
376
377
378
379 private EditorPanel buildEditorPanel() {
380 WelcomePanel welcomePanel = new WelcomePanel();
381
382 editorPanel.addEditor(welcomePanel);
383 editorPanel.addEditor(new EmptyPanel());
384 editorPanel.addEditor(new ArtistEditor());
385 editorPanel.addEditor(new DiscEditor());
386 editorPanel.addEditor(new TrackEditor());
387
388 editorPanel.setActiveEditor(welcomePanel);
389 return editorPanel;
390 }
391
392
393
394
395
396 private JComponent buildFilterPanel() {
397 return UIFactory.createStrippedSplitPane(JSplitPane.VERTICAL_SPLIT, navigator, filterPanel, 0.64);
398 }
399
400
401
402
403
404 private JComponent buildMainSplitPane() {
405 navigatorSplitPane = (JSplitPane)buildFilterPanel();
406 mainSplitPane = UIFactory.createStrippedSplitPane(JSplitPane.HORIZONTAL_SPLIT, navigatorSplitPane,
407 buildEditorHelpPanel(), 0.25);
408 mainSplitPane.setBorder(BorderFactory.createEmptyBorder(6, 4, 0, 4));
409 return mainSplitPane;
410 }
411
412
413
414
415 private JPanel buildStatusBar() {
416 final JPanel statusPanel = new JPanel(new BorderLayout());
417 final JPanel infoPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
418 final JToolBar toolbar = MainToolBarBuilder.buildPlayerToolBar();
419 toolbar.setBorder(BorderFactory.createEmptyBorder());
420 infoPanel.setBorder(BorderFactory.createEmptyBorder());
421 statusPanel.add(toolbar, BorderLayout.WEST);
422 statusPanel.add(infoPanel, BorderLayout.EAST);
423 statusPanel.setBorder(BorderFactory.createEmptyBorder());
424 infoPanel.add(trackField);
425 trackField.setBorder(BorderFactory.createEmptyBorder());
426 infoPanel.add(bitrateField);
427 bitrateField.setBorder(BorderFactory.createEmptyBorder());
428 infoPanel.add(durationField);
429 durationField.setBorder(BorderFactory.createEmptyBorder());
430 infoPanel.add(elapsedBar);
431 elapsedBar.setBorder(BorderFactory.createEmptyBorder());
432 return statusPanel;
433 }
434
435
436
437
438 private void initComponents() {
439 navigator = new SimpleInternalFrame(Resources.getString("navigator.label"));
440 navigator.setFrameIcon(Resources.NAVIGATOR_ICON);
441 navigationPanel = new NavigationPanelBuilder(mainModule);
442 navigator.setContent(navigationPanel.build());
443 navigator.setSelected(true);
444 navigator.setMinimumSize(new Dimension(100, 100));
445 navigator.setPreferredSize(new Dimension(160, 200));
446 navigator.setFrameIcon(Resources.NAVIGATOR_ICON);
447
448 filterPanel = new FilterPanel(MainModule.SETTINGS);
449 filterPanel.setSelected(true);
450 filterPanel.setMinimumSize(new Dimension(100, 45));
451 filterPanel.setPreferredSize(new Dimension(100, 45));
452
453 editorPanel = new EditorPanel(mainModule);
454 editorPanel.setMinimumSize(new Dimension(200, 100));
455 editorPanel.setPreferredSize(new Dimension(400, 200));
456
457 playlistPanel = new PlaylistPanel(this.playlist);
458 playlistPanel.setMinimumSize(new Dimension(300, 100));
459 playlistPanel.setPreferredSize(new Dimension(300, 100));
460
461 trackField = new ActionLabel("");
462 trackField.addActionListener(new ActionListener() {
463
464
465 public void actionPerformed(ActionEvent event) {
466 LOG.debug("Track clicked");
467 if (playlist.getCurrentTrack() != null) {
468 mainModule.selectNodeInTree(playlist.getCurrentTrack());
469 }
470 }
471 });
472 bitrateField = UIFactory.createPlainLabel("");
473 durationField = UIFactory.createPlainLabel("");
474 elapsedBar = new JProgressBar(0, 1);
475 elapsedBar.setIndeterminate(false);
476 elapsedBar.setStringPainted(true);
477 elapsedBar.setString("");
478
479 timer = new Timer(950, new ActionListener() {
480
481 public void actionPerformed(ActionEvent aE) {
482 final int elapsed = (int)(getElapsedTime() / 1000);
483 TIMESPAN.setTime(elapsed);
484 elapsedBar.setValue(elapsed);
485 elapsedBar.setString(TIMESPAN.getMusicDuration());
486 }
487 });
488 }
489
490
491
492
493
494
495 protected synchronized long getElapsedTime() {
496 return this.elapsedTime;
497 }
498
499
500
501
502
503
504 protected synchronized void setElapsedTime(long aElapsedTime) {
505 this.elapsedTime = aElapsedTime;
506 }
507
508
509 private class PresentationSettingsChangeHandler
510 implements PropertyChangeListener {
511
512
513
514
515
516
517 public void propertyChange(PropertyChangeEvent evt) {
518 final String[] props = {
519 Settings.PROPERTYNAME_AUDIT_INFO, Settings.PROPERTYNAME_COVER_SIZE_LARGE,
520 Settings.PROPERTYNAME_COVER_SIZE_SMALL
521 };
522 final String[] refreshprops = {
523 Settings.PROPERTYNAME_DISPLAY_FORMAT_DISC, Settings.PROPERTYNAME_DISPLAY_FORMAT_TRACK,
524 Settings.PROPERTYNAME_NEW_FILE_IN_DAYS
525 };
526 final String[] connectProps = {
527 Settings.PROPERTYNAME_REMOTE_DATABASE_URL, Settings.PROPERTYNAME_DIRECTORY_DB_LOCATION
528 };
529
530 if (Settings.PROPERTYNAME_PLAYER_BUFFER_SIZE.equals(evt.getPropertyName())) {
531 LOG.debug("Buffer size changed");
532 BasicPlayer.EXTERNAL_BUFFER_SIZE = MainModule.SETTINGS.getPlayerBufferSize();
533 }
534
535 if (Settings.PROPERTYNAME_LOG_LEVEL.equals(evt.getPropertyName())) {
536 LOG.info("Log Level changed");
537 Logger.getRootLogger().setLevel(Level.toLevel(MainModule.SETTINGS.getLogLevel()));
538 Logger.getLogger("com.melloware").setLevel(Level.toLevel(MainModule.SETTINGS.getLogLevel()));
539 Logger.getLogger("com.melloware.jukes.gui").setLevel(Level.toLevel(MainModule.SETTINGS.getLogLevel()));
540 }
541
542
543 if (StringUtils.indexOfAny(evt.getPropertyName(), props) >= 0) {
544 LOG.debug("Editor options changed");
545 editorPanel.clearEditors();
546 buildEditorPanel();
547 }
548
549
550 if (StringUtils.indexOfAny(evt.getPropertyName(), connectProps) >= 0) {
551 LOG.debug("DB Location changed, reconnecting to DB and refreshing tree.");
552 HibernateUtil.shutdown();
553 final String remoteURL = MainModule.SETTINGS.getRemoteDatabaseURL();
554 if ((StringUtils.isNotBlank(remoteURL)) && (!Settings.DEFAULT_REMOTE_DATABASE_URL.equals(remoteURL))) {
555 HibernateUtil.setRemoteUrl(remoteURL);
556 } else {
557 HibernateUtil.setRemoteUrl(null);
558 }
559 ActionManager.get(Actions.CONNECT_ID).actionPerformed(null);
560 }
561
562
563 if (StringUtils.indexOfAny(evt.getPropertyName(), refreshprops) >= 0) {
564 LOG.debug("Navigation options changed.");
565 ActionManager.get(Actions.REFRESH_ID).actionPerformed(null);
566 }
567
568 if (Settings.PROPERTYNAME_ANALYZER_MODE.equals(evt.getPropertyName())) {
569 mainframe.getAnalyzer().setDisplayMode(MainModule.SETTINGS.getAnalyzerMode());
570 }
571 }
572 }
573
574 private class RefreshedPanel
575 extends JPanel {
576
577
578
579
580 public void updateUI() {
581 super.updateUI();
582 if (getComponentCount() == 0) {
583 return;
584 }
585 if ((editorPlaylistSplitPane == null) || isPlaylistVisible()) {
586 return;
587 }
588 ComponentTreeUtils.updateComponentTreeUI(editorPlaylistSplitPane);
589 }
590
591 }
592
593
594
595 }