Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/virtuosotechnologies/asaph/maingui/ListsImpl.java


1   /*
2   ================================================================================
3   
4     FILE:  ListsImpl.java
5     
6     PROJECT:
7     
8       Asaph
9     
10    CONTENTS:
11    
12      Implementation of the database and song lists.
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.maingui;
43  
44  
45  import java.util.Map;
46  import java.util.HashMap;
47  import java.util.List;
48  import java.util.ArrayList;
49  import java.util.Set;
50  import java.util.HashSet;
51  import java.util.Collection;
52  import java.util.Arrays;
53  import java.util.Iterator;
54  import java.util.prefs.Preferences;
55  import java.awt.Component;
56  import java.awt.Graphics;
57  import java.awt.Graphics2D;
58  import java.awt.Font;
59  import java.awt.Color;
60  import java.awt.Insets;
61  import java.awt.BorderLayout;
62  import java.awt.Dimension;
63  import java.awt.GridBagLayout;
64  import java.awt.GridBagConstraints;
65  import java.awt.event.MouseAdapter;
66  import java.awt.event.MouseEvent;
67  import java.awt.event.KeyAdapter;
68  import java.awt.event.KeyEvent;
69  import javax.swing.JList;
70  import javax.swing.JSplitPane;
71  import javax.swing.JScrollPane;
72  import javax.swing.JPanel;
73  import javax.swing.JTextArea;
74  import javax.swing.JComponent;
75  import javax.swing.BorderFactory;
76  import javax.swing.ListSelectionModel;
77  import javax.swing.ListModel;
78  import javax.swing.DefaultListModel;
79  import javax.swing.DefaultListCellRenderer;
80  import javax.swing.event.ListSelectionListener;
81  import javax.swing.event.ListSelectionEvent;
82  
83  import com.virtuosotechnologies.lib.util.EventBroadcastHelper;
84  import com.virtuosotechnologies.lib.util.EventAbortedException;
85  import com.virtuosotechnologies.lib.util.ExceptionUtils;
86  import com.virtuosotechnologies.lib.swing.DetailedMessageDialog;
87  import com.virtuosotechnologies.lib.swing.ScrollablePanel;
88  
89  import com.virtuosotechnologies.asaph.model.SongDatabaseFailedException;
90  import com.virtuosotechnologies.asaph.model.SongDatabase;
91  import com.virtuosotechnologies.asaph.model.Song;
92  import com.virtuosotechnologies.asaph.model.SongID;
93  import com.virtuosotechnologies.asaph.model.SongIDResultSet;
94  import com.virtuosotechnologies.asaph.model.opsemantics.NopSemantics;
95  import com.virtuosotechnologies.asaph.model.opsemantics.GetFieldAsStringSemantics;
96  import com.virtuosotechnologies.asaph.model.opsemantics.GetFieldAsStringArraySemantics;
97  import com.virtuosotechnologies.asaph.model.opsemantics.PredicateSemantics;
98  import com.virtuosotechnologies.asaph.model.opsemantics.TruePredicateSemantics;
99  import com.virtuosotechnologies.asaph.modelutils.SongUtils;
100 
101 
102 /**
103  * Implementation of the database and song lists.
104  */
105 /*package*/ class ListsImpl
106 implements SelectionManager, ListUpdateManager
107 {
108   private static final String STR_Dialog_DatabaseErrorTitle =
109     ResourceAccess.Strings.buildString("Dialog_DatabaseErrorTitle");
110   private static final String STR_Message_FetchIndexFailed =
111     ResourceAccess.Strings.buildString("Message_FetchIndexFailed");
112   private static final String STR_Dialog_ErrorHeader =
113     ResourceAccess.Strings.buildString("Dialog_ErrorHeader");
114   private static final String STR_Dialog_FirstExceptionTemplate =
115     ResourceAccess.Strings.buildString("Dialog_FirstExceptionTemplate");
116   private static final String STR_Dialog_NextExceptionTemplate =
117     ResourceAccess.Strings.buildString("Dialog_NextExceptionTemplate");
118   private static final String STR_InfoPane_AltTitlesHeader =
119     ResourceAccess.Strings.buildString("InfoPane_AltTitlesHeader");
120   
121   private static final Font INFO_FONT = new Font("Dialog", Font.PLAIN, 12);
122   private static final Color INFO_COLOR = Color.black;
123   
124   private static final String DETAILS_PREFSNODE = "detailsPane";
125   private static final String SHOW_COMMENT_PREF = "showComment";
126   private static final String SHOW_AUTHOR_PREF = "showAuthor";
127   private static final String SHOW_COPYRIGHT_PREF = "showCopyright";
128   private static final String SHOW_ALTTITLES_PREF = "showAltTitles";
129   
130   
131   private DatabaseManagerImpl databaseManager_;
132   private CommandManagerImpl commandManager_;
133   private SongUtils songUtils_;
134   
135   private DatabaseManagerImpl.Item selectedDatabase_;
136   private SongListModel.Item[] selectedSongs_;
137   private Map songListModels_;
138   
139   private HeaderPane songsHeader_;
140   private JList databasesList_;
141   private JList songsList_;
142   private JPanel infoPanel_;
143   private JSplitPane listsPanel_;
144   private JSplitPane upperSplit_;
145   
146   private PredicateSemantics filterPredicate_;
147   
148   private Preferences detailPanePrefs_;
149   private boolean showCommentDetail_;
150   private boolean showAuthorDetail_;
151   private boolean showCopyrightDetail_;
152   private boolean showAltTitlesDetail_;
153   
154   private EventBroadcastHelper broadcaster_;
155   
156   private DatabaseListener listener_;
157   
158   private SongListModel.SizeChangeListener songsHeaderUpdater_;
159   
160   
161   /**
162    * Constructor
163    */
164   /*package*/ ListsImpl(
165     final SongUtils songUtils,
166     DatabaseManagerImpl databaseManager,
167     final JComponent dialogParent)
168   {
169     songUtils_ = songUtils;
170     databaseManager_ = databaseManager;
171     selectedDatabase_ = null;
172     songListModels_ = new HashMap();
173     filterPredicate_ = new TruePredicateSemantics.DefaultImplementation();
174     
175     Preferences rootPrefs = Preferences.userNodeForPackage(ListsImpl.class);
176     detailPanePrefs_ = rootPrefs.node(DETAILS_PREFSNODE);
177     showCommentDetail_ = detailPanePrefs_.getBoolean(SHOW_COMMENT_PREF, true);
178     showAuthorDetail_ = detailPanePrefs_.getBoolean(SHOW_AUTHOR_PREF, true);
179     showCopyrightDetail_ = detailPanePrefs_.getBoolean(SHOW_COPYRIGHT_PREF, false);
180     showAltTitlesDetail_ = detailPanePrefs_.getBoolean(SHOW_ALTTITLES_PREF, true);
181     
182     databaseManager_.addDatabaseListener(
183       listener_ = new DatabaseListener()
184       {
185         public void databaseDirtyChanged(
186           DatabaseEvent ev)
187         {
188         }
189         
190         public void databaseClosing(
191           DatabaseEvent ev)
192         throws
193           EventAbortedException
194         {
195         }
196         
197         public void databaseOpened(
198           DatabaseEvent ev)
199         {
200           SongListModel songListModel = new SongListModel(ev.getSongDatabase(), songUtils,
201             new TitleSortSongComparator(), filterPredicate_);
202           songListModels_.put(ev.getSongDatabase(), songListModel);
203           try
204           {
205             songListModel.addSongIDs(ev.getSongDatabase().performOperation(
206               new NopSemantics.DefaultImplementation(), null));
207           }
208           catch (SongDatabaseFailedException ex)
209           {
210             DetailedMessageDialog dialog = DetailedMessageDialog.create(
211               dialogParent, STR_Dialog_DatabaseErrorTitle,
212               STR_Message_FetchIndexFailed, STR_Dialog_ErrorHeader,
213               ExceptionUtils.getDescriptionFor(ex, STR_Dialog_FirstExceptionTemplate,
214                 STR_Dialog_NextExceptionTemplate),
215               null);
216             dialog.show();
217           }
218         }
219         
220         public void databaseClosed(
221           DatabaseEvent ev)
222         {
223           SongListModel songListModel = (SongListModel)
224             songListModels_.remove(ev.getSongDatabase());
225           songListModel.clear();
226         }
227       });
228     
229     // Databases list
230     databasesList_ = new JList();
231     databasesList_.setModel(databaseManager_.getSwingListModel());
232     databasesList_.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
233     databasesList_.addListSelectionListener(
234       new ListSelectionListener()
235       {
236         public void valueChanged(
237           ListSelectionEvent ev)
238         {
239           DatabaseManagerImpl.Item old = selectedDatabase_;
240           selectedDatabase_ = (DatabaseManagerImpl.Item)databasesList_.getSelectedValue();
241           broadcaster_.fireEvent(SelectionListener.DATABASE_SELECTION_CHANGED_METHOD,
242             new DatabaseSelectionEvent(this,
243               old == null ? null : old.getDatabase(),
244               selectedDatabase_ == null ? null : selectedDatabase_.getDatabase()));
245           if (selectedDatabase_ == null)
246           {
247             ListModel oldModel = songsList_.getModel();
248             if (oldModel instanceof SongListModel)
249             {
250               ((SongListModel)oldModel).setSizeChangeListener(null);
251             }
252             songsList_.setModel(new DefaultListModel());
253             songsHeader_.setString(ResourceAccess.Strings.buildString("MainGui_EmptySongsHeader"));
254           }
255           else
256           {
257             ListModel oldModel = songsList_.getModel();
258             if (oldModel instanceof SongListModel)
259             {
260               ((SongListModel)oldModel).setSizeChangeListener(null);
261             }
262             SongListModel newModel = (SongListModel)songListModels_.get(selectedDatabase_.getDatabase());
263             songsList_.setModel(newModel);
264             newModel.setSizeChangeListener(songsHeaderUpdater_);
265             songsHeader_.setString(ResourceAccess.Strings.buildString("MainGui_SongsHeader",
266               new Integer(newModel.getSize()), new Integer(newModel.getTotalSize())));
267           }
268         }
269       });
270     databasesList_.setCellRenderer(
271       new DefaultListCellRenderer()
272       {
273         public Component getListCellRendererComponent(
274           JList list,
275           Object value,
276           int index,
277           boolean isSelected,
278           boolean cellHasFocus)
279         {
280           Component ret = super.getListCellRendererComponent(
281             list, value, index, isSelected, cellHasFocus);
282           setIcon(((DatabaseManagerImpl.Item)value).getIcon());
283           return ret;
284         }
285       });
286     databasesList_.addMouseListener(
287       new MouseAdapter()
288       {
289         public void mouseClicked(
290           MouseEvent ev)
291         {
292           if (ev.getClickCount() == 2)
293           {
294             int index = databasesList_.locationToIndex(ev.getPoint());
295             if (index >= 0 && index < databasesList_.getModel().getSize())
296             {
297               databaseManager_.getDatabaseHandler(index).handleDatabaseGetInfo();
298             }
299           }
300         }
301       });
302     databasesList_.addKeyListener(
303       new KeyAdapter()
304       {
305         public void keyPressed(
306           KeyEvent ev)
307         {
308           if (ev.getKeyCode() == KeyEvent.VK_ENTER && selectedDatabase_ != null)
309           {
310             selectedDatabase_.getHandler().handleDatabaseGetInfo();
311           }
312         }
313       });
314     
315     // Song list
316     songsList_ = new JList();
317     songsList_.addListSelectionListener(
318       new ListSelectionListener()
319       {
320         public void valueChanged(
321           ListSelectionEvent ev)
322         {
323           List oldSel = new ArrayList(selectedSongs_.length);
324           for (int i=0; i<selectedSongs_.length; ++i)
325           {
326             oldSel.add(selectedSongs_[i].getSongID());
327           }
328           Collection old = Arrays.asList(selectedSongs_);
329           Object[] list = songsList_.getSelectedValues();
330           selectedSongs_ = new SongListModel.Item[list.length];
331           System.arraycopy(list, 0, selectedSongs_, 0, list.length);
332           List newSel = new ArrayList(selectedSongs_.length);
333           for (int i=0; i<selectedSongs_.length; ++i)
334           {
335             newSel.add(selectedSongs_[i].getSongID());
336           }
337           SongDatabase seldb = null;
338           if (selectedDatabase_ != null)
339           {
340             seldb = selectedDatabase_.getDatabase();
341           }
342           broadcaster_.fireEvent(SelectionListener.SONG_SELECTION_CHANGED_METHOD,
343             new SongSelectionEvent(this, seldb, oldSel, newSel));
344           if (newSel.size() == 1)
345           {
346             setupInfoPane((SongID)newSel.get(0));
347           }
348           else
349           {
350             setupInfoPane(newSel.size());
351           }
352         }
353       });
354     songsList_.addMouseListener(
355       new MouseAdapter()
356       {
357         public void mouseClicked(
358           MouseEvent ev)
359         {
360           if (ev.getClickCount() == 2)
361           {
362             int index = songsList_.locationToIndex(ev.getPoint());
363             if (index >= 0 && index < songsList_.getModel().getSize() &&
364               selectedSongs_.length == 1)
365             {
366               commandManager_.doDefaultSongCommand();
367             }
368           }
369         }
370       });
371     songsList_.addKeyListener(
372       new KeyAdapter()
373       {
374         public void keyPressed(
375           KeyEvent ev)
376         {
377           if (ev.getKeyCode() == KeyEvent.VK_ENTER && selectedSongs_.length == 1)
378           {
379             commandManager_.doDefaultSongCommand();
380           }
381         }
382       });
383     selectedSongs_ = new SongListModel.Item[0];
384     
385     // Details panel
386     infoPanel_ = new JPanel(new GridBagLayout());
387     infoPanel_.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
388     ScrollablePanel infoPanelWrapper = new ScrollablePanel(new BorderLayout());
389     infoPanelWrapper.add(infoPanel_, BorderLayout.NORTH);
390     
391     // Assemble gui
392     JPanel databasesOuterPane = new JPanel(new BorderLayout());
393     databasesOuterPane.add(new HeaderPane(ResourceAccess.Strings.buildString(
394       "MainGui_DatabasesHeader")), BorderLayout.NORTH);
395     databasesOuterPane.add(new JScrollPane(databasesList_), BorderLayout.CENTER);
396     JPanel songsOuterPane = new JPanel(new BorderLayout());
397     songsHeader_ = new HeaderPane(ResourceAccess.Strings.buildString(
398       "MainGui_EmptySongsHeader"));
399     songsOuterPane.add(songsHeader_, BorderLayout.NORTH);
400     songsOuterPane.add(new JScrollPane(songsList_), BorderLayout.CENTER);
401     JPanel infoOuterPane = new JPanel(new BorderLayout());
402     infoOuterPane.add(new HeaderPane(ResourceAccess.Strings.buildString(
403       "MainGui_InfoHeader")), BorderLayout.NORTH);
404     infoOuterPane.add(new JScrollPane(infoPanelWrapper), BorderLayout.CENTER);
405     
406     // Songs header updater
407     songsHeaderUpdater_ = new SongListModel.SizeChangeListener()
408     {
409       public void sizeChanged(
410         SongListModel model)
411       {
412         songsHeader_.setString(ResourceAccess.Strings.buildString(
413           "MainGui_SongsHeader", new Integer(model.getSize()), new Integer(model.getTotalSize())));
414       }
415     };
416     
417     upperSplit_ = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
418       databasesOuterPane, songsOuterPane);
419     upperSplit_.setDividerSize(6);
420     listsPanel_ = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
421       upperSplit_, infoOuterPane);
422     listsPanel_.setDividerSize(6);
423     
424     broadcaster_ = new EventBroadcastHelper(SelectionListener.class);
425     
426     setupInfoPane(0);
427   }
428   
429   
430   private void updateInfoPane()
431   {
432     if (selectedSongs_.length == 1)
433     {
434       setupInfoPane(selectedSongs_[0].getSongID());
435     }
436     else
437     {
438       setupInfoPane(selectedSongs_.length);
439     }
440   }
441   
442   
443   private void setupInfoPane(
444     int numSongs)
445   {
446     infoPanel_.removeAll();
447     GridBagConstraints gbc = new GridBagConstraints();
448     gbc.gridx = 0;
449     gbc.weighty = 1;
450     gbc.weightx = 1;
451     gbc.insets = new Insets(5, 5, 0, 5);
452     gbc.anchor = GridBagConstraints.WEST;
453     gbc.fill = GridBagConstraints.HORIZONTAL;
454     
455     JTextArea field = new JTextArea(ResourceAccess.Strings.buildString(
456       "InfoPane_MultiSongs", new Integer(numSongs)));
457     field.setEditable(false);
458     field.setLineWrap(true);
459     field.setWrapStyleWord(true);
460     field.setFont(INFO_FONT);
461     field.setForeground(INFO_COLOR);
462     field.setBackground(infoPanel_.getBackground());
463     infoPanel_.add(field, gbc);
464     
465     infoPanel_.revalidate();
466   }
467   
468   
469   private void setupInfoPane(
470     SongID songID)
471   {
472     infoPanel_.removeAll();
473     if (songID != null)
474     {
475       GridBagConstraints gbc = new GridBagConstraints();
476       gbc.gridx = 0;
477       gbc.weighty = 1;
478       gbc.weightx = 1;
479       gbc.insets = new Insets(5, 5, 0, 5);
480       gbc.anchor = GridBagConstraints.WEST;
481       gbc.fill = GridBagConstraints.HORIZONTAL;
482       
483       SongDatabase database = songID.getDatabase();
484       SongIDResultSet resultSet = database.createEmptyResultSet();
485       resultSet.add(songID);
486       SongIDResultSet.Entry entry = resultSet.getEntryFor(songID);
487       
488       try
489       {
490         database.performOperation(
491           new GetFieldAsStringSemantics.DefaultImplementation(
492             Song.MAINTITLE_FIELD, songUtils_),
493           resultSet);
494         String str = (String)entry.getData();
495         if (str != null)
496         {
497           JTextArea field = new JTextArea(ResourceAccess.Strings.buildString(
498             "InfoPane_TitleTemplate", str));
499           field.setEditable(false);
500           field.setLineWrap(true);
501           field.setWrapStyleWord(true);
502           field.setFont(INFO_FONT);
503           field.setForeground(INFO_COLOR);
504           field.setBackground(infoPanel_.getBackground());
505           infoPanel_.add(field, gbc);
506         }
507       }
508       catch (SongDatabaseFailedException ex)
509       {
510       }
511       
512       if (showCommentDetail_)
513       {
514         try
515         {
516           database.performOperation(
517             new GetFieldAsStringSemantics.DefaultImplementation(
518               Song.COMMENT_FIELD, songUtils_),
519             resultSet);
520           String str = (String)entry.getData();
521           if (str != null && str.length() > 0)
522           {
523             JTextArea field = new JTextArea(ResourceAccess.Strings.buildString(
524               "InfoPane_CommentTemplate", str));
525             field.setEditable(false);
526             field.setLineWrap(true);
527             field.setWrapStyleWord(true);
528             field.setFont(INFO_FONT);
529             field.setForeground(INFO_COLOR);
530             field.setBackground(infoPanel_.getBackground());
531             infoPanel_.add(field, gbc);
532           }
533         }
534         catch (SongDatabaseFailedException ex)
535         {
536         }
537       }
538       
539       if (showAuthorDetail_)
540       {
541         try
542         {
543           database.performOperation(
544             new GetFieldAsStringSemantics.DefaultImplementation(
545               Song.AUTHOR_FIELD, songUtils_),
546             resultSet);
547           String str = (String)entry.getData();
548           if (str != null && str.length() > 0)
549           {
550             JTextArea field = new JTextArea(ResourceAccess.Strings.buildString(
551               "InfoPane_AuthorTemplate", str));
552             field.setEditable(false);
553             field.setLineWrap(true);
554             field.setWrapStyleWord(true);
555             field.setFont(INFO_FONT);
556             field.setForeground(INFO_COLOR);
557             field.setBackground(infoPanel_.getBackground());
558             infoPanel_.add(field, gbc);
559           }
560         }
561         catch (SongDatabaseFailedException ex)
562         {
563         }
564       }
565       
566       if (showCopyrightDetail_)
567       {
568         try
569         {
570           database.performOperation(
571             new GetFieldAsStringSemantics.DefaultImplementation(
572               Song.COPYRIGHT_FIELD, songUtils_),
573             resultSet);
574           String str = (String)entry.getData();
575           if (str != null && str.length() > 0)
576           {
577             JTextArea field = new JTextArea(ResourceAccess.Strings.buildString(
578               "InfoPane_CopyrightTemplate", str));
579             field.setEditable(false);
580             field.setLineWrap(true);
581             field.setWrapStyleWord(true);
582             field.setFont(INFO_FONT);
583             field.setForeground(INFO_COLOR);
584             field.setBackground(infoPanel_.getBackground());
585             infoPanel_.add(field, gbc);
586           }
587         }
588         catch (SongDatabaseFailedException ex)
589         {
590         }
591       }
592       
593       if (showAltTitlesDetail_)
594       {
595         try
596         {
597           database.performOperation(
598             new GetFieldAsStringArraySemantics.DefaultImplementation(
599               Song.ALTERNATETITLELIST_FIELD, songUtils_),
600             resultSet);
601           String[] array = (String[])entry.getData();
602           if (array != null && array.length > 0)
603           {
604             StringBuffer buf = new StringBuffer(STR_InfoPane_AltTitlesHeader);
605             for (int i=0; i<array.length; ++i)
606             {
607               buf.append('\n');
608               buf.append(array[i]);
609             }
610             JTextArea field = new JTextArea(new String(buf));
611             field.setEditable(false);
612             field.setLineWrap(true);
613             field.setWrapStyleWord(true);
614             field.setFont(INFO_FONT);
615             field.setForeground(INFO_COLOR);
616             field.setBackground(infoPanel_.getBackground());
617             infoPanel_.add(field, gbc);
618           }
619         }
620         catch (SongDatabaseFailedException ex)
621         {
622         }
623       }
624     }
625     infoPanel_.revalidate();
626   }
627   
628   
629   /*package*/ void shutdown()
630   {
631     detailPanePrefs_.putBoolean(SHOW_COMMENT_PREF, showCommentDetail_);
632     detailPanePrefs_.putBoolean(SHOW_AUTHOR_PREF, showAuthorDetail_);
633     detailPanePrefs_.putBoolean(SHOW_COPYRIGHT_PREF, showCopyrightDetail_);
634     detailPanePrefs_.putBoolean(SHOW_ALTTITLES_PREF, showAltTitlesDetail_);
635   }
636   
637   
638   /*package*/ boolean isShowingCommentDetail()
639   {
640     return showCommentDetail_;
641   }
642   
643   
644   /*package*/ boolean isShowingAuthorDetail()
645   {
646     return showAuthorDetail_;
647   }
648   
649   
650   /*package*/ boolean isShowingCopyrightDetail()
651   {
652     return showCopyrightDetail_;
653   }
654   
655   
656   /*package*/ boolean isShowingAltTitlesDetail()
657   {
658     return showAltTitlesDetail_;
659   }
660   
661   
662   /*package*/ void setShowingCommentDetail(
663     boolean value)
664   {
665     showCommentDetail_ = value;
666     updateInfoPane();
667   }
668   
669   
670   /*package*/ void setShowingAuthorDetail(
671     boolean value)
672   {
673     showAuthorDetail_ = value;
674     updateInfoPane();
675   }
676   
677   
678   /*package*/ void setShowingCopyrightDetail(
679     boolean value)
680   {
681     showCopyrightDetail_ = value;
682     updateInfoPane();
683   }
684   
685   
686   /*package*/ void setShowingAltTitlesDetail(
687     boolean value)
688   {
689     showAltTitlesDetail_ = value;
690     updateInfoPane();
691   }
692   
693   
694   /*package*/ void setTopSplitPanePosition(
695     int pos)
696   {
697     upperSplit_.setDividerLocation(pos);
698   }
699   
700   
701   /*package*/ int getTopSplitPanePosition()
702   {
703     return upperSplit_.getDividerLocation();
704   }
705   
706   
707   /*package*/ void setBottomSplitPanePosition(
708     int pos)
709   {
710     if (pos > 0)
711     {
712       listsPanel_.setDividerLocation(pos);
713     }
714   }
715   
716   
717   /*package*/ int getBottomSplitPanePosition()
718   {
719     return listsPanel_.getDividerLocation();
720   }
721   
722   
723   /*package*/ JComponent getJComponent()
724   {
725     return listsPanel_;
726   }
727   
728   
729   /*package*/ DatabaseHandler getSelectedDatabaseHandler()
730   {
731     if (selectedDatabase_ == null)
732     {
733       return null;
734     }
735     return selectedDatabase_.getHandler();
736   }
737   
738   
739   /*package*/ String getNameForSongID(
740     SongID id)
741   {
742     SongListModel songListModel = (SongListModel)songListModels_.get(id.getDatabase());
743     return songListModel.getNameForSongID(id);
744   }
745   
746   
747   /*package*/ void refreshDatabaseIndex(
748     SongDatabase database)
749   throws
750     SongDatabaseFailedException
751   {
752     SongListModel songListModel = (SongListModel)songListModels_.get(database);
753     songListModel.clear();
754     songListModel.addSongIDs(database.performOperation(
755       new NopSemantics.DefaultImplementation(), null));
756   }
757   
758   
759   /*package*/ void setCommandManager(
760     CommandManagerImpl commandManager)
761   {
762     commandManager_ = commandManager;
763   }
764   
765   
766   /*package*/ void setFilter(
767     PredicateSemantics filter)
768   {
769     if (!filter.equals(filterPredicate_))
770     {
771       filterPredicate_ = filter;
772       for (Iterator iter = songListModels_.values().iterator(); iter.hasNext(); )
773       {
774         SongListModel model = (SongListModel)iter.next();
775         model.setFilter(filter);
776       }
777     }
778   }
779   
780   
781   /*package*/ PredicateSemantics getFilter()
782   {
783     return filterPredicate_;
784   }
785   
786   
787   /*package*/ void requestFocus()
788   {
789     songsList_.requestFocus();
790   }
791   
792   
793   /**
794    * Get the currently selected database.
795    *
796    * @return a SongDatabase, or null for no selection
797    */
798   public SongDatabase getSelectedDatabase()
799   {
800     if (selectedDatabase_ == null)
801     {
802       return null;
803     }
804     return selectedDatabase_.getDatabase();
805   }
806   
807   
808   /**
809    * Get the Set of currently selected songs.
810    *
811    * @return a Set of SongIDs. Returns the empty set for no selection
812    */
813   public Set getSelectedSongSet()
814   {
815     Set set = new HashSet();
816     for (int i=0; i<selectedSongs_.length; ++i)
817     {
818       set.add(selectedSongs_[i].getSongID());
819     }
820     return set;
821   }
822   
823   
824   /**
825    * Select the specified database in the gui.
826    *
827    * @param database the database to select
828    * @exception IllegalArgumentException the given database is not open.
829    */
830   public void selectDatabase(
831     SongDatabase database)
832   {
833     int index = databaseManager_.findDatabaseIndex(database);
834     if (index == -1)
835     {
836       throw new IllegalArgumentException("Database not opened");
837     }
838     databasesList_.setSelectedIndex(index);
839   }
840   
841   
842   /**
843    * Add a SelectionListener to the main gui.
844    * Note that it is added as a weak listener, so make sure it is strongly
845    * referenced elsewhere.
846    *
847    * @param listener SelectionListener
848    */
849   public void addSelectionListener(
850     SelectionListener listener)
851   {
852     broadcaster_.addListenerWeak(listener);
853   }
854   
855   
856   /**
857    * Remove a SelectionListener from the main gui. Does nothing if the
858    * listener is not added.
859    *
860    * @param listener SelectionListener
861    */
862   public void removeSelectionListener(
863     SelectionListener listener)
864   {
865     broadcaster_.removeListener(listener);
866   }
867   
868   
869   /**
870    * Notify the main gui that a song has been added by this plugin. This
871    * causes the main gui to update its song list appropriately.
872    * It isn't strictly necessary to use this callback, but failure to
873    * do so may cause the main gui to get out of sync with the database
874    * contents until the user explicitly refreshes the index.
875    *
876    * @param songID SongID of the added song.
877    */
878   public void reportSongAdded(
879     SongID songID)
880   {
881     SongListModel songListModel = (SongListModel)songListModels_.get(songID.getDatabase());
882     if (songListModel != null)
883     {
884       int index = songListModel.addSongID(songID);
885       if (index != -1 && songID.getDatabase().equals(selectedDatabase_.getDatabase()))
886       {
887         songsList_.setSelectedIndex(index);
888         songsList_.scrollRectToVisible(songsList_.getCellBounds(index, index)); 
889       }
890     }
891   }
892   
893   
894   /**
895    * Notify the main gui that a song has been removed by this plugin. This
896    * causes the main gui to update its song list appropriately.
897    * It isn't strictly necessary to use this callback, but failure to
898    * do so may cause the main gui to get out of sync with the database
899    * contents until the user explicitly refreshes the index.
900    *
901    * @param songID SongID of the removed song.
902    */
903   public void reportSongRemoved(
904     SongID songID)
905   {
906     SongListModel songListModel = (SongListModel)songListModels_.get(songID.getDatabase());
907     if (songListModel != null)
908     {
909       songListModel.removeSongID(songID);
910     }
911   }
912   
913   
914   /**
915    * Notify the main gui that a song has been changed by this plugin. This
916    * causes the main gui to update its song list appropriately.
917    * It isn't strictly necessary to use this callback, but failure to
918    * do so may cause the main gui to get out of sync with the database
919    * contents until the user explicitly refreshes the index.
920    *
921    * @param songID SongID of the changed song.
922    */
923   public void reportSongChanged(
924     SongID songID)
925   {
926     SongListModel songListModel = (SongListModel)songListModels_.get(songID.getDatabase());
927     if (songListModel != null)
928     {
929       songListModel.updateSongID(songID);
930       updateInfoPane();
931     }
932   }
933   
934   
935   /*package*/ static class HeaderPane
936   extends JComponent
937   {
938     private String str_;
939     private static final Font font_ = new Font("Dialog", Font.BOLD, 10);
940     
941     
942     private HeaderPane(
943       String str)
944     {
945       str_ = str;
946       setPreferredSize(new Dimension(0, 18));
947     }
948     
949     public void paintComponent(
950       Graphics g)
951     {
952       Graphics2D gr = (Graphics2D)g;
953       gr.setColor(Color.gray);
954       gr.fill3DRect(0, 0, getWidth(), getHeight(), true);
955       gr.setColor(Color.white);
956       gr.setFont(font_);
957       gr.drawString(str_, 10, 13);
958     }
959     
960     /*package*/ void setString(
961       String str)
962     {
963       str_ = str;
964       repaint();
965     }
966   }
967 }