Source code: com/virtuosotechnologies/asaph/standardgui/SongEditor.java
1 /*
2 ================================================================================
3
4 FILE: SongEditor.java
5
6 PROJECT:
7
8 Asaph
9
10 CONTENTS:
11
12 Song editor pane implementation
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.GridBagLayout;
46 import java.awt.GridBagConstraints;
47 import java.awt.BorderLayout;
48 import java.awt.Insets;
49 import java.util.logging.Logger;
50 import javax.swing.BorderFactory;
51 import javax.swing.JTabbedPane;
52 import javax.swing.JComponent;
53 import javax.swing.JPanel;
54 import javax.swing.JSeparator;
55 import javax.swing.JScrollPane;
56 import javax.swing.JTextArea;
57 import javax.swing.SingleSelectionModel;
58 import javax.swing.undo.UndoManager;
59 import javax.swing.undo.UndoableEdit;
60 import javax.swing.undo.AbstractUndoableEdit;
61 import javax.swing.undo.CannotUndoException;
62 import javax.swing.undo.CannotRedoException;
63 import javax.swing.event.UndoableEditListener;
64 import javax.swing.event.UndoableEditEvent;
65 import javax.swing.event.ChangeListener;
66 import javax.swing.event.ChangeEvent;
67
68 import com.virtuosotechnologies.lib.command.CommandNode;
69 import com.virtuosotechnologies.lib.command.CommandEvent;
70 import com.virtuosotechnologies.lib.basiccommand.BasicCommandNode;
71 import com.virtuosotechnologies.lib.basiccommand.BasicGroupCommandNode;
72 import com.virtuosotechnologies.lib.basiccommand.BasicContainerCommandNode;
73 import com.virtuosotechnologies.lib.basiccommand.BasicItemCommandNode;
74 import com.virtuosotechnologies.lib.basiccommand.BasicSeparatorCommandNode;
75 import com.virtuosotechnologies.lib.swing.ScrollablePanel;
76 import com.virtuosotechnologies.lib.swing.SwingUtils;
77 import com.virtuosotechnologies.lib.asyncjob.AsyncJobRunner;
78 import com.virtuosotechnologies.lib.platform.PlatformUtils;
79
80 import com.virtuosotechnologies.asaph.model.StringListField;
81 import com.virtuosotechnologies.asaph.model.SongID;
82 import com.virtuosotechnologies.asaph.model.Song;
83 import com.virtuosotechnologies.asaph.model.Variation;
84 import com.virtuosotechnologies.asaph.model.ChordSet;
85 import com.virtuosotechnologies.asaph.modelutils.SongUtils;
86 import com.virtuosotechnologies.asaph.maingui.GuiEnvironmentManager;
87
88
89 /**
90 * Song editor pane implementation
91 */
92 /*package*/ class SongEditor
93 {
94 private static final String STR_menu_EditTitle =
95 ResourceAccess.Strings.buildString("menu_EditTitle");
96 private static final String STR_menu_Edit_UndoItem =
97 ResourceAccess.Strings.buildString("menu_Edit_UndoItem");
98 private static final String STR_menu_Edit_RedoItem =
99 ResourceAccess.Strings.buildString("menu_Edit_RedoItem");
100 private static final String STR_menu_Edit_CutItem =
101 ResourceAccess.Strings.buildString("menu_Edit_CutItem");
102 private static final String STR_menu_Edit_CopyItem =
103 ResourceAccess.Strings.buildString("menu_Edit_CopyItem");
104 private static final String STR_menu_Edit_PasteItem =
105 ResourceAccess.Strings.buildString("menu_Edit_PasteItem");
106 private static final String STR_menu_Edit_DeleteItem =
107 ResourceAccess.Strings.buildString("menu_Edit_DeleteItem");
108
109 private static final String STR_menu_Edit_UndoKeyStroke =
110 ResourceAccess.Strings.buildString("menu_Edit_UndoKeyStroke");
111 private static final String STR_menu_Edit_RedoKeyStroke =
112 ResourceAccess.Strings.buildString("menu_Edit_RedoKeyStroke");
113 private static final String STR_menu_Edit_CutKeyStroke =
114 ResourceAccess.Strings.buildString("menu_Edit_CutKeyStroke");
115 private static final String STR_menu_Edit_CopyKeyStroke =
116 ResourceAccess.Strings.buildString("menu_Edit_CopyKeyStroke");
117 private static final String STR_menu_Edit_PasteKeyStroke =
118 ResourceAccess.Strings.buildString("menu_Edit_PasteKeyStroke");
119
120 private static final String STR_SongEditor_FieldsTab =
121 ResourceAccess.Strings.buildString("SongEditor_FieldsTab");
122 private static final String STR_SongEditor_AltTitlesLabel =
123 ResourceAccess.Strings.buildString("SongEditor_AltTitlesLabel");
124 private static final String STR_SongEditor_KeywordsLabel =
125 ResourceAccess.Strings.buildString("SongEditor_KeywordsLabel");
126 private static final String STR_SongEditor_VersionsTab =
127 ResourceAccess.Strings.buildString("SongEditor_VersionsTab");
128 private static final String STR_SongEditor_BodyTab =
129 ResourceAccess.Strings.buildString("SongEditor_BodyTab");
130 private static final String STR_SongEditor_NotesTab =
131 ResourceAccess.Strings.buildString("SongEditor_NotesTab");
132
133
134 private Logger logger_;
135
136 private EditorPrefs editorPrefs_;
137
138 private SongUtils songUtils_;
139 private SongWindow window_;
140 private AsyncJobRunner jobRunner_;
141
142 private SongID songID_;
143 private Song song_;
144
145 private BasicCommandNode commandGroup_;
146 private BasicCommandNode undoCommand_;
147 private BasicCommandNode redoCommand_;
148 private BasicCommandNode cutCommand_;
149 private BasicCommandNode copyCommand_;
150 private BasicCommandNode pasteCommand_;
151 private BasicCommandNode deleteCommand_;
152 private BasicCommandNode formClipboardGroup_;
153 private BasicCommandNode bodyEditorEditGroup_;
154 private BasicCommandNode bodyEditorMenuGroup_;
155
156 private JTabbedPane tabbedPane_;
157 private boolean showingBodyEditor_;
158 private UndoManager undoManager_;
159 private SongBodyEditorPane bodyEditor_;
160
161
162 /**
163 * Constructor
164 */
165 /*package*/ SongEditor(
166 SongWindow window,
167 SongUtils songUtils,
168 EditorPrefs editorPrefs,
169 GuiEnvironmentManager guiEnvironmentManager,
170 AsyncJobRunner jobRunner,
171 SongID songID,
172 Song song)
173 {
174 logger_ = Logger.getLogger("com.virtuosotechnologies.asaph.standardgui");
175
176 editorPrefs_ = editorPrefs;
177 songUtils_ = songUtils;
178 window_ = window;
179 jobRunner_ = jobRunner;
180 songID_ = songID;
181 song_ = song;
182
183 undoManager_ = new UndoManager();
184
185 // Create menus
186 buildMenus();
187
188 // Create tabs
189 tabbedPane_ = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
190 buildFieldsTab();
191 buildVersionsTab();
192 buildSongTab(guiEnvironmentManager);
193 buildNotesTab();
194 }
195
196
197 /**
198 * Dispose the editor
199 */
200 /*package*/ void dispose()
201 {
202 if (showingBodyEditor_)
203 {
204 // Anything need to be done?
205 }
206 }
207
208
209 /*package*/ void setUndoLevels(
210 int levels)
211 {
212 undoManager_.setLimit(levels);
213 }
214
215
216 /**
217 * Get the component
218 */
219 /*package*/ JComponent getJComponent()
220 {
221 return tabbedPane_;
222 }
223
224
225 /**
226 * Get the toplevel command node
227 */
228 /*package*/ CommandNode getCommandNode()
229 {
230 return commandGroup_;
231 }
232
233
234 /*package*/ void markDirty()
235 {
236 window_.markDirty();
237 undoCommand_.setDisabledProperty(false);
238 }
239
240
241 /*package*/ void commitCurrentField()
242 {
243 StringUpdater.commitCurrent();
244 }
245
246
247 /*package*/ void updateFieldClipboardMenus(
248 int level)
249 {
250 cutCommand_.setDisabledProperty(level != 2);
251 copyCommand_.setDisabledProperty(level != 2);
252 pasteCommand_.setDisabledProperty(level == 0);
253 deleteCommand_.setDisabledProperty(level != 2);
254 }
255
256
257 private void buildMenus()
258 {
259 // Toplevel
260 commandGroup_ = new BasicGroupCommandNode();
261
262 // Edit menu
263 BasicCommandNode editMenu = new BasicContainerCommandNode();
264 editMenu.setNameProperty(STR_menu_EditTitle);
265 commandGroup_.addChild(editMenu);
266
267 undoCommand_ = new BasicItemCommandNode()
268 {
269 public void commandInvoked(
270 CommandEvent ev)
271 {
272 commitCurrentField();
273 if (undoManager_.canUndo())
274 {
275 undoManager_.undo();
276 }
277 }
278 };
279 undoCommand_.setNameProperty(STR_menu_Edit_UndoItem);
280 undoCommand_.setDisabledProperty(true);
281 undoCommand_.setAcceleratorKeystrokeProperty(SwingUtils.buildAcceleratorKeyStroke(
282 STR_menu_Edit_UndoKeyStroke));
283 editMenu.addChild(undoCommand_);
284
285 redoCommand_ = new BasicItemCommandNode()
286 {
287 public void commandInvoked(
288 CommandEvent ev)
289 {
290 commitCurrentField();
291 if (undoManager_.canRedo())
292 {
293 undoManager_.redo();
294 }
295 }
296 };
297 redoCommand_.setNameProperty(STR_menu_Edit_RedoItem);
298 redoCommand_.setDisabledProperty(true);
299 redoCommand_.setAcceleratorKeystrokeProperty(SwingUtils.buildAcceleratorKeyStroke(
300 STR_menu_Edit_RedoKeyStroke));
301 editMenu.addChild(redoCommand_);
302
303 editMenu.addChild(BasicSeparatorCommandNode.getInstance());
304 formClipboardGroup_ = new BasicGroupCommandNode();
305 editMenu.addChild(formClipboardGroup_);
306
307 cutCommand_ = new BasicItemCommandNode()
308 {
309 public void commandInvoked(
310 CommandEvent ev)
311 {
312 StringUpdater.cutCurrent();
313 }
314 };
315 cutCommand_.setNameProperty(STR_menu_Edit_CutItem);
316 cutCommand_.setDisabledProperty(true);
317 cutCommand_.setAcceleratorKeystrokeProperty(SwingUtils.buildAcceleratorKeyStroke(
318 STR_menu_Edit_CutKeyStroke));
319 formClipboardGroup_.addChild(cutCommand_);
320
321 copyCommand_ = new BasicItemCommandNode()
322 {
323 public void commandInvoked(
324 CommandEvent ev)
325 {
326 StringUpdater.copyCurrent();
327 }
328 };
329 copyCommand_.setNameProperty(STR_menu_Edit_CopyItem);
330 copyCommand_.setDisabledProperty(true);
331 copyCommand_.setAcceleratorKeystrokeProperty(SwingUtils.buildAcceleratorKeyStroke(
332 STR_menu_Edit_CopyKeyStroke));
333 formClipboardGroup_.addChild(copyCommand_);
334
335 pasteCommand_ = new BasicItemCommandNode()
336 {
337 public void commandInvoked(
338 CommandEvent ev)
339 {
340 StringUpdater.pasteCurrent();
341 }
342 };
343 pasteCommand_.setNameProperty(STR_menu_Edit_PasteItem);
344 pasteCommand_.setDisabledProperty(true);
345 pasteCommand_.setAcceleratorKeystrokeProperty(SwingUtils.buildAcceleratorKeyStroke(
346 STR_menu_Edit_PasteKeyStroke));
347 formClipboardGroup_.addChild(pasteCommand_);
348
349 deleteCommand_ = new BasicItemCommandNode()
350 {
351 public void commandInvoked(
352 CommandEvent ev)
353 {
354 StringUpdater.deleteCurrent();
355 }
356 };
357 deleteCommand_.setNameProperty(STR_menu_Edit_DeleteItem);
358 deleteCommand_.setDisabledProperty(true);
359 formClipboardGroup_.addChild(deleteCommand_);
360
361 formClipboardGroup_.addChild(BasicSeparatorCommandNode.getInstance());
362
363 bodyEditorEditGroup_ = new BasicGroupCommandNode();
364 editMenu.addChild(bodyEditorEditGroup_);
365
366 bodyEditorMenuGroup_ = new BasicGroupCommandNode();
367 commandGroup_.addChild(bodyEditorMenuGroup_);
368 }
369
370
371 private void performCommonOperations()
372 {
373 window_.markDirty();
374 undoCommand_.setDisabledProperty(!undoManager_.canUndo());
375 redoCommand_.setDisabledProperty(!undoManager_.canRedo());
376 }
377
378
379 private void addTabSwitchingEdit(
380 final UndoableEdit delegate,
381 final JComponent component)
382 {
383 addTabSwitchingEdit(delegate, component, null);
384 }
385
386
387 private void addTabSwitchingEdit(
388 final UndoableEdit delegate,
389 final JComponent component,
390 final SongBodyEditorPane bodyEditorPane)
391 {
392 final Variation variation =
393 (bodyEditorPane == null ? null : bodyEditorPane.getCurVariation());
394 final ChordSet chordSet =
395 (bodyEditorPane == null ? null : bodyEditorPane.getCurChordSet());
396 undoManager_.addEdit(
397 new AbstractUndoableEdit()
398 {
399 public void undo()
400 throws CannotUndoException
401 {
402 super.undo();
403 tabbedPane_.setSelectedComponent(component);
404 if (bodyEditorPane != null)
405 {
406 bodyEditorPane.setCurVariation(variation);
407 bodyEditorPane.setCurChordSet(chordSet);
408 }
409 delegate.undo();
410 performCommonOperations();
411 }
412
413 public void redo()
414 throws CannotRedoException
415 {
416 super.redo();
417 tabbedPane_.setSelectedComponent(component);
418 if (bodyEditorPane != null)
419 {
420 bodyEditorPane.setCurVariation(variation);
421 bodyEditorPane.setCurChordSet(chordSet);
422 }
423 delegate.redo();
424 performCommonOperations();
425 }
426 });
427 performCommonOperations();
428 }
429
430
431 private void buildFieldsTab()
432 {
433 final JPanel mainPanel = new JPanel(new GridBagLayout());
434 // ScrollablePanel viewportPanel = new ScrollablePanel(new BorderLayout());
435 // viewportPanel.add(mainPanel, BorderLayout.NORTH);
436 // final JScrollPane fieldsComponent = new JScrollPane(viewportPanel);
437 final JPanel fieldsComponent = new JPanel(new BorderLayout());
438 fieldsComponent.add(mainPanel, BorderLayout.NORTH);
439 tabbedPane_.add(STR_SongEditor_FieldsTab, fieldsComponent);
440
441 GridBagConstraints gbc = new GridBagConstraints();
442 gbc.gridx = 0;
443 gbc.weightx = 1;
444 gbc.weighty = 1;
445 gbc.fill = GridBagConstraints.HORIZONTAL;
446 gbc.insets = new Insets(0, 5, 0, 5);
447
448 final MainFieldsEditor mainFieldsEditor = new MainFieldsEditor(song_, this,
449 new UndoableEditListener()
450 {
451 public void undoableEditHappened(
452 UndoableEditEvent ev)
453 {
454 addTabSwitchingEdit(ev.getEdit(), fieldsComponent);
455 }
456 });
457 mainFieldsEditor.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
458 mainPanel.add(mainFieldsEditor, gbc);
459
460 mainPanel.add(new JSeparator(), gbc);
461
462 final StringListEditor altTitlesEditor = new StringListEditor(STR_SongEditor_AltTitlesLabel,
463 (StringListField)song_.getNamedField(Song.ALTERNATETITLELIST_FIELD), this,
464 new UndoableEditListener()
465 {
466 public void undoableEditHappened(
467 UndoableEditEvent ev)
468 {
469 addTabSwitchingEdit(ev.getEdit(), fieldsComponent);
470 }
471 });
472 altTitlesEditor.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
473 mainPanel.add(altTitlesEditor, gbc);
474
475 mainPanel.add(new JSeparator(), gbc);
476
477 final StringListEditor keywordsEditor = new StringListEditor(STR_SongEditor_KeywordsLabel,
478 (StringListField)song_.getNamedField(Song.KEYWORDLIST_FIELD), this,
479 new UndoableEditListener()
480 {
481 public void undoableEditHappened(
482 UndoableEditEvent ev)
483 {
484 addTabSwitchingEdit(ev.getEdit(), fieldsComponent);
485 }
486 });
487 keywordsEditor.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
488 mainPanel.add(keywordsEditor, gbc);
489 }
490
491
492 private void buildVersionsTab()
493 {
494 final JPanel mainPanel = new JPanel(new GridBagLayout());
495 // ScrollablePanel viewportPanel = new ScrollablePanel(new BorderLayout());
496 // viewportPanel.add(mainPanel, BorderLayout.NORTH);
497 // final JScrollPane versionsComponent = new JScrollPane(viewportPanel);
498 final JPanel versionsComponent = new JPanel(new BorderLayout());
499 versionsComponent.add(mainPanel, BorderLayout.NORTH);
500 tabbedPane_.add(STR_SongEditor_VersionsTab, versionsComponent);
501
502 GridBagConstraints gbc = new GridBagConstraints();
503 gbc.gridx = 0;
504 gbc.weightx = 1;
505 gbc.weighty = 1;
506 gbc.fill = GridBagConstraints.HORIZONTAL;
507 gbc.insets = new Insets(0, 5, 0, 5);
508
509 final ChordSetListEditor chordSetsEditor = new ChordSetListEditor(
510 window_.getSongUtils(), window_.getNotationManager(), song_, this,
511 new UndoableEditListener()
512 {
513 public void undoableEditHappened(
514 UndoableEditEvent ev)
515 {
516 addTabSwitchingEdit(ev.getEdit(), versionsComponent);
517 }
518 });
519 chordSetsEditor.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
520 mainPanel.add(chordSetsEditor, gbc);
521
522 mainPanel.add(new JSeparator(), gbc);
523
524 final VariationListEditor variationsEditor = new VariationListEditor(
525 song_, this, new UndoableEditListener()
526 {
527 public void undoableEditHappened(
528 UndoableEditEvent ev)
529 {
530 addTabSwitchingEdit(ev.getEdit(), versionsComponent);
531 }
532 });
533 variationsEditor.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
534 mainPanel.add(variationsEditor, gbc);
535 }
536
537
538 private void buildSongTab(
539 GuiEnvironmentManager guiEnvironmentManager)
540 {
541 bodyEditor_ = new SongBodyEditorPane(jobRunner_, songID_, song_,
542 bodyEditorEditGroup_, editorPrefs_, guiEnvironmentManager,
543 window_.getSongUtils(), window_.getDataTransferUtils(),
544 window_.getNotationManager());
545 final JComponent component = bodyEditor_.getJComponent();
546 bodyEditor_.setUndoableEditListener(
547 new UndoableEditListener()
548 {
549 public void undoableEditHappened(
550 UndoableEditEvent ev)
551 {
552 addTabSwitchingEdit(ev.getEdit(), component, bodyEditor_);
553 }
554 });
555 tabbedPane_.add(STR_SongEditor_BodyTab, component);
556 showingBodyEditor_ = false;
557 final SingleSelectionModel tabModel = tabbedPane_.getModel();
558 tabModel.addChangeListener(
559 new ChangeListener()
560 {
561 public void stateChanged(
562 ChangeEvent ev)
563 {
564 boolean newShowing =
565 (tabbedPane_.getComponentAt(tabModel.getSelectedIndex()) == component);
566 if (newShowing != showingBodyEditor_)
567 {
568 showingBodyEditor_ = newShowing;
569 if (showingBodyEditor_)
570 {
571 commitCurrentField();
572 bodyEditor_.activatePane(bodyEditorMenuGroup_);
573 bodyEditorEditGroup_.setHiddenProperty(false);
574 formClipboardGroup_.setHiddenProperty(true);
575 }
576 else
577 {
578 bodyEditorMenuGroup_.removeAllChildren();
579 bodyEditorEditGroup_.setHiddenProperty(true);
580 formClipboardGroup_.setHiddenProperty(false);
581 }
582 }
583 }
584 });
585 }
586
587
588 private void buildNotesTab()
589 {
590 StringListField field = (StringListField)song_.getNamedField(Song.NOTESLIST_FIELD);
591 final JTextArea textArea = new JTextArea(songUtils_.getMultiLineString(field));
592 textArea.setLineWrap(true);
593 textArea.setWrapStyleWord(true);
594 textArea.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
595 final JScrollPane scroller = new JScrollPane(textArea);
596 StringUpdater.install(textArea, field, songUtils_, this,
597 new UndoableEditListener()
598 {
599 public void undoableEditHappened(
600 UndoableEditEvent ev)
601 {
602 addTabSwitchingEdit(ev.getEdit(), scroller);
603 }
604 });
605 tabbedPane_.add(STR_SongEditor_NotesTab, scroller);
606 }
607 }