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}