1// BEGIN LICENSE BLOCK 2// Version: CMPL 1.1 3// 4// The contents of this file are subject to the Cisco-style Mozilla Public 5// License Version 1.1 (the "License"); you may not use this file except 6// in compliance with the License. You may obtain a copy of the License 7// at www.eclipse-clp.org/license. 8// 9// Software distributed under the License is distributed on an "AS IS" 10// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 11// the License for the specific language governing rights and limitations 12// under the License. 13// 14// The Original Code is The ECLiPSe Constraint Logic Programming System. 15// The Initial Developer of the Original Code is Cisco Systems, Inc. 16// Portions created by the Initial Developer are 17// Copyright (C) 2006 Cisco Systems, Inc. All Rights Reserved. 18// 19// Contributor(s): 20// 21// END LICENSE BLOCK 22 23package com.parctechnologies.eclipse.visualisation; 24 25import java.awt.*; 26import java.awt.event.*; 27import java.util.*; 28import java.beans.*; 29import javax.swing.*; 30 31import com.parctechnologies.eclipse.*; 32 33/** 34 * Superclass of all classes in this package implementing Viewer. 35 * Any implementation which is common to most viewers should be located in this 36 * class. This includes actions which are common to all viewers, the 37 * implementation of some default behaviour and the implementation of the 38 * simplest get/set methods from the Viewer interface. 39 * 40 */ 41public abstract class ViewerImpl implements Viewer 42{ 43 private InterestSpec interestSpec; 44 private Viewable viewable; 45 private VisEvent currentEvent; 46 private VisClientStateModel stateModel; 47 private ViewerManager viewerManager; 48 private static final Atom fineAtom = new Atom("fine"); 49 private static final Atom timedAtom = new Atom("timed"); 50 private static final Atom coarseAtom = new Atom("coarse"); 51 private VPSRadioButton fineMenuButton; 52 private VPSRadioButton timedMenuButton; 53 private VPSRadioButton coarseMenuButton; 54 private PropertyChangeSupport propertyChangeSupport = 55 new PropertyChangeSupport(this); 56 private JMenuBar jMenuBar = new JMenuBar(); 57 private Map menuTitleToMenu = new HashMap(); 58 private Map menuTitleToPopupMenu = new HashMap(); 59 private String description = "Viewer"; 60 61 private SymRef symRef ; 62 63 // This boolean determines whether to hold when ECLiPSe backtracks over the 64 // viewable_create goal, producing the DestroyEvent for the viewable. 65 private boolean holdAtLastBacktrack = true; 66 private boolean holdAtExpansions = true; 67 private boolean holdAtContractions = true; 68 69 public ViewerImpl(VisClientStateModel stateModel, Viewable viewable) 70 { 71 setViewable(viewable); 72 setInterestSpec(viewable.createInterestSpec()); 73 setStateModel(stateModel); 74 initialiseMenu(); 75 symRef = new SymRef(this, viewable.getSymRef(), getClass().getName()); 76 } 77 78 public void setViewerManager(ViewerManager viewerManager) 79 { 80 this.viewerManager = viewerManager; 81 } 82 83 public void close() 84 { 85 if (DebuggingSupport.logMessages) { 86 DebuggingSupport.logMessage(this,"ViewerImpl close called"); 87 } 88 PropertyChangeSupport pcs = stateModel.getPropertyChangeSupport(); 89 pcs.removePropertyChangeListener("canPerformRPC", fineMenuButton); 90 pcs.removePropertyChangeListener("canPerformRPC", timedMenuButton); 91 pcs.removePropertyChangeListener("canPerformRPC", coarseMenuButton); 92 viewerManager.closeViewer(this); 93 } 94 95 96 private void initialiseMenu() 97 { 98 // add some boolean options common to all viewers. 99 JCheckBoxMenuItem halbCheckBox = new JCheckBoxMenuItem("Hold at destruction"); 100 halbCheckBox.setModel(new BooleanPropertyModel("holdAtLastBacktrack", 101 this, getPropertyChangeSupport())); 102 JCheckBoxMenuItem haeCheckBox = new JCheckBoxMenuItem("Hold at expansions"); 103 haeCheckBox.setModel(new BooleanPropertyModel("holdAtExpansions", 104 this, getPropertyChangeSupport())); 105 JCheckBoxMenuItem hacCheckBox = new JCheckBoxMenuItem("Hold at contractions"); 106 hacCheckBox.setModel(new BooleanPropertyModel("holdAtContractions", 107 this, getPropertyChangeSupport())); 108 addMenuItem("Options", haeCheckBox); 109 addMenuItem("Options", hacCheckBox); 110 addMenuItem("Options", halbCheckBox); 111 ButtonGroup bg = new ButtonGroup(); 112 fineMenuButton = new VPSRadioButton(this,fineAtom); 113 bg.add(fineMenuButton); 114 addMenuItem("Options", fineMenuButton); 115 coarseMenuButton = new VPSRadioButton(this,coarseAtom); 116 bg.add(coarseMenuButton); 117 addMenuItem("Options", coarseMenuButton); 118 timedMenuButton = new VPSRadioButton(this,timedAtom); 119 bg.add(timedMenuButton); 120 addMenuItem("Options", timedMenuButton); 121 } 122 123 public void setHoldAtLastBacktrack(boolean newValue) 124 { 125 (new ViewerSetBooleanPropertyCommand(this, 126 "holdAtLastBacktrack", newValue)).issue(); 127 } 128 129 public void setHoldAtLastBacktrackPrivate(boolean newValue) 130 { 131 boolean oldValue = holdAtLastBacktrack; 132 holdAtLastBacktrack = newValue; 133 this.getPropertyChangeSupport(). 134 firePropertyChange("holdAtLastBacktrack", oldValue, newValue); 135 } 136 137 public boolean getHoldAtLastBacktrack() 138 { 139 return(holdAtLastBacktrack); 140 } 141 142 public void setHoldAtExpansions(boolean newValue) 143 { 144 (new ViewerSetBooleanPropertyCommand(this, 145 "holdAtExpansions", newValue)).issue(); 146 } 147 148 public void setHoldAtExpansionsPrivate(boolean newValue) 149 { 150 boolean oldValue = holdAtExpansions; 151 holdAtExpansions = newValue; 152 this.getPropertyChangeSupport(). 153 firePropertyChange("holdAtExpansions", oldValue, newValue); 154 155 } 156 157 158 public boolean getHoldAtExpansions() 159 { 160 return(holdAtExpansions); 161 } 162 163 public void setHoldAtContractions(boolean newValue) 164 { 165 (new ViewerSetBooleanPropertyCommand(this, 166 "holdAtContractions", newValue)).issue(); 167 } 168 169 public void setHoldAtContractionsPrivate(boolean newValue) 170 { 171 boolean oldValue = holdAtContractions; 172 holdAtContractions = newValue; 173 this.getPropertyChangeSupport(). 174 firePropertyChange("holdAtContractions", oldValue, newValue); 175 176 } 177 178 public boolean getHoldAtContractions() 179 { 180 return(holdAtContractions); 181 } 182 183 public void setViewPropagationSteps(Object atom) 184 { 185 (new ViewerSetPropagationStepsCommand(this, (Atom)atom)).issue(); 186 } 187 188 public void setViewPropagationStepsPrivate(Object newValue) 189 { 190 Object oldValue = getViewPropagationSteps(); 191 if (DebuggingSupport.logMessages) { 192 DebuggingSupport. 193 logMessage(this, 194 "setViewPropagationStepsPrivate newValue = "+ 195 newValue + " oldValue = " + oldValue); 196 } 197 if(!oldValue.equals(newValue)) { 198 getInterestSpec().setViewGranularity((Atom)newValue); 199 propertyChangeSupport. 200 firePropertyChange("viewPropagationSteps", 201 oldValue, newValue); 202 } 203 } 204 205 public Object getViewPropagationSteps() 206 { 207 return(getInterestSpec().getViewGranularity()); 208 } 209 210 /** 211 * each viewer uses a PropertyChangeSupport object to allow some of its 212 * proprties to be observable by other objects. 213 */ 214 public PropertyChangeSupport getPropertyChangeSupport() 215 { 216 return(propertyChangeSupport); 217 } 218 219 public String getDescription() 220 { 221 return(description); 222 } 223 224 public void setDescription(String description) 225 { 226 this.description = description; 227 } 228 229 /** 230 * Returns the mnemonic to use for a given menuTitle 231 */ 232 protected int getMenuMnemonic(String menuTitle) { 233 if ("Options".equals(menuTitle)) return KeyEvent.VK_O; 234 return -1; 235 } 236 237 /** 238 * Adds the given item to the named menu (creating the JMenu if 239 * necessary), storing the menuTitle->JMenu map in the provided map 240 * and adding the JMenu to the given JMenuBar provided it is not 241 * null. */ 242 private void addMenuItem(Map menuTitleMap, 243 JMenuBar menuBar, 244 String menuTitle, 245 Object item) { 246 JMenu menu = (JMenu) menuTitleMap.get(menuTitle); 247 if(menu == null) { 248 menu = new JMenu(menuTitle); 249 // Add optional menu shortcut 250 int mnemonic = getMenuMnemonic(menuTitle); 251 if (mnemonic != -1) { 252 menu.setMnemonic(mnemonic); 253 } 254 if (menuBar != null) { 255 menuBar.add(menu); 256 } 257 menuTitleMap.put(menuTitle, menu); 258 } else { 259 if(item == null) { 260 // if item is null and the menu exists then add a seperator 261 menu.addSeparator(); 262 return; 263 } 264 } 265 if(item instanceof Action) 266 { 267 menu.add((Action) item); 268 return; 269 } 270 if(item instanceof JMenuItem) 271 { 272 menu.add((JMenuItem) item); 273 return; 274 } 275 } 276 277 /** Convenience method to add menu items based on the menu title: if a menu 278 * with that title exists, add to that menu. Otherwise, create menu with that 279 * title and add to that. 280 **/ 281 protected void addMenuItem(String menuTitle, Object item) 282 { 283 addMenuItem(menuTitleToMenu, jMenuBar, menuTitle, item); 284 } 285 286 /** Convenience method to add popup menu items based on the menu 287 * title: if a popup menu with that title exists, add to that 288 * menu. Otherwise, create menu with that title and add to that. 289 **/ 290 protected void addPopupMenuItem(String menuTitle, Object item) 291 { 292 addMenuItem(menuTitleToPopupMenu, null, menuTitle, item); 293 } 294 295 /** Convenience method to add both normal menu and popup menu items 296 * based on the menu title: if a popup menu with that title exists, 297 * add to that menu. Otherwise, create menu with that title and add 298 * to that. 299 **/ 300 protected void addMenuAndPopupMenuItem(String menuTitle, Object item) 301 { 302 addMenuItem(menuTitleToMenu, jMenuBar, menuTitle, item); 303 addMenuItem(menuTitleToPopupMenu, null, menuTitle, item); 304 } 305 306 public JMenu getPopupMenu(String menuTitle) { 307 return (JMenu) menuTitleToPopupMenu.get(menuTitle); 308 } 309 310 public JMenuBar getJMenuBar() 311 { 312 return(jMenuBar); 313 } 314 315 public InterestSpec getInterestSpec() 316 { 317 return(interestSpec); 318 } 319 320 public void setInterestSpec(InterestSpec interestSpec) 321 { 322 this.interestSpec = interestSpec; 323 } 324 325 public void setStateModel(VisClientStateModel stateModel) 326 { 327 this.stateModel = stateModel; 328 } 329 330 protected VisClientStateModel getStateModel() 331 { 332 return(stateModel); 333 } 334 335 public Viewable getViewable() 336 { 337 return(viewable); 338 } 339 340 protected void setViewable(Viewable viewable) 341 { 342 this.viewable = viewable; 343 } 344 345 346 public abstract Component getComponent(); 347 348 /** 349 * It is useful for some methods to have access to the current event while 350 * it is happening. 351 */ 352 protected VisEvent getCurrentEvent() 353 { 354 return(currentEvent); 355 } 356 357 /** 358 * The default case here does nothing. 359 */ 360 public void prepareForEvent(VisEvent event){ 361 if (event instanceof DestroyEvent) { 362 if (DebuggingSupport.logMessages) { 363 DebuggingSupport.logMessage(this,"destroyEventIssued fired"); 364 } 365 getPropertyChangeSupport(). 366 firePropertyChange("destroyEventIssued",false,true); 367 } 368 } 369 370 /** 371 * The default case here returns an empty Batch goal. 372 */ 373 public BatchGoal collectPreEventGoals(VisEvent event) 374 { 375 return(new BatchGoal()); 376 } 377 378 /** 379 * The default behaviour is to do nothing but set the current event. This 380 * will obviously be overridden in most cases. 381 */ 382 public void startEvent(VisEvent event, java.util.List goalResults) 383 { 384 currentEvent = event; 385 } 386 387 /** 388 * The default behaviour for shouldHold is that we hold at any Create or 389 * Expand events and at Destroy events if holdAtLastBacktrack is true. 390 * This can be overridden. 391 */ 392 public boolean shouldHold() 393 { 394 if(currentEvent instanceof CreateEvent) 395 { 396 return(true); 397 } 398 if(currentEvent instanceof DestroyEvent && holdAtLastBacktrack) 399 { 400 return(true); 401 } 402 if(currentEvent instanceof ContractEvent && holdAtContractions) 403 { 404 return(true); 405 } 406 if(currentEvent instanceof ExpandEvent && holdAtExpansions) 407 { 408 return(true); 409 } 410 return(false); 411 } 412 413 /** 414 * The default behaviour here simply resets the currentEvent. 415 */ 416 public void stopEvent() 417 { 418 currentEvent = null; 419 } 420 421 /** 422 * This inner class provides the "view propagation steps X" radio 423 * buttons. It must become disabled whenever ECLiPSe has control 424 * because a user action which changes the value requires an 425 * in-event goal, which can only be performed when the VC has 426 * control. */ 427 private class VPSRadioButton extends JRadioButtonMenuItem 428 implements PropertyChangeListener 429 { 430 VPSRadioButton(Object propertyHolder,Atom atom) 431 { 432 super("View propagation steps ("+atom.functor()+")"); 433 setModel(new BooleanGroupPropertyModel("viewPropagationSteps", 434 propertyHolder, getPropertyChangeSupport(), atom)); 435 stateModel.getPropertyChangeSupport(). 436 addPropertyChangeListener("canPerformRPC", this); 437 getPropertyChangeSupport(). 438 addPropertyChangeListener("destroyEventIssued", this); 439 } 440 441 public void propertyChange(PropertyChangeEvent event) 442 { 443 if (event.getPropertyName() == "destroyEventIssued") { 444 setEnabled(false); 445 } else { 446 // event.getPropertyName() == "canPerformRPC" 447 if(((Boolean) event.getNewValue()).booleanValue()) { 448 setEnabled(true); 449 } else { 450 setEnabled(false); 451 } 452 } 453 } 454 } 455 456 /** 457 * The default case here does nothing. 458 */ 459 public void gainFocus() 460 {} 461 462 /** 463 * The default case here does nothing. 464 */ 465 public void loseFocus() 466 {} 467 468 /** Returns the dimensions of the Viewer */ 469 public Rectangle getBounds() { 470 return getComponent().getBounds(); 471 } 472 473 /** 474 * The default case here does nothing 475 */ 476 public void zoomInByRatio(float ratio) 477 {} 478 479 /** 480 * The default case here does nothing. 481 */ 482 public void zoomToLevel(float newLevel) 483 {} 484 485 /** Sets the dimensions of the Viewer */ 486 public void setBounds(Rectangle r) { 487 // NOTE: should probably remove the ComponentListener for the 488 // duration of this resizing 489 getComponent().setBounds(r.x, r.y, r.width, r.height); 490 } 491 492 493 /** Access the context sensitive popup menu */ 494 public JPopupMenu getPopupMenu() { 495 return new JPopupMenu(); 496 } 497 498 499 public void setSymRef(SymRef symRef) { 500 this.symRef = symRef; 501 } 502 503 public SymRef getSymRef() { 504 return symRef; 505 } 506} 507 508 509