001: /*
002: * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package com.jgoodies.binding.tutorial.manager;
032:
033: import java.awt.event.ActionEvent;
034: import java.awt.event.MouseAdapter;
035: import java.awt.event.MouseEvent;
036: import java.awt.event.MouseListener;
037: import java.beans.PropertyChangeEvent;
038: import java.beans.PropertyChangeListener;
039:
040: import javax.swing.AbstractAction;
041: import javax.swing.Action;
042: import javax.swing.SwingUtilities;
043:
044: import com.jgoodies.binding.list.SelectionInList;
045: import com.jgoodies.binding.tutorial.Album;
046:
047: /**
048: * Provides the models and Actions for managing and editing Albums.
049: * Works with an underlying AlbumManager that provides a ListModel
050: * for the Albums and operations to add, remove, and change a Album.
051: * In other words, this class turns the raw data and operations
052: * form the AlbumManager into a form usable in a user interface.<p>
053: *
054: * This model keeps the Albums in a SelectionInList, refers to another
055: * PresentationModel for editing the selected Album, and provides
056: * Actions for the Album operations: add, remove and edit the selected Album.
057: *
058: * @author Karsten Lentzsch
059: * @version $Revision: 1.14 $
060: *
061: * @see AlbumManager
062: * @see com.jgoodies.binding.PresentationModel
063: */
064: public final class AlbumManagerModel {
065:
066: /**
067: * Holds the List of Albums and provides operations
068: * to create, add, remove and change a Album.
069: */
070: private final AlbumManager albumManager;
071:
072: /**
073: * Holds the list of managed albums plus a single selection.
074: */
075: private SelectionInList<Album> albumSelection;
076:
077: private Action newAction;
078: private Action editAction;
079: private Action deleteAction;
080:
081: // Instance Creation ******************************************************
082:
083: /**
084: * Constructs an AlbumManager for editing the given list of Albums.
085: *
086: * @param albumManager the list of albums to edit
087: */
088: public AlbumManagerModel(AlbumManager albumManager) {
089: this .albumManager = albumManager;
090: initModels();
091: initEventHandling();
092: }
093:
094: /**
095: * Initializes the SelectionInList and Action.
096: * In this case we eagerly initialize the Actions.
097: * As an alternative you can create the Actions lazily
098: * in the Action getter methods. To synchronize the Action enablement
099: * with the selection state, we update the enablement now.
100: */
101: private void initModels() {
102: albumSelection = new SelectionInList<Album>(albumManager
103: .getManagedAlbums());
104:
105: newAction = new NewAction();
106: editAction = new EditAction();
107: deleteAction = new DeleteAction();
108: updateActionEnablement();
109: }
110:
111: /**
112: * Initializes the event handling by just registering a
113: * handler that updates the Action enablement if the
114: * albumSelection's 'selectionEmpty' property changes.
115: */
116: private void initEventHandling() {
117: albumSelection.addPropertyChangeListener(
118: SelectionInList.PROPERTYNAME_SELECTION_EMPTY,
119: new SelectionEmptyHandler());
120: }
121:
122: // Exposing Models and Actions ********************************************
123:
124: /**
125: * Returns the List of Albums with the current selection.
126: * Useful to display the managed Albums in a JList or JTable.
127: *
128: * @return the List of Albums with selection
129: */
130: public SelectionInList<Album> getAlbumSelection() {
131: return albumSelection;
132: }
133:
134: /**
135: * Returns the Action that creates a new Album and adds it
136: * to this model's List of managed Albums. Opens a AlbumEditorDialog
137: * on the newly created Album.
138: *
139: * @return the Action that creates and adds a new Album
140: */
141: public Action getNewAction() {
142: return newAction;
143: }
144:
145: /**
146: * Returns the Action that opens a AlbumEditorDialog on the selected Album.
147: *
148: * @return the Action that opens a AlbumEditorDialog on the selected Album
149: */
150: public Action getEditAction() {
151: return editAction;
152: }
153:
154: /**
155: * Returns the Action that deletes the selected Album from
156: * this model's List of managed albums.
157: *
158: * @return The Action that deletes the selected Album
159: */
160: public Action getDeleteAction() {
161: return deleteAction;
162: }
163:
164: /**
165: * Returns a MouseListener that selects and edits a Album on double-click.
166: *
167: * @return a MouseListener that selects and edits a Album on double-click.
168: */
169: public MouseListener getDoubleClickHandler() {
170: return new DoubleClickHandler();
171: }
172:
173: // Action Operations ******************************************************
174: // For every Action we provide a method that is performed for this Action.
175: // This makes it easier to overview this class.
176:
177: private void doNew() {
178: Album newAlbum = createAndAddItem();
179: getAlbumSelection().setSelection(newAlbum);
180: }
181:
182: /**
183: * Edits the selected item and marks it as changed,
184: * if the editor dialog has not been canceled.
185: */
186: private void doEdit() {
187: editSelectedItem();
188: }
189:
190: /**
191: * Lets the AlbumManager removes the selected Album from the list of Albums.
192: * The AlbumManager fires the list data change event. If the AlbumManager
193: * wouldn't fire this event, we could use
194: * {@link SelectionInList#fireIntervalRemoved(int, int)}.
195: */
196: private void doDelete() {
197: albumManager.removeItem(getSelectedItem());
198: }
199:
200: // Managing Albums ********************************************************
201:
202: /**
203: * Lets the AlbumManager add the given Album to the list of Albums.
204: * The AlbumManager fires the list data change event. If the AlbumManager
205: * won't fire this event, we could use
206: * {@link SelectionInList#fireIntervalAdded(int, int)}.
207: */
208: private void addItem(Album albumToAdd) {
209: albumManager.addItem(albumToAdd);
210: }
211:
212: /**
213: * Opens a AlbumEditorDialog for the given Album.
214: *
215: * @param album the Album to be edited
216: * @return true if the dialog has been canceled, false if accepted
217: */
218: private boolean openAlbumEditor(Album album) {
219: AlbumEditorDialog dialog = new AlbumEditorDialog(null, album);
220: dialog.open();
221: return dialog.hasBeenCanceled();
222: }
223:
224: private Album createAndAddItem() {
225: Album newAlbum = albumManager.createItem();
226: boolean canceled = openAlbumEditor(newAlbum);
227: if (!canceled) {
228: addItem(newAlbum);
229: return newAlbum;
230: }
231: return null;
232: }
233:
234: /**
235: * Edits the selected item. If the editor dialog has not been canceled,
236: * the presentations is notified that the contents has changed.<p>
237: *
238: * This implementation fires the contents change event using
239: * {@link SelectionInList#fireSelectedContentsChanged()}.
240: * Since the album SelectionInList contains a ListModel,
241: * the <code>albumSelection</code> managed by the AlbumManager,
242: * the AlbumManager could fire that event. However, I favored to fire
243: * the contents change in the SelectionInList because this approach
244: * works with underlying Lists, ListModels, and managers that don't
245: * fire contents changes.
246: */
247: private void editSelectedItem() {
248: boolean canceled = openAlbumEditor(getSelectedItem());
249: if (!canceled) {
250: getAlbumSelection().fireSelectedContentsChanged();
251: }
252: }
253:
254: private Album getSelectedItem() {
255: return getAlbumSelection().getSelection();
256: }
257:
258: // Actions ****************************************************************
259:
260: private final class NewAction extends AbstractAction {
261:
262: private NewAction() {
263: super ("New\u2026");
264: }
265:
266: public void actionPerformed(ActionEvent e) {
267: doNew();
268: }
269: }
270:
271: private final class EditAction extends AbstractAction {
272:
273: private EditAction() {
274: super ("Edit\u2026");
275: }
276:
277: public void actionPerformed(ActionEvent e) {
278: doEdit();
279: }
280: }
281:
282: private final class DeleteAction extends AbstractAction {
283:
284: private DeleteAction() {
285: super ("Delete");
286: }
287:
288: public void actionPerformed(ActionEvent e) {
289: doDelete();
290: }
291: }
292:
293: // Event Handling *********************************************************
294:
295: /**
296: * A mouse listener that edits the selected item on double click.
297: */
298: private final class DoubleClickHandler extends MouseAdapter {
299: @Override
300: public void mouseClicked(MouseEvent e) {
301: if (SwingUtilities.isLeftMouseButton(e)
302: && e.getClickCount() == 2)
303: editSelectedItem();
304: }
305: }
306:
307: private void updateActionEnablement() {
308: boolean hasSelection = getAlbumSelection().hasSelection();
309: getEditAction().setEnabled(hasSelection);
310: getDeleteAction().setEnabled(hasSelection);
311: }
312:
313: /**
314: * Enables or disables this model's Actions when it is notified
315: * about a change in the <em>selectionEmpty</em> property
316: * of the SelectionInList.
317: */
318: private final class SelectionEmptyHandler implements
319: PropertyChangeListener {
320:
321: public void propertyChange(PropertyChangeEvent evt) {
322: updateActionEnablement();
323: }
324: }
325:
326: }
|