001 /* 002 Copyright (C) 2001-2003 Renaud Pawlak <renaud@aopsys.com>, 003 Laurent Martelli <laurent@aopsys.com> 004 005 This program is free software; you can redistribute it and/or modify 006 it under the terms of the GNU Lesser General Public License as 007 published by the Free Software Foundation; either version 2 of the 008 License, or (at your option) any later version. 009 010 This program is distributed in the hope that it will be useful, 011 but WITHOUT ANY WARRANTY; without even the implied warranty of 012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 GNU Lesser General Public License for more details. 014 015 You should have received a copy of the GNU Lesser General Public License 016 along with this program; if not, write to the Free Software 017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 018 019 package org.objectweb.jac.aspects.gui.swing; 020 021 import java.awt.BorderLayout; 022 import java.awt.Insets; 023 import java.awt.Point; 024 import java.awt.event.ActionEvent; 025 import java.awt.event.ActionListener; 026 import java.awt.event.KeyEvent; 027 import java.awt.event.KeyListener; 028 import java.awt.event.MouseEvent; 029 import java.awt.event.MouseListener; 030 import java.util.List; 031 import javax.swing.DefaultListSelectionModel; 032 import javax.swing.JButton; 033 import javax.swing.JComponent; 034 import javax.swing.JPanel; 035 import javax.swing.JScrollPane; 036 import javax.swing.ListSelectionModel; 037 import javax.swing.event.ListSelectionEvent; 038 import javax.swing.event.ListSelectionListener; 039 import org.objectweb.jac.aspects.gui.*; 040 import org.objectweb.jac.aspects.session.SessionAC; 041 import org.objectweb.jac.core.Collaboration; 042 import org.objectweb.jac.core.Wrappee; 043 import org.objectweb.jac.core.rtti.ClassRepository; 044 import org.objectweb.jac.core.rtti.CollectionItem; 045 import org.objectweb.jac.core.rtti.FieldItem; 046 import org.objectweb.jac.core.rtti.MethodItem; 047 import org.objectweb.jac.core.rtti.NamingConventions; 048 import org.objectweb.jac.util.ExtArrays; 049 050 /** 051 * Base class to implement ListView and TableView 052 */ 053 public abstract class AbstractCollection extends AbstractView 054 implements ListSelectionListener, ActionListener, 055 MouseListener, CollectionView, KeyListener 056 { 057 CollectionItem collection; 058 Object substance; 059 CollectionModel model; 060 protected org.objectweb.jac.aspects.gui.CollectionItemView itemView; 061 JComponent component; 062 063 JButton viewButton; 064 JButton removeButton; 065 JButton moveupButton; 066 JButton movedownButton; 067 068 public CollectionModel getCollectionModel() { 069 return model; 070 } 071 072 public AbstractCollection(ViewFactory factory, DisplayContext context, 073 CollectionItem collection, Object substance, 074 CollectionModel model, 075 org.objectweb.jac.aspects.gui.CollectionItemView itemView) { 076 super(factory,context); 077 this.collection = collection; 078 this.substance = substance; 079 this.model = model; 080 this.itemView = itemView; 081 082 setLayout(new BorderLayout()); 083 084 component = getInnerComponent(model); 085 component.addKeyListener(this); 086 JScrollPane scrollPane = new JScrollPane(component); 087 component.addMouseListener(this); 088 add(scrollPane, BorderLayout.CENTER); 089 090 // add, remove and view buttons 091 JPanel lowerCont = new JPanel(); 092 JButton b = null; 093 094 CustomizedGUI customized = null; 095 List targets = null; 096 if (context.getCustomizedView()!=null) { 097 customized = context.getCustomizedView().getCustomizedGUI(); 098 targets = customized.getFieldTargets(collection); 099 } 100 101 if (targets==null || targets.size()==0) { 102 viewButton = createButton("view_icon","View","open"); 103 viewButton.setEnabled(false); 104 lowerCont.add(viewButton); 105 } 106 107 if (GuiAC.isAddable(substance,collection)) { 108 b = createButton("new_icon",GuiAC.getLabelAdd(),"add"); 109 lowerCont.add(b); 110 } 111 112 if (GuiAC.isRemovable(substance,collection)) { 113 removeButton = createButton("remove_icon","Remove","remove"); 114 removeButton.setEnabled(false); 115 lowerCont.add(removeButton); 116 } 117 118 if (collection.isList() && !collection.isCalculated()) { 119 moveupButton = createButton("up_icon","Move up","moveup"); 120 moveupButton.setEnabled(false); 121 lowerCont.add(moveupButton); 122 123 movedownButton = createButton("down_icon","Move up","movedown"); 124 movedownButton.setEnabled(false); 125 lowerCont.add(movedownButton); 126 } 127 128 MethodItem[] directMeths = (MethodItem[]) 129 collection.getAttribute(GuiAC.DIRECT_COLLECTION_METHODS); 130 if (directMeths != null) { 131 for (int i=0; i<directMeths.length; i++) { 132 // We should use SwingMethodView for more consistency 133 lowerCont.add(new DirectButton(directMeths[i])); 134 } 135 } 136 137 add(lowerCont,BorderLayout.SOUTH); 138 139 // Utils.registerCollection(substance,collection,this); 140 } 141 142 protected JButton createButton(String icon, String tooltip, String action) { 143 JButton button = new JButton(ResourceManager.getIconResource(icon)); 144 button.setToolTipText(tooltip); 145 button.setActionCommand(action); 146 button.addActionListener(this); 147 button.setMargin(new Insets(1,5,1,5)); 148 return button; 149 } 150 151 protected abstract JComponent getInnerComponent(Model model); 152 153 boolean isEditor; 154 public boolean isEditor() { 155 return isEditor; 156 } 157 public void setEditor(boolean isEditor) { 158 this.isEditor = isEditor; 159 } 160 161 public void setAutoUpdate(boolean autoUpdate) { 162 // TODO ... 163 } 164 165 public void close(boolean validate) { 166 closed = true; 167 model.close(); 168 } 169 170 /** 171 * Defines what happens when the selection changes. 172 */ 173 174 public void valueChanged(ListSelectionEvent event) { 175 loggerEvents.debug("valueChanged "+event); 176 177 ListSelectionModel lsm = (ListSelectionModel)event.getSource(); 178 if (lsm.isSelectionEmpty()) { 179 if (viewButton!=null) 180 viewButton.setEnabled(false); 181 if (removeButton!=null) 182 removeButton.setEnabled(false); 183 if (moveupButton!=null) 184 moveupButton.setEnabled(false); 185 if (movedownButton!=null) 186 movedownButton.setEnabled(false); 187 return; 188 } else if (event.getValueIsAdjusting()) { 189 return; 190 } else { 191 if (removeButton!=null) 192 removeButton.setEnabled(true); 193 if (viewButton!=null) 194 viewButton.setEnabled(selectionContainsAWrappee()); 195 int indice = getSelectedIndices()[0]; 196 CollectionPosition collpos = new CollectionPosition( 197 this, 198 collection, 199 indice, 200 substance); 201 EventHandler.get().onSelection(context, 202 collection, 203 getSelected()[0], 204 null, 205 collpos); 206 //null); 207 208 if (moveupButton!=null) 209 moveupButton.setEnabled(indice>0); 210 if (movedownButton!=null) 211 movedownButton.setEnabled(indice<model.getRowCount()-1); 212 } 213 } 214 215 /** 216 * Tells wether the selection contains at least on Wrappee object 217 */ 218 boolean selectionContainsAWrappee() { 219 Object[] selection = getSelected(); 220 for (int i=0; i<selection.length; i++) { 221 if (selection[i] instanceof Wrappee) { 222 return true; 223 } 224 } 225 return false; 226 } 227 228 // KeyListener interface 229 230 public void keyTyped(KeyEvent event) {} 231 232 public void keyPressed(KeyEvent event) {} 233 234 public void keyReleased(KeyEvent event) { 235 if (event.getKeyCode()==KeyEvent.VK_DELETE && 236 removeButton!=null 237 /* GuiAC.isRemovable(collection) // this does not work 238 because the session's context is not initialized */ ) { 239 doDelete(); 240 } 241 } 242 243 void doDelete() { 244 Object[] selected = getSelected(); 245 MethodItem removeMethod = collection.getRemover(); 246 loggerEvents.debug("remover="+removeMethod); 247 if (removeMethod!=null) { 248 // try with the removing method 249 for (int i=0; i<selected.length; i++) { 250 EventHandler.get().onRemoveFromCollection( 251 context, 252 new RemoveEvent( 253 this, 254 substance, 255 collection, 256 selected[i]), 257 false); 258 } 259 } else { 260 // call the collection's method directly 261 /* 262 getMethod = collection.getGetter(); 263 if (getMethod!=null) { 264 MethodItem method = null; 265 Object c; 266 try { 267 c = getMethod.invoke(substance,ExtArrays.emptyObjectArray); 268 } catch (Exception e) { 269 Log.error("Getting collection with "+getMethod.getName()+ 270 " failed : "+e); 271 return; 272 } 273 ClassItem cl = ClassRepository.get().getClass(c.getClass()); 274 if (!collection.isMap()) { 275 method = cl.getMethod("remove(java.lang.Object)"); 276 Log.trace("gui","Invoking "+method.getName()+" on collection itself"); 277 for (int i=0; i<selected.length; i++) { 278 try { 279 method.invoke(c,new Object[] {selected[i]}); 280 } catch (Exception e) { 281 Log.error("Removing from collection with "+method.getName()+ 282 " failed : "+e); 283 } 284 } 285 } 286 */ 287 } 288 onRemove(); 289 } 290 291 /** 292 * Handles the actions on this view. 293 * 294 * <p>On a collection view, the three default possible actions are 295 * to open a new view on the selected item, to add a new item to 296 * the collection, and to remove the selected item from the 297 * collection. 298 * 299 * @param event the user event 300 */ 301 public void actionPerformed(ActionEvent event) { 302 setContext(); 303 loggerEvents.debug("action performed on collection : "+event.getActionCommand()+ 304 "; modifiers = "+event.getModifiers()); 305 if (event.getActionCommand().equals("open")) { 306 Object[] selected = getSelected(); 307 for (int i=0; i<selected.length; i++) { 308 try { 309 EventHandler.get().onSelection( 310 context,collection,selected[i],null,null,true); 311 } catch (Exception e) { 312 loggerEvents.error("failed to view "+selected[i],e); 313 } 314 } 315 } else if (event.getActionCommand().equals("add")) { 316 EventHandler.get().onAddToCollection( 317 context, 318 new AddEvent(this,substance,collection,null), 319 (event.getModifiers()& ActionEvent.CTRL_MASK) != 0); 320 } else if (event.getActionCommand().equals("remove")) { 321 doDelete(); 322 } else if (event.getActionCommand().equals("movedown")) { 323 Object selected = getSelected()[0]; 324 int index = getSelectedIndices()[0]; 325 if (index<model.getRowCount()-1) { 326 Integer i = new Integer(index); 327 collection.remove(substance,selected,i); 328 CollectionUpdate update = getCollectionUpdate(); 329 update.onRemove(substance, collection, null, i , null); 330 i = new Integer(index+1); 331 collection.add(substance,selected,i); 332 update.onAdd(substance, collection, null, i, null); 333 setSelected(index+1); 334 } 335 } else if (event.getActionCommand().equals("moveup")) { 336 Object selected = getSelected()[0]; 337 int index = getSelectedIndices()[0]; 338 if (index>0) { 339 Integer i = new Integer(index); 340 collection.remove(substance,selected,i); 341 CollectionUpdate update = getCollectionUpdate(); 342 update.onRemove(substance, collection, null, i, null); 343 i = new Integer(index-1); 344 collection.add(substance,selected,i); 345 update.onAdd(substance, collection, null, i, null); 346 setSelected(index-1); 347 } 348 } 349 } 350 351 // button for direct method 352 class DirectButton extends JButton 353 implements ListSelectionListener, ActionListener 354 { 355 MethodItem method; 356 DirectButton(MethodItem method) { 357 super(GuiAC.getLabel(method)); 358 this.method = method; 359 setIcon(ResourceManager.getIcon(GuiAC.getIcon(method))); 360 addActionListener(this); 361 setEnabled(false); 362 getSelectionModel().addListSelectionListener(this); 363 } 364 public void valueChanged(ListSelectionEvent event) { 365 ListSelectionModel lsm = (ListSelectionModel)event.getSource(); 366 setEnabled(!lsm.isSelectionEmpty()); 367 } 368 public void actionPerformed(ActionEvent event) { 369 Collaboration.get().addAttribute( 370 SessionAC.SESSION_ID, GuiAC.getLocalSessionID()); 371 372 Object[] selected = getSelected(); 373 for (int i=0; i<selected.length; i++) { 374 try { 375 EventHandler.get().onInvoke( 376 context, 377 new InvokeEvent(AbstractCollection.this,selected[i],method)).join(); 378 } catch(InterruptedException e) { 379 context.getDisplay().showModal( 380 e, 381 "Error: "+e, 382 "An error occured during the invocation of "+ 383 method.getCompactFullName()+ 384 " on "+selected[i].getClass().getName()+ 385 " \""+GuiAC.toString(selected[i])+"\"", 386 context.getWindow(), 387 false,false,true); 388 } 389 } 390 } 391 } 392 393 // should call clear selection on the component's selection model 394 protected abstract void onRemove(); 395 396 protected abstract CollectionUpdate getCollectionUpdate(); 397 398 /** 399 * Returns an array of the selected objects. The array is empty if 400 * no object is selected, but not null. 401 **/ 402 403 protected Object[] getSelected() { 404 Object[] selected = null; 405 int[] indices = getSelectedIndices(); 406 if (indices != null) { 407 selected = new Object[indices.length]; 408 for (int i=0; i<indices.length; i++) 409 { 410 selected[i]= model.getObject(indices[i]); 411 } 412 } else { 413 selected = ExtArrays.emptyObjectArray; 414 } 415 return selected; 416 } 417 418 /** 419 * Returns the indices of selected objects. 420 */ 421 protected abstract int[] getSelectedIndices(); 422 423 protected abstract ListSelectionModel getSelectionModel(); 424 425 protected void setNoRefresh(boolean norefresh) { 426 if (norefresh==false) { 427 repaint(); 428 } 429 } 430 431 static class BetterListSelectionModel extends DefaultListSelectionModel { 432 JComponent list = null; 433 public BetterListSelectionModel( JComponent list ) { 434 super(); 435 this.list = list; 436 } 437 public JComponent getList() { 438 return list; 439 } 440 } 441 442 // MouseListener interface 443 444 public void mouseClicked(MouseEvent me){ 445 loggerEvents.debug("mouseClicked"); 446 if (model.getRowCount()==1) { 447 loggerEvents.debug("rowCount==1 => forceRefresh"); 448 if (getSelectedIndices().length>0) { 449 CollectionPosition collpos = new CollectionPosition( 450 this, 451 collection, 452 getSelectedIndices()[0], 453 substance); 454 EventHandler.get().onSelection(context, 455 collection, 456 getSelected()[0], 457 null, 458 collpos); 459 } 460 } 461 } 462 463 public void mousePressed(MouseEvent e) { 464 maybeShowPopup(e); 465 } 466 public void mouseReleased(MouseEvent e) { 467 maybeShowPopup(e); 468 } 469 public void mouseExited(MouseEvent me){} 470 public void mouseEntered(MouseEvent me){} 471 void maybeShowPopup(MouseEvent event) { 472 loggerEvents.debug("maybeShowPopup"); 473 if (event.isPopupTrigger()) { 474 int index = locationToIndex(event.getPoint()); 475 if (index!=-1) { 476 Object o = model.getObject(index); 477 if (o instanceof Wrappee) 478 SwingEvents.showObjectMenu(context, o, event); 479 } 480 } 481 } 482 /** 483 * Returns the index of the element at the given location, or -1 if 484 * if no element is under this location . */ 485 abstract int locationToIndex(Point location); 486 487 public void setField(FieldItem field) { 488 collection = (CollectionItem)field; 489 } 490 491 public void setSubstance(Object substance) { 492 this.substance = substance; 493 } 494 495 public Object getSubstance() { 496 return substance; 497 } 498 499 public FieldItem getField() { 500 return collection; 501 } 502 503 public void setValue(Object value) { 504 } 505 506 public void updateModel(Object substance) { 507 } 508 509 }