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.event.ListSelectionEvent;
037: import javax.swing.event.ListSelectionListener;
038: import javax.swing.text.JTextComponent;
039:
040: import com.jgoodies.binding.PresentationModel;
041: import com.jgoodies.binding.adapter.BasicComponentFactory;
042: import com.jgoodies.binding.tutorial.Album;
043: import com.jgoodies.binding.tutorial.TutorialUtils;
044: import com.jgoodies.binding.value.ConverterFactory;
045: import com.jgoodies.binding.value.ValueHolder;
046: import com.jgoodies.forms.builder.PanelBuilder;
047: import com.jgoodies.forms.factories.ButtonBarFactory;
048: import com.jgoodies.forms.layout.CellConstraints;
049: import com.jgoodies.forms.layout.FormLayout;
050:
051: /**
052: * Demonstrates a "hand-made" way how to connect a master list with a bound
053: * details view. It builds a JList of Albums with an attached details panel
054: * that presents the current Album selection. The details panel's components
055: * are bound to the domain using ValueModels returned by a PresentationModel.<p>
056: *
057: * This example handles selection changes with a custom ListSelectionListener,
058: * the AlbumSelectionHandler, that sets the JList's selected values as new
059: * bean of the details PresentationModel. A simpler means to achieve the same
060: * effect is demonstrated by the MasterDetailsSelectionInListExample that uses
061: * the SelectionInList as bean channel for the details PresentationModel.<p>
062: *
063: * Another variant of this example is the MasterDetailsCopyingExample
064: * that copies the details data on list selection changes, instead of binding
065: * the details UI components to the details PresentationModel's ValueModels.
066: *
067: * @author Karsten Lentzsch
068: * @version $Revision: 1.14 $
069: *
070: * @see com.jgoodies.binding.PresentationModel
071: * @see com.jgoodies.binding.tutorial.basics.MasterDetailsCopyingExample
072: * @see com.jgoodies.binding.tutorial.basics.MasterDetailsSelectionInListExample
073: */
074: public final class MasterDetailsBoundExample {
075:
076: /**
077: * The Albums displayed in the master list.
078: */
079: private final List<Album> albums;
080:
081: /**
082: * Holds the edited Album and vends ValueModels that adapt Album properties.
083: */
084: private final PresentationModel<Album> detailsModel;
085:
086: private JList albumsList;
087: private JTextComponent titleField;
088: private JTextComponent artistField;
089: private JTextComponent classicalField;
090: private JTextComponent composerField;
091: private JButton closeButton;
092:
093: // Launching **************************************************************
094:
095: public static void main(String[] args) {
096: try {
097: UIManager
098: .setLookAndFeel("com.jgoodies.looks.plastic.PlasticXPLookAndFeel");
099: } catch (Exception e) {
100: // Likely PlasticXP is not in the class path; ignore.
101: }
102: JFrame frame = new JFrame();
103: frame.setTitle("Binding Tutorial :: Master/Details (Bound)");
104: frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
105: JComponent panel = new MasterDetailsBoundExample().buildPanel();
106: frame.getContentPane().add(panel);
107: frame.pack();
108: TutorialUtils.locateOnOpticalScreenCenter(frame);
109: frame.setVisible(true);
110: }
111:
112: // Instance Creation ******************************************************
113:
114: /**
115: * Constructs a list editor using a example Album list.
116: */
117: public MasterDetailsBoundExample() {
118: this (Album.ALBUMS);
119: }
120:
121: /**
122: * Constructs a list editor for editing the given list of Albums.
123: *
124: * @param albums the list of Albums to edit
125: */
126: public MasterDetailsBoundExample(List<Album> albums) {
127: this .albums = albums;
128: detailsModel = new PresentationModel<Album>(new ValueHolder(
129: null, true));
130: }
131:
132: // Component Creation and Initialization **********************************
133:
134: /**
135: * Creates, binds, and configures the UI components.
136: * All components in the details view are read-only.<p>
137: *
138: * The coding style used here is based on standard Swing components.
139: * Therefore we can create and bind the components in one step.
140: * And that's the purpose of the BasicComponentFactory class.<p>
141: *
142: * If you need to bind custom components, for example MyTextField,
143: * MyCheckBox, MyComboBox, you can use the more basic Bindings class.
144: * The code would then read:<pre>
145: * titleField = new MyTextField();
146: * Bindings.bind(titleField,
147: * detailsModel.getModel(Album.PROPERTYNAME_TITLE));
148: * </pre><p>
149: *
150: * I strongly recommend to use either the BasicComponentFactory or
151: * the Bindings class. These classes hide details of the binding.
152: * So you better <em>not</em> write the following code:<pre>
153: * titleField = new JTextField();
154: * titleField.setDocument(new DocumentAdapter(
155: * detailsModel.getModel(Album.PROPERTYNAME_TITLE)));
156: * </pre>
157: */
158: private void initComponents() {
159: albumsList = new JList(albums.toArray());
160: albumsList
161: .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
162: albumsList.setCellRenderer(TutorialUtils
163: .createAlbumListCellRenderer());
164:
165: titleField = BasicComponentFactory.createTextField(detailsModel
166: .getModel(Album.PROPERTYNAME_TITLE));
167: titleField.setEditable(false);
168: artistField = BasicComponentFactory
169: .createTextField(detailsModel
170: .getModel(Album.PROPERTYNAME_ARTIST));
171: artistField.setEditable(false);
172: classicalField = BasicComponentFactory
173: .createTextField(ConverterFactory
174: .createBooleanToStringConverter(
175: detailsModel
176: .getModel(Album.PROPERTYNAME_CLASSICAL),
177: "Yes", "No"));
178: classicalField.setEditable(false);
179: composerField = BasicComponentFactory
180: .createTextField(detailsModel
181: .getModel(Album.PROPERTYNAME_COMPOSER));
182: composerField.setEditable(false);
183: closeButton = new JButton(TutorialUtils.getCloseAction());
184: }
185:
186: private void initEventHandling() {
187: albumsList
188: .addListSelectionListener(new AlbumSelectionHandler());
189: }
190:
191: // Building ***************************************************************
192:
193: /**
194: * Builds and returns a panel that consists of
195: * a master list and a details form.
196: *
197: * @return the built panel
198: */
199: public JComponent buildPanel() {
200: initComponents();
201: initEventHandling();
202:
203: FormLayout layout = new FormLayout(
204: "right:pref, 3dlu, 150dlu:grow",
205: "p, 1dlu, p, 9dlu, p, 1dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 9dlu, p");
206:
207: PanelBuilder builder = new PanelBuilder(layout);
208: builder.setDefaultDialogBorder();
209: CellConstraints cc = new CellConstraints();
210:
211: builder.addSeparator("Albums", cc.xyw(1, 1, 3));
212: builder.add(new JScrollPane(albumsList), cc.xy(3, 3));
213:
214: builder.addSeparator("Details", cc.xyw(1, 5, 3));
215: builder.addLabel("Artist", cc.xy(1, 7));
216: builder.add(artistField, cc.xy(3, 7));
217: builder.addLabel("Title", cc.xy(1, 9));
218: builder.add(titleField, cc.xy(3, 9));
219: builder.addLabel("Classical", cc.xy(1, 11));
220: builder.add(classicalField, cc.xy(3, 11));
221: builder.addLabel("Composer", cc.xy(1, 13));
222: builder.add(composerField, cc.xy(3, 13));
223: builder.add(buildButtonBar(), cc.xyw(1, 15, 3));
224:
225: return builder.getPanel();
226: }
227:
228: private JComponent buildButtonBar() {
229: return ButtonBarFactory.buildRightAlignedBar(closeButton);
230: }
231:
232: // Event Handling ********************************************************
233:
234: /**
235: * Sets the selected album as bean in the details model.
236: */
237: private final class AlbumSelectionHandler implements
238: ListSelectionListener {
239:
240: public void valueChanged(ListSelectionEvent e) {
241: if (e.getValueIsAdjusting())
242: return;
243: detailsModel.setBean((Album) albumsList.getSelectedValue());
244: }
245: }
246:
247: }
|