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.viewers; 24 25import com.parctechnologies.eclipse.*; 26import com.parctechnologies.eclipse.visualisation.*; 27 28import java.awt.Rectangle; 29import java.awt.Shape; 30import java.awt.geom.*; 31import java.awt.Color; 32import java.awt.Component; 33import java.util.*; 34import java.awt.event.ActionEvent; 35import javax.swing.*; 36import javax.swing.table.*; 37import att.grappa.*; 38 39 40/** 41 * Displays a textual representation of the viewable element 42 */ 43public class TextViewletType extends AbstractViewletType { 44 private TableCellRenderer tableCellRenderer; 45 private CustomRenderer customRenderer; 46 public static final int MAX_FADE = 10; 47 48 /** Holds four colours per fade level*/ 49 FadeColorSupport fadeColorSupport; 50 51 public TextViewletType(String changeable) { 52 super(changeable); 53 fadeColorSupport = new FadeColorSupport(MAX_FADE, new Color(0,255,0), new Color(255,0,0)); 54 } 55 56 57 /* ViewletFactory methods */ 58 public boolean canBuildFrom(ElementType elementType) 59 { 60 return(true); 61 } 62 63 public ViewletData build() 64 { 65 return new Data(); 66 } 67 68 public String getDescription() 69 { 70 return("Text viewlet"); 71 } 72 73 74 /* ViewletType methods */ 75 public synchronized TableCellRenderer getTableCellRenderer() { 76 if (tableCellRenderer == null) { 77 tableCellRenderer = new CellRenderer(); 78 } 79 return tableCellRenderer; 80 } 81 82 synchronized Class getCustomRendererClass() { 83// if (customRenderer == null) { 84// customRenderer = new CustRenderer(); 85// } 86// return customRenderer; 87 return Renderer.class; 88 } 89 90 protected Color getColor(Data data, boolean isSelected) { 91 int greyness = 0; 92 if (data.getHoldsOnUpdates()) { 93 greyness++; 94 } 95 if (isSelected) { 96 greyness+=2; 97 } 98 Color col; 99 int val = data.getFadeCount(); 100 if (val > 0) { 101 col = fadeColorSupport.forwardColor[val][greyness]; 102 } else { 103 col = fadeColorSupport.backwardColor[-val][greyness]; 104 } 105 return col; 106 } 107 108 public void customizeElement(ViewletDataStore store, 109 java.util.List index, 110 Element element) { 111 Data data = (Data)(store.getViewletDataAt(index)); 112 if (element instanceof Node) { 113 // set the element data 114 element.object = data; 115 116 // set the custom renderer 117 element.setAttribute("shape",new Integer(Grappa.CUSTOM_SHAPE)); 118 element.setAttribute(Grappa.CUSTOM_ATTR,getCustomRendererClass().getName()); 119 // set the node label 120 element.setAttribute("label",data.getText()); 121 122 // set filled 123 element.setAttribute("style", "filled"); 124 125 // set background color 126 element.setAttribute("color",getColor(data, false)); 127 128 // force shape update 129 element.getGrappaNexus().updateShape(); 130 } else { 131 // instance of edge 132 } 133 } 134 135 136 public BatchGoal collectPreBuildGoal(Viewer viewer, 137 ViewletDataStore store, 138 ViewletRange range) 139 { 140 BatchGoal result = new BatchGoal(); 141 Iterator indexListIterator = range.iterator(); 142 java.util.List currentIndex; 143 Collection viewlets; 144 while(indexListIterator.hasNext()) 145 { 146 currentIndex = (java.util.List) indexListIterator.next(); 147 CompoundTerm goal = 148 new CompoundTermImpl(":", new Atom("vc_support"), 149 new CompoundTermImpl("viewable_element_to_string", 150 new CompoundTermImpl("element",currentIndex), null)); 151 result.add(composeElementGoal(currentIndex, store.getViewable().getNameAtom(), goal)); 152 } 153 return(result); 154 } 155 156 public void startBuild(Viewer viewer, 157 ViewletDataStore store, 158 ViewletRange range, 159 List results) { 160 Iterator indexListIterator = range.iterator(); 161 Iterator resultsIterator = results.iterator(); 162 CompoundTermImpl elementResult; 163 List currentIndex; 164 if (DebuggingSupport.logMessages) { 165 DebuggingSupport.logMessage(this, 166 "startBuild called with range=" + 167 range + " results=" + results); 168 } 169 170 while(indexListIterator.hasNext()) { 171 currentIndex = (List) indexListIterator.next(); 172 if (DebuggingSupport.logMessages) { 173 DebuggingSupport.logMessage(this, "currentIndex="+currentIndex); 174 } 175 Data viewletData = 176 (Data)(store.getViewletDataAt(currentIndex)); 177 if (DebuggingSupport.logMessages) { 178 DebuggingSupport.logMessage(this, "viewletData="+viewletData); 179 } 180 elementResult = (CompoundTermImpl) resultsIterator.next(); 181 if (DebuggingSupport.logMessages) { 182 DebuggingSupport.logMessage(this, "elementResult="+elementResult); 183 } 184 // store the goal result text into the viewletDataStore 185 Object text = 186 ((CompoundTermImpl)decomposeElementGoal(elementResult)).argCT(2).arg(2); 187 if (viewletData == null) { 188 viewletData = (Data)build(); 189 } 190 viewletData.setText(text.toString()); 191 store.setViewletDataAt(currentIndex, viewletData); 192 } 193 } 194 195 196 public BatchGoal collectPreUpdateGoal(Viewer viewer, 197 ViewletDataStore store, 198 ViewletRange range, 199 UpdateEvent updateEvent) 200 { 201 BatchGoal result = new BatchGoal(); 202 Iterator indexListIterator = range.iterator(); 203 java.util.List currentIndex; 204 Collection viewlets; 205 206 while(indexListIterator.hasNext()) 207 { 208 currentIndex = (java.util.List) indexListIterator.next(); 209 CompoundTerm goal = 210 new CompoundTermImpl(":", new Atom("vc_support"), 211 new CompoundTermImpl("viewable_element_to_string", 212 new CompoundTermImpl("element",currentIndex), null)); 213 result.add(composeElementGoal(currentIndex, store.getViewable().getNameAtom(), goal)); 214 } 215 return(result); 216 } 217 218 protected void setUpdating(Viewer viewer, 219 ViewletDataStore store, 220 ViewletRange range, 221 int fadeCount) { 222 Iterator indexListIterator = range.iterator(); 223 List currentIndex; 224 while(indexListIterator.hasNext()) { 225 currentIndex = (List) indexListIterator.next(); 226 if (DebuggingSupport.logMessages) { 227 DebuggingSupport.logMessage(this, "currentIndex="+currentIndex); 228 } 229 Data viewletData = 230 (Data)(store.getViewletDataAt(currentIndex)); 231 if (DebuggingSupport.logMessages) { 232 DebuggingSupport.logMessage(this, "viewletData="+viewletData); 233 } 234 // Set the updating flag and store the new data 235 if (viewletData == null) { 236 viewletData = (Data)build(); 237 } 238 viewletData.setFadeCount(fadeCount); 239 store.setViewletDataAt(currentIndex, viewletData); 240 } 241 } 242 243 public void startUpdate(Viewer viewer, 244 ViewletDataStore store, 245 ViewletRange range, 246 List results, 247 UpdateEvent updateEvent) 248 { 249 // do the same as for building a viewlet 250 startBuild(viewer, store, range, results); 251 // set the fade counter 252 setUpdating(viewer, store, range,((updateEvent instanceof ForwardUpdateEvent)?MAX_FADE:-MAX_FADE)); 253 254 ViewletRange all = store.getEntireViewletRange() ; 255 ViewletRange faded = new ViewletRangeCollection(); 256 for(Iterator it = all.iterator(); it.hasNext(); ) { 257 List index = (List)it.next(); 258 Data data = (Data)(store.getViewletDataAt(index)); 259 if (data.fade()) { 260 faded.add(index); 261 } 262 } 263 // Indicate that these cells were updated 264 store.fireViewletRangeUpdated(faded); 265 // store.fireViewletRangeUpdated(range); 266 } 267 268 269 /* 270 * Data is a viewlet which can monitor elements of any type. It is 271 * responsible for: 272 * <ul> 273 * <li> Maintaining a record of the text representation of the term. 274 * </ul> 275 */ 276 public static class Data extends ViewletDataImpl 277 { 278 private String text; 279 int updating; 280 private int fadeCount; 281 private boolean fading; 282 283 public Data() 284 { 285 super(); 286 text = ""; 287 fadeCount = 0; 288 fading = true; 289 } 290 291 public String getText() 292 { 293 return text; 294 } 295 296 public void setText(String newValue) 297 { 298 text = newValue; 299 } 300 301 public String toString() { 302 return text; 303 } 304 305 306 public int getFadeCount() { 307 return fadeCount; 308 } 309 310 public void setFadeCount(int count) { 311 fadeCount = count; 312 } 313 314 public void setFading(boolean fading) { 315 this.fading = fading; 316 } 317 318 public boolean getFading() { 319 return fading; 320 } 321 322 323 /** 324 * Move the fade counter toward zero 325 * @return true iff the fadeCount changed 326 */ 327 public boolean fade() { 328 if (fadeCount == 0) { 329 return false; 330 } if (fading) { 331 if (fadeCount > 0) { 332 fadeCount--; 333 } else { 334 /* fadeCount < 0 */ 335 fadeCount++; 336 } 337 } else { 338 // when not fading colors should revert after one cylce 339 fadeCount = 0; 340 } 341 return true; 342 } 343 344 } 345 346 347 /** 348 * Return a collection of actions which can be applied to viewlets 349 * in this table 350 */ 351 public Collection getActions(ViewletDataStore store, 352 ViewletRange range) { 353 Collection ll = super.getActions(store, range); 354 if ((range != null) & (!range.isEmpty())) { 355 ll.add((new ToggleFadeAction()).createCompoundAction(store, range)); 356 } 357 return ll; 358 } 359 360 private class ToggleFadeAction extends ViewletAction 361 { 362 ToggleFadeAction() 363 { 364 super("Fade update history"); 365 putValue(Action.NAME, "Fade update history"); 366 putValue(Action.LONG_DESCRIPTION, 367 "Change whether previous updates show as fading colours or not"); 368 putValue(Action.SHORT_DESCRIPTION, 369 "Change whether colours fade"); 370 putValue(Action.SMALL_ICON, new FadeIcon(20, 20)); 371 372 } 373 374 public void actionPerformed(ActionEvent e) 375 { 376 // do nothing 377 } 378 379 /** 380 * If all viewlets in the collection have hold set to true, then the 381 * compound version sets it to false. If any of them have it set to false 382 * then the compound version sets all to true. 383 */ 384 public ViewletAction createCompoundAction(ViewletDataStore store, 385 ViewletRange range) 386 { 387 boolean allFade = true; 388 Data currentViewlet; 389 Iterator viewletsIterator = store.getViewletDataIterator(range); 390 while(viewletsIterator.hasNext()) 391 { 392 currentViewlet = (Data) viewletsIterator.next(); 393 if(!currentViewlet.getFading()) 394 { 395 allFade = false; 396 break; 397 } 398 } 399 return(new CompoundToggleFadeAction(!allFade, store, range)); 400 } 401 } 402 403 private class CompoundToggleFadeAction extends ToggleFadeAction 404 { 405 private boolean newValue; 406 private ViewletRange range; 407 private ViewletDataStore store; 408 CompoundToggleFadeAction(boolean newValue, 409 ViewletDataStore store, 410 ViewletRange range) 411 { 412 super(); 413 this.newValue = newValue; 414 this.range = range; 415 this.store = store; 416 } 417 418 public void actionPerformed(ActionEvent e) 419 { 420 Data currentViewlet; 421 Iterator viewletsIterator = range.iterator(); 422 while(viewletsIterator.hasNext()) 423 { 424 List index = (List)viewletsIterator.next(); 425 currentViewlet = (Data)(store.getViewletDataAt(index)); 426 currentViewlet.setFading(newValue); 427 store.setViewletDataAt(index, currentViewlet); 428 } 429 // trigger the jtable to update as a whole bunch of viewlets have just 430 // changed 431 store.fireViewletRangeUpdated( range ); 432 } 433 } 434 435 436 437 438 /** 439 * The default text cell render 440 */ 441 private class CellRenderer extends DefaultTableCellRenderer { 442 443 Icon fadeIcon; 444 445 public CellRenderer() { 446 super(); 447 setHorizontalAlignment(SwingConstants.CENTER); 448 // Create the single instance fadeIcon to be used by the renderer 449 fadeIcon = fadeColorSupport.getFadeIcon(10,10); 450 } 451 452 public Component getTableCellRendererComponent(JTable table, 453 Object value, 454 boolean isSelected, 455 boolean hasFocus, 456 int row, 457 int column) { 458 459 JLabel result ; 460 if (table == null) { 461 result = new JLabel(); 462 } else { 463 result = 464 (JLabel)(super.getTableCellRendererComponent(table, 465 value, 466 isSelected, 467 hasFocus, 468 row, 469 column)); 470 } 471 Data data = (Data)value; 472 473 result.setBackground(getColor(data, isSelected)); 474 if (data.getFading()) { 475 result.setIcon(fadeIcon); 476 } else { 477 result.setIcon(null); 478 } 479 return result; 480 } 481 482 public void setValue(Object value) { 483 super.setValue(value); 484 setToolTipText(value.toString()); 485 } 486 487 } 488 489 public static class Renderer extends CustRenderer { 490 public Renderer(Element element, 491 double x, double y, double w, double h) { 492 super(element, x, y, w, h); 493 if (DebuggingSupport.logMessages) { 494 DebuggingSupport.logMessage(this,"Plain Text Renderer constructed for element "+element); 495 } 496 // somehow work out a way of getting the actual 497 // ViewletData for this element (must work when the 498 // viewlettype is contained in a multiViewlet type) 499 //Data data = (Data)element.object; 500 Data data = (Data)getViewletData(); 501 if (data != null) { 502 configure(new Rectangle2D.Double(x,y,w,h), 503 data); 504 } 505 } 506 507 public void configure(Rectangle2D bounds, 508 Data data) { 509 // draw a line underneath the text to show the selection status 510 // Text 511 // ---- 512 // 513 float y = (float)(bounds.getMaxY()); 514 float dy = 1.0f; 515 float vertices[][] = {{(float)bounds.getMinX(), y-dy}, 516 {(float)bounds.getMaxX(), y-dy}, 517 {(float)bounds.getMaxX(), y+dy}, 518 {(float)bounds.getMinX(), y+dy}}; 519 path.moveTo(vertices[0][0], vertices[0][1]); 520 for(int i = 1; i < vertices.length; i++) { 521 if (DebuggingSupport.logMessages) { 522 DebuggingSupport.logMessage(this,"Bounds point "+i+ 523 " x="+vertices[i][0]+ 524 " y="+vertices[i][1]); 525 } 526 path.lineTo(vertices[i][0], vertices[i][1]); 527 } 528 path.closePath(); 529 } 530 531 public void draw(java.awt.Graphics2D g2d) { 532 if ((element.highlight & SELECTION_MASK) != 0) { 533 // do not draw outline unless the element is selected 534 super.draw(g2d); 535 } 536 } 537 } 538 539} 540 541