001/*
002 * Copyright (c) 2004-2007 by Michael Connor. 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 FormLayoutBuilder or Michael Connor 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 */
030package org.mlc.swing.layout;
031
032import java.awt.Component;
033import java.awt.LayoutManager2;
034import java.beans.BeanInfo;
035import java.beans.Introspector;
036import java.beans.PropertyDescriptor;
037import java.lang.reflect.Method;
038import java.util.ArrayList;
039import java.util.Collections;
040import java.util.HashMap;
041import java.util.Iterator;
042import java.util.LinkedHashMap;
043import java.util.List;
044import java.util.Map;
045import java.util.StringTokenizer;
046
047import com.jgoodies.forms.layout.CellConstraints;
048import com.jgoodies.forms.layout.FormLayout;
049
050/**
051 * This class acts as a surrogate layout manager for the JGoodies
052 * (www.jgoodies.com) FormLayout manager. This layout manager enables us to
053 * associate names with components and then define the constraints for the
054 * component elsewhere (like xml)
055 *
056 * @author Michael Connor
057@version $Id$
058@since Ptolemy II 8.0
059 */
060public class ContainerLayout implements LayoutManager2 {
061    java.util.List<String> rowSpecs = new ArrayList<String>();
062
063    java.util.List<String> columnSpecs = new ArrayList<String>();
064
065    LinkedHashMap<String, CellConstraints> componentConstraints = new LinkedHashMap<String, CellConstraints>();
066
067    Map<Component, String> componentsToNames = new HashMap<Component, String>();
068
069    Map<String, Map<String, Object>> componentNameToCustomProps = new HashMap<String, Map<String, Object>>();
070
071    Map<String, ComponentDef> componentNameToComponentDef = new HashMap<String, ComponentDef>();
072
073    FormLayout formLayout;
074
075    String name;
076
077    public ContainerLayout(String name, String columnSpecs, String rowSpecs) {
078        this.name = name;
079        StringTokenizer cols = new StringTokenizer(columnSpecs, ",", false);
080        StringTokenizer rows = new StringTokenizer(rowSpecs, ",", false);
081
082        while (cols.hasMoreTokens()) {
083            this.columnSpecs.add(cols.nextToken());
084        }
085
086        while (rows.hasMoreTokens()) {
087            this.rowSpecs.add(rows.nextToken());
088        }
089
090        formLayout = new FormLayout(columnSpecs, rowSpecs);
091
092    }
093
094    private void buildLayout() throws IllegalArgumentException {
095        formLayout = new FormLayout(delimit(columnSpecs), delimit(rowSpecs));
096
097        // now we have to add all of the compenents to the new form
098        for (Object element : componentsToNames.keySet()) {
099            Component component = (Component) element;
100            String componentName = componentsToNames.get(component);
101            CellConstraints constraints = componentConstraints
102                    .get(componentName);
103            formLayout.addLayoutComponent(component, constraints);
104        }
105    }
106
107    private String delimit(List<String> values) {
108        StringBuffer buffer = new StringBuffer();
109
110        for (int index = 0; index < values.size(); index++) {
111            buffer.append(values.get(index));
112            if (index < values.size() - 1) {
113                buffer.append(",");
114            }
115        }
116
117        return buffer.toString();
118    }
119
120    protected Map<Component, String> getComponentsToNames() {
121        return Collections.unmodifiableMap(componentsToNames);
122    }
123
124    /**
125     * Registers the value of the name property
126     *
127     * @param name
128     *          The value of the property
129     */
130    public void setName(String name) {
131        this.name = name;
132    }
133
134    /**
135     * Returns the value of the name property
136     *
137     * @return The value
138     */
139    public String getName() {
140        return this.name;
141    }
142
143    /**
144     * Returns custom properties for the component. If no custom props exist then
145     * an empty map will be returned.
146     */
147    public Map<String, Object> getCustomProperties(String componentName) {
148        return componentNameToCustomProps.containsKey(componentName)
149                ? componentNameToCustomProps.get(componentName)
150                : new HashMap<String, Object>();
151    }
152
153    /**
154     * Set a user defined property for this component so that the tool can manage
155     * the properties of the component thus reducing the burden on the user
156     */
157    public void setProperty(String componentName, String property,
158            Object value) {
159        Map customProps = componentNameToCustomProps.get(componentName);
160        if (customProps == null) {
161            customProps = new HashMap<String, Object>();
162            componentNameToCustomProps.put(componentName, customProps);
163        }
164        customProps.put(property, value);
165    }
166
167    public void setCellConstraints(String componentName,
168            CellConstraints constraints) {
169        componentConstraints.put(name, constraints);
170
171        for (Object element : componentsToNames.keySet()) {
172            Component component = (Component) element;
173            String thisName = componentsToNames.get(component);
174            if (thisName.equals(componentName)) {
175                formLayout.setConstraints(component, constraints);
176                break;
177            }
178        }
179    }
180
181    public LinkedHashMap<String, CellConstraints> getCellConstraints() {
182        return componentConstraints;
183    }
184
185    public void addComponent(String componentName, ComponentDef componentDef,
186            CellConstraints constraints) {
187        componentConstraints.put(componentName, constraints);
188        componentNameToComponentDef.put(componentName, componentDef);
189    }
190
191    public ComponentDef getComponentDef(String componentName) {
192        return componentNameToComponentDef.get(componentName);
193    }
194
195    public String getColumnSpecsString() {
196        StringBuffer buffer = new StringBuffer();
197        for (Iterator i = columnSpecs.iterator(); i.hasNext();) {
198            buffer.append(i.next());
199            if (i.hasNext()) {
200                buffer.append(",");
201            }
202        }
203
204        return buffer.toString();
205    }
206
207    public String getRowSpecsString() {
208        StringBuffer buffer = new StringBuffer();
209        for (Iterator i = rowSpecs.iterator(); i.hasNext();) {
210            buffer.append(i.next());
211            if (i.hasNext()) {
212                buffer.append(",");
213            }
214        }
215
216        return buffer.toString();
217    }
218
219    public int getRowCount() {
220        return rowSpecs.size();
221    }
222
223    public int getColumnCount() {
224        return columnSpecs.size();
225    }
226
227    public List<String> getRowSpecs() {
228        return this.rowSpecs;
229    }
230
231    public List<String> getColumnSpecs() {
232        return this.columnSpecs;
233    }
234
235    public void constraintsChanged(String name, CellConstraints constraints) {
236        componentConstraints.put(name, constraints);
237    }
238
239    public CellConstraints getCellConstraints(String name) {
240        return componentConstraints.get(name);
241    }
242
243    public void addCellConstraints(String name, CellConstraints constraints) {
244        componentConstraints.put(name, constraints);
245    }
246
247    public CellConstraints removeCellConstraints(String name) {
248        CellConstraints constraints = componentConstraints.remove(name);
249        return constraints;
250    }
251
252    public void addColumnSpec(String columnSpec)
253            throws IllegalArgumentException {
254        columnSpecs.add(columnSpec);
255        buildLayout();
256    }
257
258    public String getRowSpec(int index) {
259        return rowSpecs.get(index);
260    }
261
262    public String getColumnSpec(int index) {
263        return columnSpecs.get(index);
264    }
265
266    public void setRowSpec(int index, String rowSpec)
267            throws IllegalArgumentException {
268        rowSpecs.set(index, rowSpec);
269        buildLayout();
270    }
271
272    public void setColumnSpec(int index, String columnSpec)
273            throws IllegalArgumentException {
274        columnSpecs.set(index, columnSpec);
275        buildLayout();
276    }
277
278    public void addRowSpec(String rowSpec) throws IllegalArgumentException {
279        rowSpecs.add(rowSpec);
280        buildLayout();
281    }
282
283    public String removeRowSpec(int index) {
284        String rowSpec = rowSpecs.remove(index);
285        try {
286            buildLayout();
287        } catch (IllegalArgumentException e) {
288            throw new RuntimeException(e);
289        }
290
291        return rowSpec;
292    }
293
294    public String removeColumnSpec(int index) {
295        String spec = columnSpecs.remove(index);
296        try {
297            buildLayout();
298        } catch (IllegalArgumentException e) {
299            throw new RuntimeException(e);
300        }
301
302        return spec;
303    }
304
305    public void addRowSpec(int index, String rowSpec)
306            throws IllegalArgumentException {
307        rowSpecs.add(index, rowSpec);
308        buildLayout();
309    }
310
311    public void addColumnSpec(int index, String columnSpec)
312            throws IllegalArgumentException {
313        columnSpecs.add(index, columnSpec);
314        buildLayout();
315    }
316
317    /* the following methods realize the LayoutManager interface */
318
319    public String getComponentName(Component component) {
320        return componentsToNames.get(component);
321    }
322
323    /**
324     * Returns the component with the given name or null if not found
325     */
326    public Component getComponentByName(String name) {
327        for (Component component : componentsToNames.keySet()) {
328            String testName = componentsToNames.get(component);
329            if (testName.equals(name)) {
330                return component;
331            }
332        }
333        return null;
334    }
335
336    public CellConstraints getComponentConstraints(Component component) {
337        String name = componentsToNames.get(component);
338        if (name == null) {
339            throw new RuntimeException(
340                    "Unable to find name for component " + component);
341        }
342        return componentConstraints.get(name);
343    }
344
345    // interface for LayoutManager2
346    @Override
347    public void addLayoutComponent(String name, java.awt.Component comp) {
348        throw new RuntimeException(
349                "This method should not be called.  Call addLayoutComponent (Component, Object) instead");
350    }
351
352    @Override
353    public float getLayoutAlignmentX(java.awt.Container target) {
354        return formLayout.getLayoutAlignmentX(target);
355    }
356
357    @Override
358    public float getLayoutAlignmentY(java.awt.Container target) {
359        return formLayout.getLayoutAlignmentY(target);
360    }
361
362    public FormLayout.LayoutInfo getLayoutInfo(java.awt.Container container) {
363        // KBR added to allow FormDebugPanel to work with ContainerLayout
364        return this.formLayout.getLayoutInfo(container);
365    }
366
367    @Override
368    public void invalidateLayout(java.awt.Container target) {
369        formLayout.invalidateLayout(target);
370    }
371
372    @Override
373    public void layoutContainer(java.awt.Container parent) {
374        formLayout.layoutContainer(parent);
375    }
376
377    @Override
378    public java.awt.Dimension maximumLayoutSize(java.awt.Container target) {
379        return formLayout.maximumLayoutSize(target);
380    }
381
382    @Override
383    public java.awt.Dimension minimumLayoutSize(java.awt.Container parent) {
384        return formLayout.minimumLayoutSize(parent);
385    }
386
387    @Override
388    public java.awt.Dimension preferredLayoutSize(java.awt.Container parent) {
389        return formLayout.preferredLayoutSize(parent);
390    }
391
392    @Override
393    public void removeLayoutComponent(java.awt.Component comp) {
394        String componentName = componentsToNames.get(comp);
395        componentsToNames.remove(comp);
396        componentConstraints.remove(componentName);
397        formLayout.removeLayoutComponent(comp);
398    }
399
400    @Override
401    public void addLayoutComponent(java.awt.Component comp,
402            Object constraints) {
403        if (!(constraints instanceof String)) {
404            throw new RuntimeException(
405                    "The constraints must be a String name which should reference a CellConstraints entry in the xml file");
406        }
407        String componentName = (String) constraints;
408        CellConstraints cellConstraints = componentConstraints
409                .get(componentName);
410        if (cellConstraints == null) {
411            System.out.println("Warning : " + componentName
412                    + " was added without constraints");
413            cellConstraints = new CellConstraints();
414            componentConstraints.put(componentName, cellConstraints);
415            comp.setVisible(false);
416        } else {
417
418            Map<String, Object> customProps = componentNameToCustomProps
419                    .get(componentName);
420            if (customProps != null) {
421                for (String prop : customProps.keySet()) {
422                    Object value = customProps.get(prop);
423                    // KBR Class compClass = comp.getClass();
424                    try {
425                        BeanInfo beanInfo = Introspector
426                                .getBeanInfo(comp.getClass());
427                        PropertyDescriptor[] props = beanInfo
428                                .getPropertyDescriptors();
429                        for (PropertyDescriptor propertyDescriptor : props) {
430                            if (propertyDescriptor.getName().equals(prop)) {
431                                Method writeMethod = propertyDescriptor
432                                        .getWriteMethod();
433                                writeMethod.invoke(comp,
434                                        new Object[] { value });
435                                break;
436                            }
437                        }
438                    } catch (Exception e) {
439                        throw new RuntimeException(e);
440                    }
441                }
442            }
443        }
444
445        componentsToNames.put(comp, componentName);
446        formLayout.addLayoutComponent(comp, cellConstraints);
447    }
448
449    //  private class LocationScore implements Comparable<LocationScore>
450    //  {
451    //    public int score;
452    //
453    //    public int row;
454    //
455    //    public int col;
456    //
457    //    public int width;
458    //
459    //    public LocationScore(int score, int row, int col, int width)
460    //    {
461    //      this.score = score;
462    //      this.row = row;
463    //      this.col = col;
464    //      this.width = width;
465    //    }
466    //
467    //    public int compareTo(LocationScore testScore)
468    //    {
469    //      return this.score < testScore.score ? -1
470    //          : this.score > testScore.score ? 1 : 0;
471    //    }
472    //  }
473
474    public static void main(String[] args) {
475    }
476
477}