Source code: com/virtuosotechnologies/asaph/standardgui/SongWindow.java
1 /*
2 ================================================================================
3
4 FILE: SongWindow.java
5
6 PROJECT:
7
8 Asaph
9
10 CONTENTS:
11
12 A window in the standard gui, for viewing and editing a single song
13
14 PROGRAMMERS:
15
16 Daniel Azuma (DA) <dazuma@kagi.com>
17
18 COPYRIGHT:
19
20 Copyright (C) 2003 Daniel Azuma (dazuma@kagi.com)
21
22 This program is free software; you can redistribute it and/or
23 modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation; either version 2
25 of the License, or (at your option) any later version.
26
27 This program is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public
33 License along with this program; if not, write to
34 Free Software Foundation, Inc.
35 59 Temple Place, Suite 330
36 Boston, MA 02111-1307 USA
37
38 ================================================================================
39 */
40
41
42 package com.virtuosotechnologies.asaph.standardgui;
43
44
45 import java.awt.Dimension;
46 import java.awt.BorderLayout;
47 import java.awt.print.PrinterJob;
48 import java.util.logging.Logger;
49 import javax.swing.JPanel;
50 import javax.swing.JOptionPane;
51 import javax.swing.SwingUtilities;
52 import javax.print.attribute.PrintRequestAttributeSet;
53
54 import com.virtuosotechnologies.lib.command.CommandEvent;
55 import com.virtuosotechnologies.lib.basiccommand.BasicCommandNode;
56 import com.virtuosotechnologies.lib.basiccommand.BasicGroupCommandNode;
57 import com.virtuosotechnologies.lib.basiccommand.BasicContainerCommandNode;
58 import com.virtuosotechnologies.lib.basiccommand.BasicRadioGroupCommandNode;
59 import com.virtuosotechnologies.lib.basiccommand.BasicSeparatorCommandNode;
60 import com.virtuosotechnologies.lib.basiccommand.BasicItemCommandNode;
61 import com.virtuosotechnologies.lib.basiccommand.BasicToggleItemCommandNode;
62 import com.virtuosotechnologies.lib.util.ExceptionUtils;
63 import com.virtuosotechnologies.lib.swing.SwingUtils;
64 import com.virtuosotechnologies.lib.swing.DetailedMessageDialog;
65 import com.virtuosotechnologies.lib.swing.SizeConstrainingLayout;
66 import com.virtuosotechnologies.lib.asyncjob.AsyncJobRunner;
67 import com.virtuosotechnologies.lib.propertyset.PropertySetListener;
68 import com.virtuosotechnologies.lib.propertyset.PropertySetEvent;
69 import com.virtuosotechnologies.lib.platform.PlatformUtils;
70
71 import com.virtuosotechnologies.asaph.model.SongID;
72 import com.virtuosotechnologies.asaph.model.Song;
73 import com.virtuosotechnologies.asaph.model.SongDatabase;
74 import com.virtuosotechnologies.asaph.model.SongDatabaseFailedException;
75 import com.virtuosotechnologies.asaph.model.SongDeletedException;
76 import com.virtuosotechnologies.asaph.model.SongNotFreshException;
77 import com.virtuosotechnologies.asaph.maingui.DatabaseManager;
78 import com.virtuosotechnologies.asaph.maingui.GuiEnvironmentManager;
79 import com.virtuosotechnologies.asaph.maingui.ListUpdateManager;
80 import com.virtuosotechnologies.asaph.maingui.PaneManager;
81 import com.virtuosotechnologies.asaph.maingui.PaneHandler;
82 import com.virtuosotechnologies.asaph.maingui.PaneController;
83 import com.virtuosotechnologies.asaph.modelutils.SongUtils;
84 import com.virtuosotechnologies.asaph.modelutils.DataTransferUtils;
85 import com.virtuosotechnologies.asaph.notationmanager.NotationManager;
86
87
88 /**
89 * A window in the standard gui, for viewing and editing a single song
90 */
91 /*package*/ class SongWindow
92 {
93 private static final String STR_menu_SongTitle =
94 ResourceAccess.Strings.buildString("menu_SongTitle");
95 private static final String STR_menu_Song_SaveItem =
96 ResourceAccess.Strings.buildString("menu_Song_SaveItem");
97 private static final String STR_menu_Song_CloseItem =
98 ResourceAccess.Strings.buildString("menu_Song_CloseItem");
99 private static final String STR_menu_Song_ToggleViewerItem =
100 ResourceAccess.Strings.buildString("menu_Song_ToggleViewerItem");
101 private static final String STR_menu_Song_ToggleEditorItem =
102 ResourceAccess.Strings.buildString("menu_Song_ToggleEditorItem");
103
104 private static final String STR_menu_Song_SaveKeyStroke =
105 ResourceAccess.Strings.buildString("menu_Song_SaveKeyStroke");
106 private static final String STR_menu_Song_CloseKeyStroke =
107 ResourceAccess.Strings.buildString("menu_Song_CloseKeyStroke");
108
109 private static final String STR_SongWindow_UntitledSongTitle =
110 ResourceAccess.Strings.buildString("SongWindow_UntitledSongTitle");
111
112 private static final String STR_dialog_ErrorTitle =
113 ResourceAccess.Strings.buildString("dialog_ErrorTitle");
114 private static final String STR_dialog_ConfirmTitle =
115 ResourceAccess.Strings.buildString("dialog_ConfirmTitle");
116 private static final String STR_dialog_ErrorsHeader =
117 ResourceAccess.Strings.buildString("dialog_ErrorsHeader");
118 private static final String STR_dialog_FirstExceptionTemplate =
119 ResourceAccess.Strings.buildString("dialog_FirstExceptionTemplate");
120 private static final String STR_dialog_NextExceptionTemplate =
121 ResourceAccess.Strings.buildString("dialog_NextExceptionTemplate");
122 private static final String STR_message_SongVanishedOnSave =
123 ResourceAccess.Strings.buildString("message_SongVanishedOnSave");
124 private static final String STR_message_SongNotFreshOnSave =
125 ResourceAccess.Strings.buildString("message_SongNotFreshOnSave");
126 private static final String STR_message_SongRemovedOnSave =
127 ResourceAccess.Strings.buildString("message_SongRemovedOnSave");
128 private static final String STR_message_SaveSongError =
129 ResourceAccess.Strings.buildString("message_SaveSongError");
130
131
132 private Logger logger_;
133
134 private StandardGuiPlugin plugin_;
135 private SongUtils songUtils_;
136 private DataTransferUtils dataTransferUtils_;
137 private NotationManager notationManager_;
138 private DatabaseManager databaseManager_;
139 private GuiEnvironmentManager guiEnvironmentManager_;
140 private ListUpdateManager listUpdateManager_;
141 private AsyncJobRunner jobRunner_;
142
143 private SongID songID_;
144 private Song song_;
145 private String songTitle_;
146 private boolean dirty_;
147
148 private RenderSettings renderSettings_;
149
150 private PaneController paneController_;
151 private JPanel mainPanel_;
152 private BasicCommandNode commandGroup_;
153 private BasicCommandNode saveCommand_;
154 private BasicCommandNode viewerMode_;
155 private BasicCommandNode editorMode_;
156
157 private SongViewer viewer_;
158 private SongEditor editor_;
159
160 private EditorPrefs editorPrefs_;
161 private EditorPrefs.PrefsListener prefsListener_;
162
163
164 /**
165 * Constructor
166 */
167 /*package*/ SongWindow(
168 StandardGuiPlugin plugin,
169 SongID songID,
170 EditorPrefs editorPrefs,
171 DatabaseManager databaseManager,
172 GuiEnvironmentManager guiEnvironmentManager,
173 ListUpdateManager listUpdateManager,
174 PaneManager paneManager,
175 SongUtils songUtils,
176 DataTransferUtils dataTransferUtils,
177 NotationManager notationManager,
178 AsyncJobRunner jobRunner)
179 throws
180 SongDatabaseFailedException,
181 SongDeletedException
182 {
183 logger_ = Logger.getLogger("com.virtuosotechnologies.asaph.standardgui");
184
185 plugin_ = plugin;
186 songUtils_ = songUtils;
187 dataTransferUtils_ = dataTransferUtils;
188 notationManager_ = notationManager;
189 databaseManager_ = databaseManager;
190 guiEnvironmentManager_ = guiEnvironmentManager;
191 listUpdateManager_ = listUpdateManager;
192 jobRunner_ = jobRunner;
193
194 editorPrefs_ = editorPrefs;
195 editorPrefs_.addPrefsListener(
196 prefsListener_ = new EditorPrefs.PrefsListener()
197 {
198 public void prefsChanged(
199 EditorPrefs.PrefsChangedEvent ev)
200 {
201 if (editor_ != null)
202 {
203 editor_.setUndoLevels(editorPrefs_.getUndoLevels());
204 }
205 }
206 });
207
208 renderSettings_ = new RenderSettings();
209
210 songID_ = songID;
211 song_ = songID.getDatabase().checkOutSong(songID);
212 if (song_ == null)
213 {
214 throw new SongDeletedException();
215 }
216 boolean brandNew = songUtils_.prepareSongForEditing(song_);
217 dirty_ = false;
218
219 // Set up containers
220 commandGroup_ = new BasicGroupCommandNode();
221 mainPanel_ = new JPanel(new SizeConstrainingLayout(
222 400, 300, Integer.MAX_VALUE, Integer.MAX_VALUE));
223
224 // Set up song menu
225 BasicCommandNode songMenu = new BasicContainerCommandNode();
226 songMenu.setNameProperty(STR_menu_SongTitle);
227 commandGroup_.addChild(songMenu);
228
229 BasicCommandNode modeGroup = new BasicRadioGroupCommandNode();
230 songMenu.addChild(modeGroup);
231 viewerMode_ = new ToggleViewerCommandNode();
232 editorMode_ = new ToggleEditorCommandNode();
233 modeGroup.addChild(viewerMode_);
234 modeGroup.addChild(editorMode_);
235 if (brandNew)
236 {
237 editorMode_.setSelectionStateProperty(true);
238 }
239 else
240 {
241 viewerMode_.setSelectionStateProperty(true);
242 }
243
244 songMenu.addChild(BasicSeparatorCommandNode.getInstance());
245
246 saveCommand_ = new BasicItemCommandNode()
247 {
248 public void commandInvoked(
249 CommandEvent ev)
250 {
251 handleSave();
252 }
253 };
254 saveCommand_.setNameProperty(STR_menu_Song_SaveItem);
255 saveCommand_.setDisabledProperty(true);
256 saveCommand_.setAcceleratorKeystrokeProperty(SwingUtils.buildAcceleratorKeyStroke(
257 STR_menu_Song_SaveKeyStroke));
258 songMenu.addChild(saveCommand_);
259
260 BasicCommandNode closeNode = new BasicItemCommandNode()
261 {
262 public void commandInvoked(
263 CommandEvent ev)
264 {
265 if (handleClosing())
266 {
267 paneController_.close();
268 }
269 }
270 };
271 closeNode.setNameProperty(STR_menu_Song_CloseItem);
272 closeNode.setAcceleratorKeystrokeProperty(SwingUtils.buildAcceleratorKeyStroke(
273 STR_menu_Song_CloseKeyStroke));
274 songMenu.addChild(closeNode);
275
276 // Set up pane
277 songTitle_ = songUtils_.getFieldValueAsString(song_, Song.MAINTITLE_FIELD);
278 if (songTitle_.length() == 0)
279 {
280 songTitle_ = STR_SongWindow_UntitledSongTitle;
281 }
282 paneController_ = paneManager.createPane(mainPanel_, songTitle_, commandGroup_,
283 new PaneHandler()
284 {
285 public boolean handlePaneClosing()
286 {
287 return handleClosing();
288 }
289 });
290 plugin_.registerWindow(this);
291 }
292
293
294 private class ToggleViewerCommandNode
295 extends BasicToggleItemCommandNode
296 implements PropertySetListener
297 {
298 private ToggleViewerCommandNode()
299 {
300 setNameProperty(STR_menu_Song_ToggleViewerItem);
301 addPropertySetListener(this);
302 }
303
304 public void propertySetChanged(
305 PropertySetEvent ev)
306 {
307 if (ev.getKey() == BasicCommandNode.SELECTION_STATE_PROPERTY &&
308 Boolean.TRUE.equals(ev.getNewValue()))
309 {
310 switchToViewer();
311 }
312 }
313 }
314
315
316 private class ToggleEditorCommandNode
317 extends BasicToggleItemCommandNode
318 implements PropertySetListener
319 {
320 private ToggleEditorCommandNode()
321 {
322 setNameProperty(STR_menu_Song_ToggleEditorItem);
323 addPropertySetListener(this);
324 }
325
326 public void propertySetChanged(
327 PropertySetEvent ev)
328 {
329 if (ev.getKey() == BasicCommandNode.SELECTION_STATE_PROPERTY &&
330 Boolean.TRUE.equals(ev.getNewValue()))
331 {
332 switchToEditor();
333 }
334 }
335 }
336
337
338 private void switchToViewer()
339 {
340 if (editor_ != null)
341 {
342 editor_.dispose();
343 editor_ = null;
344 }
345 viewer_ = new SongViewer(this, songUtils_, guiEnvironmentManager_,
346 jobRunner_, songID_, song_, renderSettings_);
347 mainPanel_.removeAll();
348 mainPanel_.add(viewer_.getJComponent(), BorderLayout.CENTER);
349 SwingUtilities.invokeLater(
350 new Runnable()
351 {
352 public void run()
353 {
354 viewer_.getJComponent().requestFocus();
355 }
356 });
357 if (commandGroup_.getNumChildren() > 1)
358 {
359 commandGroup_.removeNthChild(1);
360 }
361 commandGroup_.addChild(viewer_.getCommandNode());
362 }
363
364
365 private void switchToEditor()
366 {
367 if (viewer_ != null)
368 {
369 viewer_.dispose();
370 viewer_ = null;
371 }
372 editor_ = new SongEditor(this, songUtils_, editorPrefs_,
373 guiEnvironmentManager_, jobRunner_, songID_, song_);
374 editor_.setUndoLevels(editorPrefs_.getUndoLevels());
375 mainPanel_.removeAll();
376 mainPanel_.add(editor_.getJComponent(), BorderLayout.CENTER);
377 SwingUtilities.invokeLater(
378 new Runnable()
379 {
380 public void run()
381 {
382 editor_.getJComponent().requestFocus();
383 }
384 });
385 if (commandGroup_.getNumChildren() > 1)
386 {
387 commandGroup_.removeNthChild(1);
388 }
389 commandGroup_.addChild(editor_.getCommandNode());
390 }
391
392
393 /*package*/ SongUtils getSongUtils()
394 {
395 return songUtils_;
396 }
397
398
399 /*package*/ DataTransferUtils getDataTransferUtils()
400 {
401 return dataTransferUtils_;
402 }
403
404
405 /*package*/ NotationManager getNotationManager()
406 {
407 return notationManager_;
408 }
409
410
411 /**
412 * Handle database closing event
413 */
414 /*package*/ boolean databaseClosing(
415 SongDatabase database)
416 {
417 if (songID_.getDatabase().equals(database))
418 {
419 if (handleClosing())
420 {
421 paneController_.close();
422 return true;
423 }
424 else
425 {
426 return false;
427 }
428 }
429 else
430 {
431 return true;
432 }
433 }
434
435
436 /**
437 * Handle close request
438 */
439 private boolean handleClosing()
440 {
441 if (editor_ != null)
442 {
443 editor_.commitCurrentField();
444 }
445 if (dirty_)
446 {
447 int option = JOptionPane.showConfirmDialog(guiEnvironmentManager_.getDialogParent(),
448 ResourceAccess.Strings.buildString("message_SaveSongBeforeClosing", songTitle_),
449 STR_dialog_ConfirmTitle, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
450 if (option == JOptionPane.YES_OPTION)
451 {
452 if (!handleSave())
453 {
454 return false;
455 }
456 }
457 else if (option != JOptionPane.NO_OPTION)
458 {
459 return false;
460 }
461 }
462 editorPrefs_.removePrefsListener(prefsListener_);
463 plugin_.unregisterWindow(this);
464 return true;
465 }
466
467
468 /**
469 * Handle save request
470 */
471 private boolean handleSave()
472 {
473 if (editor_ != null)
474 {
475 editor_.commitCurrentField();
476 }
477 SongDatabase database = songID_.getDatabase();
478 try
479 {
480 try
481 {
482 try
483 {
484 database.commitSong(song_);
485 }
486 catch (SongNotFreshException ex)
487 {
488 int option = JOptionPane.showConfirmDialog(guiEnvironmentManager_.getDialogParent(),
489 STR_message_SongNotFreshOnSave, STR_dialog_ConfirmTitle,
490 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
491 if (option != JOptionPane.YES_OPTION)
492 {
493 return false;
494 }
495 database.forceCommitSong(song_);
496 }
497 listUpdateManager_.reportSongChanged(songID_);
498 }
499 catch (SongDeletedException ex)
500 {
501 int option = JOptionPane.showConfirmDialog(guiEnvironmentManager_.getDialogParent(),
502 STR_message_SongRemovedOnSave, STR_dialog_ConfirmTitle,
503 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
504 if (option != JOptionPane.YES_OPTION)
505 {
506 return false;
507 }
508 SongID newSongID = database.addCopyOfSong(song_);
509 listUpdateManager_.reportSongAdded(newSongID);
510 Song newSong = database.checkOutSong(newSongID);
511 if (newSong == null)
512 {
513 throw new SongDatabaseFailedException(STR_message_SongVanishedOnSave);
514 }
515 songID_ = newSongID;
516 song_ = newSong;
517 songUtils_.prepareSongForEditing(song_);
518 if (viewerMode_.getSelectionStateProperty())
519 {
520 switchToViewer();
521 }
522 else if (editorMode_.getSelectionStateProperty())
523 {
524 switchToEditor();
525 }
526 }
527 databaseManager_.setDatabaseDirtyBit(database);
528 }
529 catch (SongDatabaseFailedException ex)
530 {
531 DetailedMessageDialog dialog = DetailedMessageDialog.create(
532 guiEnvironmentManager_.getDialogParent(), STR_dialog_ErrorTitle,
533 STR_message_SaveSongError, STR_dialog_ErrorsHeader,
534 ExceptionUtils.getDescriptionFor(ex,
535 STR_dialog_FirstExceptionTemplate, STR_dialog_NextExceptionTemplate),
536 null);
537 dialog.show();
538 return false;
539 }
540 dirty_ = false;
541 saveCommand_.setDisabledProperty(true);
542 paneController_.putValue(PaneController.WINDOW_MODIFIED_KEY, Boolean.FALSE);
543 songTitle_ = songUtils_.getFieldValueAsString(song_, Song.MAINTITLE_FIELD);
544 if (songTitle_.length() == 0)
545 {
546 songTitle_ = STR_SongWindow_UntitledSongTitle;
547 }
548 paneController_.putValue(PaneController.TITLE_KEY, songTitle_);
549 return true;
550 }
551
552
553 /**
554 * Mark the song as dirty
555 */
556 /*package*/ void markDirty()
557 {
558 if (!dirty_)
559 {
560 dirty_ = true;
561 saveCommand_.setDisabledProperty(false);
562 paneController_.putValue(PaneController.WINDOW_MODIFIED_KEY, Boolean.TRUE);
563 }
564 }
565
566
567 /**
568 * Get PrinterJob
569 */
570 /*package*/ PrinterJob getPrinterJob()
571 {
572 return plugin_.getPrinterJob();
573 }
574
575
576 /**
577 * Get PrintRequestAttributeSet
578 */
579 /*package*/ PrintRequestAttributeSet getPrintAttributes()
580 {
581 return plugin_.getPrintAttributes();
582 }
583 }