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.basics;
032:
033: import java.util.List;
034:
035: import javax.swing.*;
036: import javax.swing.text.JTextComponent;
037:
038: import com.jgoodies.binding.PresentationModel;
039: import com.jgoodies.binding.adapter.BasicComponentFactory;
040: import com.jgoodies.binding.list.SelectionInList;
041: import com.jgoodies.binding.tutorial.Album;
042: import com.jgoodies.binding.tutorial.TutorialUtils;
043: import com.jgoodies.binding.value.ConverterFactory;
044: import com.jgoodies.binding.value.DelayedReadValueModel;
045: import com.jgoodies.forms.builder.PanelBuilder;
046: import com.jgoodies.forms.factories.ButtonBarFactory;
047: import com.jgoodies.forms.layout.CellConstraints;
048: import com.jgoodies.forms.layout.FormLayout;
049:
050: /**
051: * Demonstrates an elegant means how to defer updates of a details view
052: * after selecting an element in a master list. This can be useful
053: * if changing the details requires time consuming additional operations,
054: * for example a database lookup, a heavy computation, or a remote access.<p>
055: *
056: * This class builds a JList of Albums with an attached details panel
057: * that presents the current Album selection. The details panel's components
058: * are bound to the domain using ValueModels returned by a PresentationModel.
059: * The master JList is bound to a SelectionInList that holds the list of Albums
060: * and the currently selected Album. The SelectionInList is used as bean channel
061: * for the details PresentationModel. And so, whenever the SelectionInList's
062: * selection changes, the details PresentationModel will automatically update
063: * the bean used to display the details.<p>
064: *
065: * This example implements the deferred details update by using a delayed
066: * version of the SelectionInList as the presentation model's bean channel.
067: * Changes in the SelectionInList's selection are held back for a specified
068: * delay before the selection becomes the presentation model's new bean.
069: *
070: * @author Karsten Lentzsch
071: * @version $Revision: 1.10 $
072: *
073: * @see #initComponents()
074: * @see DelayedReadValueModel
075: * @see com.jgoodies.binding.PresentationModel
076: * @see com.jgoodies.binding.tutorial.basics.MasterDetailsSelectionInListExample
077: */
078: public final class MasterDetailsDelayedReadExample {
079:
080: /**
081: * The default in milliseconds to wait before a selection change
082: * shall be forwarded to the delayed details presentation model.
083: */
084: private static final int DEFAULT_DELAY = 1000;
085:
086: /**
087: * Holds the list of Albums plus a single selection.
088: */
089: private final SelectionInList<Album> albumSelection;
090:
091: /**
092: * Holds the selected Album and vends ValueModels that adapt Album properties.
093: */
094: private final PresentationModel<Album> detailsModel;
095:
096: private JList albumsList;
097: private JTextComponent titleField;
098: private JTextComponent artistField;
099: private JTextComponent classicalField;
100: private JTextComponent composerField;
101: private JButton closeButton;
102:
103: // Launching **************************************************************
104:
105: public static void main(String[] args) {
106: try {
107: UIManager
108: .setLookAndFeel("com.jgoodies.looks.plastic.PlasticXPLookAndFeel");
109: } catch (Exception e) {
110: // Likely PlasticXP is not in the class path; ignore.
111: }
112: JFrame frame = new JFrame();
113: frame.setTitle("Binding Tutorial :: Master/Details (Delayed)");
114: frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
115: JComponent panel = new MasterDetailsDelayedReadExample()
116: .buildPanel();
117: frame.getContentPane().add(panel);
118: frame.pack();
119: TutorialUtils.locateOnOpticalScreenCenter(frame);
120: frame.setVisible(true);
121: }
122:
123: // Instance Creation ******************************************************
124:
125: /**
126: * Constructs a delayed list editor using an example Album list
127: * and the default delay.
128: */
129: public MasterDetailsDelayedReadExample() {
130: this (Album.ALBUMS);
131: }
132:
133: /**
134: * Constructs a delayed list editor for editing the given list of Albums
135: * using the default delay.
136: *
137: * @param albums the list of Albums to edit
138: */
139: public MasterDetailsDelayedReadExample(List<Album> albums) {
140: this (albums, DEFAULT_DELAY);
141: }
142:
143: /**
144: * Constructs a list editor for editing the given list of Albums
145: * using the specified delay.
146: *
147: * @param albums the list of Albums to edit
148: * @param delay the milliseconds to wait before the album selection
149: * is forwarded to the SelectionInList
150: */
151: public MasterDetailsDelayedReadExample(List<Album> albums, int delay) {
152: this .albumSelection = new SelectionInList<Album>(albums);
153:
154: // Uses the delayed SelectionInList as PresentationModel's bean channel.
155: // The SelectionInList is wrapped so that selection updates will be
156: // deferred for the specified delay. In other words, the list's
157: // selection becomes the Presentation Model's new bean - after a delay.
158: detailsModel = new PresentationModel<Album>(
159: new DelayedReadValueModel(albumSelection
160: .getSelectionHolder(), delay, true));
161: }
162:
163: // Component Creation and Initialization **********************************
164:
165: /**
166: * Creates and initializes the UI components. The JList used to present
167: * the Album list is bound directly to the SelectionInList which
168: * implements the required ListModel interface. Even the JList's
169: * selection model is bound directly bound the the SelectionInList's
170: * selection index holder. It's just that the presentation model's
171: * bean channel is not the selection, but a delayed selection.<p>
172: *
173: * If the user selects an element in the Album JList, the selection model
174: * changes immediately. It's just that the bean of the detailsModel will
175: * be set a bit later, which in turn holds back details change a bit.
176: * See the constructor to study how the master SelectionInList is wrapped
177: * by a DelayedReadValueModel to hold back selection changes for a limited
178: * amount of time.
179: */
180: private void initComponents() {
181: albumsList = BasicComponentFactory.createList(albumSelection,
182: TutorialUtils.createAlbumListCellRenderer());
183:
184: titleField = BasicComponentFactory.createTextField(detailsModel
185: .getModel(Album.PROPERTYNAME_TITLE));
186: titleField.setEditable(false);
187: artistField = BasicComponentFactory
188: .createTextField(detailsModel
189: .getModel(Album.PROPERTYNAME_ARTIST));
190: artistField.setEditable(false);
191: classicalField = BasicComponentFactory
192: .createTextField(ConverterFactory
193: .createBooleanToStringConverter(
194: detailsModel
195: .getModel(Album.PROPERTYNAME_CLASSICAL),
196: "Yes", "No"));
197: classicalField.setEditable(false);
198: composerField = BasicComponentFactory
199: .createTextField(detailsModel
200: .getModel(Album.PROPERTYNAME_COMPOSER));
201: composerField.setEditable(false);
202: closeButton = new JButton(TutorialUtils.getCloseAction());
203: }
204:
205: // Building ***************************************************************
206:
207: /**
208: * Builds and returns a panel that consists of
209: * a master list and a details form.
210: *
211: * @return the built panel
212: */
213: public JComponent buildPanel() {
214: initComponents();
215:
216: FormLayout layout = new FormLayout(
217: "right:pref, 3dlu, 150dlu:grow",
218: "p, 1dlu, p, 9dlu, p, 1dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 9dlu, p");
219:
220: PanelBuilder builder = new PanelBuilder(layout);
221: builder.setDefaultDialogBorder();
222: CellConstraints cc = new CellConstraints();
223:
224: builder.addSeparator("Albums", cc.xyw(1, 1, 3));
225: builder.add(new JScrollPane(albumsList), cc.xy(3, 3));
226:
227: builder.addSeparator("Delayed Details", cc.xyw(1, 5, 3));
228: builder.addLabel("Artist", cc.xy(1, 7));
229: builder.add(artistField, cc.xy(3, 7));
230: builder.addLabel("Title", cc.xy(1, 9));
231: builder.add(titleField, cc.xy(3, 9));
232: builder.addLabel("Classical", cc.xy(1, 11));
233: builder.add(classicalField, cc.xy(3, 11));
234: builder.addLabel("Composer", cc.xy(1, 13));
235: builder.add(composerField, cc.xy(3, 13));
236: builder.add(buildButtonBar(), cc.xyw(1, 15, 3));
237:
238: return builder.getPanel();
239: }
240:
241: private JComponent buildButtonBar() {
242: return ButtonBarFactory.buildRightAlignedBar(closeButton);
243: }
244:
245: }
|