Coverage Report - org.jfree.chart.plot.HCPlot
 
Classes in this File Line Coverage Branch Coverage Complexity
HCPlot
93%
458/492
97%
92/95
2.967
 
 1  
 /* =======================================================================
 2  
  * A visualisation library extension for JFreeChart. Please see JFreeChart
 3  
  * for further information.
 4  
  * =======================================================================
 5  
  * Copyright (C) 2006  University of Helsinki, Department of Computer Science
 6  
  *
 7  
  * This library is free software; you can redistribute it and/or
 8  
  * modify it under the terms of the GNU Lesser General Public
 9  
  * License as published by the Free Software Foundation; either
 10  
  * version 2.1 of the License, or (at your option) any later version.
 11  
  *
 12  
  * This library is distributed in the hope that it will be useful,
 13  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 15  
  * Lesser General Public License for more details.
 16  
  *
 17  
  * You should have received a copy of the GNU Lesser General Public
 18  
  * License along with this library; if not, write to the Free Software
 19  
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 20  
  * -----------------------------
 21  
  * Contact:  ohtu@cs.helsinki.fi
 22  
  * -----------------------------
 23  
  *
 24  
  */
 25  
 
 26  
 package org.jfree.chart.plot;
 27  
 
 28  
 import java.awt.Graphics2D;
 29  
 import java.awt.Shape;
 30  
 import java.awt.geom.Rectangle2D;
 31  
 import java.awt.geom.Point2D;
 32  
 import java.awt.Point;
 33  
 import java.awt.Color;
 34  
 import java.awt.Rectangle;
 35  
 import java.awt.event.MouseEvent;
 36  
 import java.lang.IllegalArgumentException;
 37  
 import java.lang.Integer;
 38  
 import java.lang.Math;
 39  
 import java.util.ResourceBundle;
 40  
 import java.util.TreeSet;
 41  
 
 42  
 import javax.swing.JPanel;
 43  
 import javax.swing.event.ChangeListener;
 44  
 import javax.swing.event.ChangeEvent;
 45  
 
 46  
 import org.jfree.ui.RectangleEdge;
 47  
 
 48  
 import org.jfree.chart.ChartMouseEvent;
 49  
 import org.jfree.chart.ChartMouseListener;
 50  
 import org.jfree.chart.axis.HeatMapAxis;
 51  
 import org.jfree.chart.axis.CategoryLabelPositions;
 52  
 import org.jfree.chart.entity.HCTreeNodeEntity;
 53  
 import org.jfree.chart.entity.HeatMapBlockEntity;
 54  
 import org.jfree.chart.entity.EntityCollection;
 55  
 import org.jfree.chart.event.ClusteringTreeChangeEvent;
 56  
 import org.jfree.chart.event.PlotChangeEvent;
 57  
 import org.jfree.chart.event.SelectionChangeEvent;
 58  
 import org.jfree.chart.labels.HCToolTipGenerator;
 59  
 import org.jfree.chart.plot.HCMediator;
 60  
 import org.jfree.chart.plot.AbstractHCClusteringInfo;
 61  
 import org.jfree.chart.plot.DummyHCClusteringInfo;
 62  
 import org.jfree.chart.plot.StandardHCClusteringInfo;
 63  
 import org.jfree.chart.plot.HCTreeNodeInfo;
 64  
 import org.jfree.chart.plot.Plot;
 65  
 import org.jfree.data.hc.HCDataset;
 66  
 import org.jfree.data.hc.HeatMap;
 67  
 import org.jfree.data.hc.DataRange;
 68  
 import org.jfree.data.hc.HCTreeNode;
 69  
 import org.jfree.chart.editor.GradientColorPaletteEditor;
 70  
 import org.jfree.chart.editor.HCOptionsEditor;
 71  
 
 72  
 
 73  
 /**
 74  
  * A plot that displays data in the a Hierarchical clustering
 75  
  * and Heatmap visualisation. The plot uses
 76  
  * data from a {@link HCDataset} -object.
 77  
  *
 78  
  * @author viski project.
 79  
  */
 80  
 public class HCPlot
 81  
     extends Plot
 82  
     implements
 83  
         ChartMouseListener,
 84  
         ChangeListener {
 85  
 
 86  
     private HCDataset dataset;
 87  
     private HeatMapAxis rowNames;
 88  
     private HeatMapAxis columnNames;
 89  
     private AbstractHCClusteringInfo rowClusteringInfo;
 90  
     private AbstractHCClusteringInfo columnClusteringInfo;
 91  
     private boolean columnNamesVisibility;
 92  
     private boolean rowNamesVisibility;
 93  
     private boolean columnTreeVisibility;
 94  
     private boolean rowTreeVisibility;
 95  
 
 96  
     private double columnTreeSize;
 97  
     private double rowTreeSize;
 98  
     private double columnNamesSize;
 99  
     private double rowNamesSize;
 100  
     private double topMarginSize;
 101  
     private double bottomMarginSize;
 102  
     private double leftMarginSize;
 103  
     private double rightMarginSize;
 104  
 
 105  
     private GradientColorPalette palette;
 106  
     private GradientColorPaletteEditor paletteEditor;
 107  
     private HCOptionsEditor optionsEditor;
 108  
 
 109  
     private Rectangle selection;
 110  
 
 111  
     public static final int LEFT = 0;
 112  
     public static final int TOP = 1;
 113  
 
 114  
     private HCToolTipGenerator toolTipGenerator;
 115  
     private boolean mouseOverHighlight;
 116  
     private int columnTreeHighlight;
 117  
     private int rowTreeHighlight;
 118  
     private boolean selectionHighlight;
 119  
     private boolean averageHighlight;
 120  
 
 121  
     private TreeSet closedRows;
 122  
     private TreeSet closedColumns;
 123  
 
 124  
     private boolean drawFromLeftToRight;
 125  
     private boolean drawFromTopToBottom;
 126  
 
 127  
     /**
 128  
      * Creates a new plot that will draw a Hierarchical Clustering and
 129  
      * Heatmap for the given dataset.
 130  
      *
 131  
      * @param dataset  the dataset.
 132  
      */
 133  20
     public HCPlot(HCDataset dataset) {
 134  
 
 135  
         HCMediator rowHCMediator;
 136  
         HCMediator columnHCMediator;
 137  
 
 138  20
         this.dataset=dataset;
 139  20
         this.columnNamesVisibility = true;
 140  20
         this.rowNamesVisibility = true;
 141  
 
 142  20
         if (this.dataset.getColumnClusteringTree() != null) {
 143  
 
 144  11
             this.columnClusteringInfo = new StandardHCClusteringInfo(
 145  
                 this.dataset.getColumnClusteringTree(),
 146  
                 this.dataset.getHeatMap().getColumnNames(),
 147  
                 this.TOP
 148  
             );
 149  11
             this.columnTreeVisibility = true;
 150  
 
 151  11
         } else {
 152  
 
 153  8
             this.columnClusteringInfo = new DummyHCClusteringInfo(
 154  
                 this.dataset.getHeatMap().getColumnNames(),
 155  
                 this.TOP
 156  
             );
 157  8
             this.columnTreeVisibility = false;
 158  
 
 159  
         }
 160  
 
 161  19
         if (this.dataset.getRowClusteringTree() != null) {
 162  
 
 163  11
             this.rowClusteringInfo = new StandardHCClusteringInfo(
 164  
                 this.dataset.getRowClusteringTree(),
 165  
                 this.dataset.getHeatMap().getRowNames(),
 166  
                 this.LEFT
 167  
             );
 168  11
             this.rowTreeVisibility = true;
 169  
 
 170  11
         } else {
 171  
 
 172  8
             this.rowClusteringInfo = new DummyHCClusteringInfo(
 173  
                 this.dataset.getHeatMap().getRowNames(),
 174  
                 this.LEFT
 175  
             );
 176  8
             this.rowTreeVisibility = false;
 177  
 
 178  
         }
 179  
 
 180  19
         columnHCMediator = new HCMediator(this.columnClusteringInfo);
 181  19
         this.columnClusteringInfo.addChangeListener(this);
 182  19
         rowHCMediator = new HCMediator(this.rowClusteringInfo);
 183  19
         this.rowClusteringInfo.addChangeListener(this);
 184  
 
 185  19
         this.rowNames = new HeatMapAxis();
 186  19
         this.rowNames.setPlot(rowHCMediator);
 187  19
         this.rowNames.setLowerMargin(0);
 188  19
         this.rowNames.setUpperMargin(0);
 189  19
         this.rowNames.setMaximumCategoryLabelWidthRatio(
 190  
                 (float)0.95);
 191  
         //this.rowNames.setCategoryLabelPositionOffset(4);
 192  
 
 193  19
         this.columnNames = new HeatMapAxis();
 194  19
         this.columnNames.setPlot(columnHCMediator);
 195  19
         this.columnNames.setLowerMargin(0);
 196  19
         this.columnNames.setUpperMargin(0);
 197  19
         this.columnNames.setCategoryLabelPositions(
 198  
 
 199  
             CategoryLabelPositions.UP_90
 200  
         );
 201  19
          this.columnNames.setMaximumCategoryLabelWidthRatio(
 202  
                 (float)0.95);
 203  
         //this.columnNames = new HeatMapAxis();
 204  
         //this.columnNames.setPlot(columnHCMediator);
 205  
         //this.columnNames.setLowerMargin(0);
 206  
         //this.columnNames.setUpperMargin(-1);
 207  
         //this.columnNames.setCategoryLabelPositionOffset(0);
 208  
         //this.columnNames.setCategoryLabelPositions(
 209  
 
 210  19
         this.optionsEditor = new HCOptionsEditor(this);
 211  19
         this.paletteEditor = new GradientColorPaletteEditor();
 212  19
         this.palette = null;
 213  19
         this.setColoring(createDefaultColorMap());
 214  19
         this.selection = null;
 215  
 
 216  19
         this.closedColumns = new TreeSet();
 217  19
         this.closedRows = new TreeSet();
 218  
 
 219  
         // these should be reasonable defaults
 220  19
         double ratio = (double)(this.dataset.getHeatMap().getRowCount())
 221  
             / this.dataset.getHeatMap().getColumnsCount();
 222  19
         if ( ratio < 1 ) {
 223  
 
 224  
             // less rows than columns, ratio < 1
 225  18
             this.columnTreeSize = 0.2;
 226  18
             this.rowTreeSize = 0.2*ratio;
 227  18
             this.columnNamesSize = 0.12;
 228  18
             this.rowNamesSize = 0.12*ratio;
 229  18
             this.topMarginSize = 1.0/64.0;
 230  18
             this.leftMarginSize = 1.0/64.0*ratio;
 231  
 
 232  18
         } else {
 233  
 
 234  
             // more rows than columns, ratio >= 1
 235  1
             this.rowTreeSize = 0.2;
 236  1
             this.columnTreeSize = 0.2/ratio;
 237  1
             this.rowNamesSize = 0.12;
 238  1
             this.columnNamesSize = 0.12/ratio;
 239  1
             this.leftMarginSize = 1.0/64.0;
 240  1
             this.topMarginSize = 1.0/64.0/ratio;
 241  
 
 242  
         }
 243  19
         this.bottomMarginSize = this.topMarginSize;
 244  19
         this.rightMarginSize = this.leftMarginSize;
 245  
 
 246  19
         this.mouseOverHighlight = true;
 247  19
         this.selectionHighlight = true;
 248  19
         this.averageHighlight = true;
 249  19
         this.columnTreeHighlight = -1;
 250  19
         this.rowTreeHighlight = -1;
 251  
         
 252  19
         this.drawFromLeftToRight = true;
 253  19
         this.drawFromTopToBottom = true;
 254  
 
 255  19
     }
 256  
 
 257  
     /**
 258  
      * Calculates min and max values of the heatmap and returns
 259  
      * a scaled palette for the heatmap.
 260  
      *
 261  
      * @return  The GradientColorPalette.
 262  
      */
 263  
     private GradientColorPalette createDefaultColorMap() {
 264  
 
 265  
         GradientColorPalette palette;
 266  
 
 267  19
         double min=0;
 268  19
         double max=0;
 269  
         int i;
 270  
 
 271  286
         for (i = 0; i<this.dataset.getHeatMap().getItemCount(); i++) {
 272  
 
 273  267
             double value = this.dataset.getHeatMap().getItem(i).doubleValue();
 274  267
             if (value < min) min = value;
 275  267
             if (value > max) max = value;
 276  
 
 277  
         }
 278  
 
 279  19
         return new GradientColorPalette(min,max);
 280  
 
 281  
     }
 282  
 
 283  
     /**
 284  
      * Returns the column clustering info
 285  
      *
 286  
      * @return The HCClusteringInfo info object.
 287  
      */
 288  
     public AbstractHCClusteringInfo getColumnClusteringInfo() {
 289  
 
 290  3
         return this.columnClusteringInfo;
 291  
 
 292  
     }
 293  
 
 294  
     /**
 295  
      * Returns the row clustering info
 296  
      *
 297  
      * @return The HCClusteringInfo info object.
 298  
      */
 299  
     public AbstractHCClusteringInfo getRowClusteringInfo() {
 300  
 
 301  4
         return this.rowClusteringInfo;
 302  
 
 303  
     }
 304  
 
 305  
     /**
 306  
      * Returns the dataset used with this plot.
 307  
      *
 308  
      * @return  The dataset.
 309  
      */
 310  
     public HCDataset getDataset() {
 311  
 
 312  2
         return this.dataset;
 313  
 
 314  
     }
 315  
 
 316  
     /**
 317  
      * Returns a string describing the plot type.
 318  
      *
 319  
      * @return  The name.
 320  
      */
 321  
     public String getPlotType() {
 322  
 
 323  1
         return "Hierarchical Clustering and Heatmap";
 324  
 
 325  
     }
 326  
 
 327  
     /**
 328  
      * Sets the row clustering tree visible.
 329  
      */
 330  
     public void showRowTree() {
 331  
 
 332  5
         if (this.dataset.getRowClusteringTree() == null)
 333  1
             throw new NullPointerException("Cannot show a null tree.");
 334  4
         this.rowTreeVisibility = true;
 335  4
         notifyListeners(new PlotChangeEvent(this));
 336  
 
 337  4
     }
 338  
 
 339  
     /**
 340  
      * Sets the column clustering tree visible.
 341  
      */
 342  
     public void showColumnTree() {
 343  
 
 344  5
         if (this.dataset.getColumnClusteringTree() == null)
 345  1
             throw new NullPointerException("Cannot show a null tree.");
 346  4
         this.columnTreeVisibility = true;
 347  4
         notifyListeners(new PlotChangeEvent(this));
 348  
 
 349  4
     }
 350  
 
 351  
     /**
 352  
      * Sets the row clustering tree invisible.
 353  
      */
 354  
     public void hideRowTree() {
 355  
 
 356  5
         if (this.dataset.getRowClusteringTree() == null)
 357  1
             throw new NullPointerException("Cannot hide a null tree.");
 358  4
         this.rowTreeVisibility = false;
 359  4
         notifyListeners(new PlotChangeEvent(this));
 360  
 
 361  4
     }
 362  
 
 363  
     /**
 364  
      * Sets the column clustering tree invisible.
 365  
      */
 366  
     public void hideColumnTree() {
 367  
 
 368  5
         if (this.dataset.getColumnClusteringTree() == null)
 369  1
             throw new NullPointerException("Cannot hide a null tree.");
 370  4
         this.columnTreeVisibility = false;
 371  4
         notifyListeners(new PlotChangeEvent(this));
 372  
 
 373  4
     }
 374  
 
 375  
     /**
 376  
      * Returns true if row clustering tree is visible, false otherwise.
 377  
      *
 378  
      * @return  A boolean.
 379  
      */
 380  
     public boolean getRowTreeVisibility() {
 381  
 
 382  18
         if (this.dataset.getRowClusteringTree() == null)
 383  3
             return false;
 384  15
         return this.rowTreeVisibility;
 385  
 
 386  
     }
 387  
 
 388  
     /**
 389  
      * Returns true if the column clustering tree is visible, false otherwise.
 390  
      *
 391  
      * @return  A boolean.
 392  
      */
 393  
     public boolean getColumnTreeVisibility() {
 394  
 
 395  17
         if (this.dataset.getColumnClusteringTree() == null)
 396  2
             return false;
 397  15
         return this.columnTreeVisibility;
 398  
 
 399  
     }
 400  
 
 401  
     /**
 402  
      * Sets column names visible.
 403  
      */
 404  
     public void showColumnNames() {
 405  
 
 406  2
         this.columnNamesVisibility = true;
 407  2
         notifyListeners(new PlotChangeEvent(this));
 408  
 
 409  2
     }
 410  
 
 411  
     /**
 412  
      * Sets row names visible.
 413  
      */
 414  
     public void showRowNames() {
 415  
 
 416  2
         this.rowNamesVisibility = true;
 417  2
         notifyListeners(new PlotChangeEvent(this));
 418  
 
 419  2
     }
 420  
 
 421  
     /**
 422  
      * Sets column names invisible.
 423  
      */
 424  
     public void hideColumnNames() {
 425  
 
 426  2
         this.columnNamesVisibility = false;
 427  2
         notifyListeners(new PlotChangeEvent(this));
 428  
 
 429  2
     }
 430  
 
 431  
     /**
 432  
      * Sets row names invisible.
 433  
      */
 434  
     public void hideRowNames() {
 435  
 
 436  2
         this.rowNamesVisibility = false;
 437  2
         notifyListeners(new PlotChangeEvent(this));
 438  
 
 439  2
     }
 440  
 
 441  
     /**
 442  
      * Returns true if the column names are visible, false otherwise.
 443  
      *
 444  
      * @return  A boolean.
 445  
      */
 446  
     public boolean getColumnNamesVisibility() {
 447  
 
 448  12
         return this.columnNamesVisibility;
 449  
 
 450  
     }
 451  
 
 452  
     /**
 453  
      * Returns true if specified names are visible, false otherwise.
 454  
      *
 455  
      * @return  A boolean.
 456  
      */
 457  
     public boolean getRowNamesVisibility() {
 458  
 
 459  12
         return this.rowNamesVisibility;
 460  
 
 461  
     }
 462  
 
 463  
     /**
 464  
      * Sets a palette to be used for coloring heatmap.
 465  
      *
 466  
      * @param color  the color palette.
 467  
      */
 468  
     public void setColoring(GradientColorPalette color) {
 469  
 
 470  21
         if (this.palette != null) this.palette.removeChangeListener(this);
 471  21
         this.palette = color;
 472  21
         this.palette.addChangeListener(this);
 473  20
         this.paletteEditor.setColoring (color);
 474  20
         notifyListeners(new PlotChangeEvent(this));
 475  
 
 476  20
     }
 477  
 
 478  
     /**
 479  
      * Returns the palette used for the heatmap.
 480  
      *
 481  
      * @return  The heatmap color palette.
 482  
      */
 483  
     public GradientColorPalette getColoring() {
 484  
 
 485  1
         return this.palette;
 486  
 
 487  
     }
 488  
 
 489  
     /**
 490  
      * Sets row names-size as percentage of visualization.
 491  
      *
 492  
      * @param size  the size as proportion. 0 is 0 pixels, 1 is the whole
 493  
      * visualisation.
 494  
      */
 495  
     public void setRowNamesSize(double size) {
 496  
 
 497  3
         if (size < 0) throw new IllegalArgumentException(
 498  
                 "Size of names cannot be negative.");
 499  2
         if (size > 1) throw new IllegalArgumentException(
 500  
                 "Size of names cannot be more than 1.");
 501  1
         this.rowNamesSize = size;
 502  1
         notifyListeners(new PlotChangeEvent(this));
 503  
 
 504  1
     }
 505  
 
 506  
     /**
 507  
      * Sets column names-size as percentage of visualization.
 508  
      *
 509  
      * @param size  the size as proportion. 0 is 0 pixels, 1 is the whole
 510  
      * visualisation.
 511  
      */
 512  
     public void setColumnNamesSize(double size) {
 513  
 
 514  3
         if (size < 0) throw new IllegalArgumentException(
 515  
                 "Size of names cannot be negative.");
 516  2
         if (size > 1) throw new IllegalArgumentException(
 517  
                 "Size of names cannot be more than 1.");
 518  1
         this.columnNamesSize = size;
 519  1
         notifyListeners(new PlotChangeEvent(this));
 520  
 
 521  1
     }
 522  
 
 523  
     /**
 524  
      * Returns row names-size as percentage of visualization.
 525  
      *
 526  
      * @return  A value between [0,1] that specifies the size of names.
 527  
      */
 528  
     public double getRowNamesSize() {
 529  
 
 530  3
         return this.rowNamesSize;
 531  
 
 532  
     }
 533  
 
 534  
     /**
 535  
      * Returns column names-size as percentage of visualization.
 536  
      *
 537  
      * @return  A value between [0,1] that specifies the size of names.
 538  
      */
 539  
     public double getColumnNamesSize() {
 540  
 
 541  3
         return this.columnNamesSize;
 542  
 
 543  
     }
 544  
 
 545  
     /**
 546  
      * Sets row tree-size as percentage of visualization.
 547  
      *
 548  
      * @param size  the size as proportion. 0 is 0 pixels, 1 is the whole
 549  
      * visualisation.
 550  
      */
 551  
     public void setRowTreeSize(double size) {
 552  
 
 553  8
         if (size < 0) throw new IllegalArgumentException(
 554  
                 "Size of a tree cannot be negative.");
 555  5
         if (size > 1) throw new IllegalArgumentException(
 556  
                 "Size of a tree cannot be more than 1.");
 557  3
         this.rowTreeSize = size;
 558  3
         notifyListeners(new PlotChangeEvent(this));
 559  
 
 560  3
     }
 561  
 
 562  
     /**
 563  
      * Sets column tree-size as percentage of visualization.
 564  
      *
 565  
      * @param size  the size as proportion. 0 is 0 pixels, 1 is the whole
 566  
      * visualisation.
 567  
      */
 568  
     public void setColumnTreeSize(double size) {
 569  
 
 570  8
         if (size < 0) throw new IllegalArgumentException(
 571  
                 "Size of a tree cannot be negative.");
 572  5
         if (size > 1) throw new IllegalArgumentException(
 573  
                 "Size of a tree cannot be more than 1.");
 574  3
         this.columnTreeSize = size;
 575  3
         notifyListeners(new PlotChangeEvent(this));
 576  
 
 577  3
     }
 578  
 
 579  
     /**
 580  
      * Returns the size of the row tree as percentage of visualisation size.
 581  
      *
 582  
      * @return  A value between [0,1] that specifies the size of the tree.
 583  
      */
 584  
     public double getRowTreeSize() {
 585  
 
 586  6
         return this.rowTreeSize;
 587  
 
 588  
     }
 589  
 
 590  
     /**
 591  
      * Returns the size of the row tree as percentage of visualisation size.
 592  
      *
 593  
      * @return  A value between [0,1] that specifies the size of the tree.
 594  
      */
 595  
     public double getColumnTreeSize() {
 596  
 
 597  6
         return this.columnTreeSize;
 598  
 
 599  
     }
 600  
 
 601  
     /**
 602  
      * Sets the selection.
 603  
      *
 604  
      * @param selection  the rectangle specifying which blocks of the visible
 605  
      * heatmap belong to the selection. Indexing begins from 0.
 606  
      *
 607  
      */
 608  
     public void setSelection(Rectangle selection)
 609  
         throws IndexOutOfBoundsException {
 610  
 
 611  11
         if (selection.getMinX() < 0) throw new IndexOutOfBoundsException();
 612  9
         if (selection.getMinY() < 0) throw new IndexOutOfBoundsException();
 613  8
         if (selection.getMaxX() > this.dataset.getHeatMap().getColumnsCount())
 614  1
             throw new IndexOutOfBoundsException();
 615  7
         if (selection.getMaxY() > this.dataset.getHeatMap().getRowCount())
 616  1
             throw new IndexOutOfBoundsException();
 617  
 
 618  6
         Rectangle oldSelection = this.selection;
 619  6
         this.selection = selection;
 620  6
         notifyListeners(new SelectionChangeEvent(this,oldSelection));
 621  
 
 622  6
     }
 623  
 
 624  
     /**
 625  
      * Returns the current selection
 626  
      *
 627  
      * @return  The rectangle specifying which blocks of the visible
 628  
      * heatmap belong to the selection. Indexing begins from 0.
 629  
      */
 630  
     public Rectangle getSelection() {
 631  
 
 632  4
         return this.selection;
 633  
     }
 634  
 
 635  
     /**
 636  
      * Turns on or off the feature, that makes
 637  
      * branches of trees highlighted when the mouse cursor is moved
 638  
      * on the corresponding heatmap blocks.
 639  
      *
 640  
      * @param highlight  A boolean.
 641  
      */
 642  
     public void setMouseOverHighlight(boolean highlight) {
 643  
 
 644  2
         this.mouseOverHighlight = highlight;
 645  2
         notifyListeners(new PlotChangeEvent(this));
 646  
 
 647  2
     }
 648  
 
 649  
     /**
 650  
      * Returns the state of the feature, that makes
 651  
      * branches of trees highlighted, when mouse cursor is moved
 652  
      * on corresponding heatmap blocks.
 653  
      *
 654  
      * @return  True, if the feature is turned on,
 655  
      * false otherwise.
 656  
      */
 657  
     public boolean getMouseOverHighlight() {
 658  
 
 659  2
         return this.mouseOverHighlight;
 660  
 
 661  
     }
 662  
 
 663  
     /**
 664  
      * Turns on or off the feature, that makes the
 665  
      * heatmap bloks highlighted, when the mouse is clicked
 666  
      * on the corresponding heatmapblocks or tree nodes.
 667  
      *
 668  
      * @param highlight  a boolean.
 669  
      */
 670  
     public void setSelectionHighlight(boolean highlight) {
 671  
 
 672  2
         this.selectionHighlight = highlight;
 673  2
         notifyListeners(new PlotChangeEvent(this));
 674  
 
 675  2
     }
 676  
 
 677  
     /**
 678  
      * Returns the state of the feature, that makes
 679  
      * heatmap blocks highlighted, when mouse is clicked
 680  
      * on corresponding heatmap blocks or tree nodes.
 681  
      *
 682  
      * @return  True, if the feature is turned on,
 683  
      * false otherwise.
 684  
      */
 685  
     public boolean getSelectionHighlight() {
 686  
 
 687  8
         return this.selectionHighlight;
 688  
 
 689  
     }
 690  
 
 691  
     /**
 692  
      * Turns on or off the feature, that makes
 693  
      * heatmap blocks highlighted, when a corresponding clustering tree
 694  
      * node is closed by a double click.
 695  
      *
 696  
      * @param highlight  A boolean.
 697  
      */
 698  
     public void setAverageHighlight(boolean highlight) {
 699  
 
 700  3
         this.averageHighlight = highlight;
 701  3
         notifyListeners(new PlotChangeEvent(this));
 702  
 
 703  3
     }
 704  
 
 705  
 
 706  
     /**
 707  
      * Returns the state of the feature, that makes
 708  
      * heatmap blocks highlighted, when a corresponding clustering tree
 709  
      * node is closed by double click.
 710  
      *
 711  
      * @return  True, if the feature is turned on,
 712  
      * false otherwise.
 713  
      */
 714  
     public boolean getAverageHighlight() {
 715  
 
 716  16
         return this.averageHighlight;
 717  
 
 718  
     }
 719  
 
 720  
     /**
 721  
      * Sets specified branch on the row tree highlighted.
 722  
      *
 723  
      * @param index  The index of the heatmap row that
 724  
      * specifies the branch to highlight. Negative
 725  
      * values turn highlight off.
 726  
      */
 727  
     public void setRowTreeHighlight(int index) {
 728  
         
 729  3
         this.rowTreeHighlight = index;
 730  3
         notifyListeners(new PlotChangeEvent(this));
 731  
 
 732  3
     }
 733  
 
 734  
     /**
 735  
      * Sets specified branch on the column tree highlighted.
 736  
      *
 737  
      * @param index  The index of the heatmap column that
 738  
      * specifies the branch to highlight. Negative
 739  
      * values turn highlight off.
 740  
      */
 741  
     public void setColumnTreeHighlight(int index) {
 742  
         
 743  3
         this.columnTreeHighlight = index;
 744  3
         notifyListeners(new PlotChangeEvent(this));
 745  
 
 746  3
     }
 747  
 
 748  
     /**
 749  
      * Returns the index of the highlighted branch.
 750  
      *
 751  
      * @return  The index of the heatmap row that
 752  
      * specifies the currently highlighted clustering tree branch.
 753  
      */
 754  
     public int getRowTreeHighlight() {
 755  
         
 756  3
         return this.rowTreeHighlight;
 757  
 
 758  
     }
 759  
 
 760  
     /**
 761  
      * Returns the index of the highlighted branch.
 762  
      *
 763  
      * @return  The index of the heatmap column that
 764  
      * specifies the currently highlighted clustering tree branch.
 765  
      */
 766  
     public int getColumnTreeHighlight() {
 767  
         
 768  3
         return this.columnTreeHighlight;
 769  
 
 770  
     }
 771  
 
 772  
     /**
 773  
      * Returns the number of visible rows on the heatmap.
 774  
      *
 775  
      * @return  The number of visible rows on the heatmap.
 776  
      */
 777  
     public int getHeatMapRowCount() {
 778  
 
 779  14
         return this.rowClusteringInfo.getNumberOfVisibleItems();
 780  
 
 781  
     }
 782  
 
 783  
     /**
 784  
      * Returns the number of visible columns on the heatmap.
 785  
      *
 786  
      * @return  The number of visible columns on heatmap.
 787  
      */
 788  
     public int getHeatMapColumnsCount() {
 789  
 
 790  14
         return this.columnClusteringInfo.getNumberOfVisibleItems();
 791  
 
 792  
     }
 793  
 
 794  
     /**
 795  
      * Returns the indexes of the rows of the dataset
 796  
      * that corresponds to the specified row of the
 797  
      * visible heatmap.
 798  
      *
 799  
      * @param row  the row.
 800  
      * @return  The datarange specifying the rows.
 801  
      */
 802  
     public DataRange getDatasetRowsAtHeatMapRow(int row) {
 803  
 
 804  4
         return this.rowClusteringInfo.getDataRangeForVisibleIndex(row);
 805  
 
 806  
     }
 807  
 
 808  
     /**
 809  
      * Returns the indexes of the columns of the dataset
 810  
      * that corresponds to the specified column of the
 811  
      * visible heatmap.
 812  
      *
 813  
      * @param column  the column.
 814  
      * @return  The datarange specifying the columns.
 815  
      */
 816  
     public DataRange getDatasetColumnsAtHeatMapColumn(int column) {
 817  
 
 818  4
         return this.columnClusteringInfo.getDataRangeForVisibleIndex(column);
 819  
 
 820  
     }
 821  
 
 822  
     /**
 823  
      * Draws a subtree of the clustering tree rooted at specified node.
 824  
      * This method also creates HCTreeNodeEntities of the nodes drawn.
 825  
      *
 826  
      * @param g2  the Graphics2D object to draw the subtree on
 827  
      * @param state  a state object containing information about the geometry
 828  
      * of the plot.
 829  
      * @param node  the node the subtree is rooted at.
 830  
      * @param area  the area where the subtree has to fit.
 831  
      * @param edge  edge of the heatmap the subtree is drawn to.
 832  
      */
 833  
     private HCTreeNodeEntity drawSubTree(
 834  
             Graphics2D g2,
 835  
             HCPlotState state,
 836  
             HCTreeNodeInfo node,
 837  
             Rectangle2D area,
 838  
             RectangleEdge edge) {
 839  
 
 840  
         HCTreeNodeEntity leftEntity;
 841  
         HCTreeNodeEntity rightEntity;
 842  
         HCTreeNodeEntity thisEntity;
 843  
         Rectangle thisArea;
 844  
         Rectangle2D areaForRightSubTree;
 845  
         Point center;
 846  
         EntityCollection entities;
 847  
         String tip;
 848  
 
 849  
         // first we calculate needed areas and draw subtrees.
 850  
 
 851  70
         if (!node.isNodeOpen()) {
 852  
 
 853  
             // closed node
 854  42
             thisArea = state.calculateClosedNodeArea(
 855  
                 area,
 856  
                 node,
 857  
                 edge
 858  
             );
 859  42
             center = state.calculateClosedNodeCenter(
 860  
                 thisArea,
 861  
                 node.getNode().getHeight(),
 862  
                 edge
 863  
             );
 864  
 
 865  
 
 866  42
         } else if (
 867  
             (node.getLeftChild() == null) ||
 868  
             (node.getRightChild() == null)
 869  
         ) {
 870  
 
 871  
             // open leaf node
 872  
             // Currently (2006-08-20), this block should be unreachable.
 873  0
             thisArea = state.calculateLeafNodeArea(area,node,edge);
 874  0
             center = state.calculateLeafNodeCenter(thisArea,edge);
 875  
 
 876  0
         } else {
 877  
 
 878  
             // open branch node
 879  28
             leftEntity = drawSubTree (
 880  
                 g2,
 881  
                 state,
 882  
                 node.getLeftChild(),
 883  
                 area,
 884  
                 edge
 885  
             );
 886  
 
 887  28
             areaForRightSubTree = state.calculateSubTreeArea(
 888  
                 area,
 889  
                 leftEntity.getSubTreeArea(),
 890  
                 edge
 891  
             );
 892  
 
 893  28
             rightEntity = drawSubTree (
 894  
                 g2,
 895  
                 state,
 896  
                 node.getRightChild(),
 897  
                 areaForRightSubTree,
 898  
                 edge
 899  
             );
 900  28
             thisArea = state.calculateBranchNodeArea(
 901  
                 node.getNode().getHeight(),
 902  
                 leftEntity,
 903  
                 rightEntity,
 904  
                 area,
 905  
                 edge
 906  
             );
 907  28
             center = state.calculateBranchNodeCenter(
 908  
                 node.getNode().getHeight(),
 909  
                 leftEntity,
 910  
                 rightEntity,
 911  
                 area,
 912  
                 edge
 913  
             );
 914  
 
 915  
         }
 916  
 
 917  
         // the only thing left to do is to create the entity and actually
 918  
         // draw this node.
 919  70
         if (this.toolTipGenerator != null) {
 920  
 
 921  65
             tip = this.toolTipGenerator.generateToolTip(node);
 922  
 
 923  65
         } else tip = null;
 924  70
         thisEntity = new HCTreeNodeEntity(
 925  
             new Rectangle(
 926  
                 (int)center.getX()-5,
 927  
                 (int)center.getY()-5,
 928  
                 11,
 929  
                 11
 930  
             ),
 931  
             tip,
 932  
             null,
 933  
             center,
 934  
             thisArea,
 935  
             node
 936  
         );
 937  70
         entities = state.getEntityCollection();
 938  70
         if (entities != null) {
 939  
 
 940  70
                 entities.add(thisEntity);
 941  
 
 942  
         }
 943  70
         redrawNode(g2, state, thisEntity, edge);
 944  
 
 945  70
         return thisEntity;
 946  
 
 947  
     }
 948  
 
 949  
     /**
 950  
      * Draws a single node of a clustering tree.
 951  
      * This does not create HCTreeNodeEntities so it can be used repeatedly.
 952  
      *
 953  
      * @param g2  the Graphics2D object to draw the subtree on
 954  
      * @param state  a state object containing information about the geometry
 955  
      * of the plot.
 956  
      * @param entity  the entity describing the node being drawn.
 957  
      * @param edge  edge of the heatmap the subtree is drawn to.
 958  
      */
 959  
     private void redrawNode(
 960  
             Graphics2D g2,
 961  
             HCPlotState state,
 962  
             HCTreeNodeEntity entity,
 963  
             RectangleEdge edge) {
 964  
 
 965  70
         int centerX = (int)entity.getCenter().getX();
 966  70
         int centerY = (int)entity.getCenter().getY();
 967  70
         Rectangle subTreeArea = entity.getSubTreeArea();
 968  70
         int width = (int)(subTreeArea.getWidth());
 969  70
         int height = (int)(subTreeArea.getHeight());
 970  70
         int minX = (int)(subTreeArea.getMinX());
 971  70
         int minY = (int)(subTreeArea.getMinY());
 972  70
         int maxX = (int)(subTreeArea.getMinX()+width);
 973  70
         int maxY = (int)(subTreeArea.getMinY()+height);
 974  70
         HCTreeNode node = entity.getHCTreeNodeInfo().getNode();
 975  
         double height1;
 976  
         double height2;
 977  
 
 978  70
         g2.setPaint(getOutlinePaint());
 979  
 
 980  70
         if (entity.getHCTreeNodeInfo().isNodeOpen()) {
 981  
 
 982  
             // open nodes
 983  28
             if ((node.getLeftChild() != null) &&
 984  
                 (node.getRightChild() != null)
 985  
             ) {
 986  
 
 987  
                 // Root and branch nodes
 988  28
                 height1 = node.getLeftChild().getHeight();
 989  28
                 height2 = node.getRightChild().getHeight();
 990  28
                 if (edge == RectangleEdge.TOP) {
 991  
 
 992  16
                     g2.drawLine(
 993  
                         minX,
 994  
                         centerY,
 995  
                         minX,
 996  
                         maxY-(int)(
 997  
                             height1*state.getColumnTreeHeightUnitInPixels()
 998  
                             +state.getSizeOfNodeSymbol()/2
 999  
                         ));
 1000  16
                     g2.drawLine(
 1001  
                         maxX,
 1002  
                         centerY,
 1003  
                         maxX,
 1004  
                         maxY-(int)(
 1005  
                             height2*state.getColumnTreeHeightUnitInPixels()
 1006  
                             +state.getSizeOfNodeSymbol()/2
 1007  
                         ));
 1008  16
                     g2.drawLine(
 1009  
                         minX,
 1010  
                         centerY,
 1011  
                         maxX,
 1012  
                         centerY);
 1013  
 
 1014  16
                 } else if (edge == RectangleEdge.BOTTOM) {
 1015  
 
 1016  
                     // Currently (2006-08-20), this block should be unreachable.
 1017  
 
 1018  0
                     g2.drawLine(
 1019  
                         minX,
 1020  
                         centerY,
 1021  
                         minX,
 1022  
                         minY+(int)(
 1023  
                             height1*state.getColumnTreeHeightUnitInPixels()
 1024  
                             +state.getSizeOfNodeSymbol()/2
 1025  
                         ));
 1026  0
                     g2.drawLine(
 1027  
                         maxX,
 1028  
                         centerY,
 1029  
                         maxX,
 1030  
                         minY+(int)(
 1031  
                             height2*state.getColumnTreeHeightUnitInPixels()
 1032  
                             +state.getSizeOfNodeSymbol()/2
 1033  
                         ));
 1034  0
                     g2.drawLine(
 1035  
                         minX,
 1036  
                         centerY,
 1037  
                         maxX,
 1038  
                         centerY);
 1039  
 
 1040  0
                 } else if(edge == RectangleEdge.LEFT) {
 1041  
 
 1042  12
                     g2.drawLine(
 1043  
                         centerX,
 1044  
                         minY,
 1045  
                         maxX-(int)(
 1046  
                             height1*state.getRowTreeHeightUnitInPixels()
 1047  
                             +state.getSizeOfNodeSymbol()/2
 1048  
                         ),
 1049  
                         minY);
 1050  12
                     g2.drawLine(
 1051  
                         centerX,
 1052  
                         maxY,
 1053  
                         maxX-(int)(
 1054  
                             height2*state.getRowTreeHeightUnitInPixels()
 1055  
                             +state.getSizeOfNodeSymbol()/2
 1056  
                         ),
 1057  
                         maxY);
 1058  12
                     g2.drawLine(
 1059  
                         centerX,
 1060  
                         minY,
 1061  
                         centerX,
 1062  
                         maxY);
 1063  
 
 1064  12
                 } else if(edge == RectangleEdge.RIGHT) {
 1065  
 
 1066  
                     // Currently (2006-08-20), this block should be unreachable.
 1067  
 
 1068  0
                     g2.drawLine(
 1069  
                         centerX,
 1070  
                         minY,
 1071  
                         minX+(int)(
 1072  
                             height1*state.getRowTreeHeightUnitInPixels()
 1073  
                             +state.getSizeOfNodeSymbol()/2
 1074  
                         ),
 1075  
                         minY);
 1076  0
                     g2.drawLine(
 1077  
                         centerX,
 1078  
                         maxY,
 1079  
                         minX+(int)(
 1080  
                             height2*state.getRowTreeHeightUnitInPixels()
 1081  
                             +state.getSizeOfNodeSymbol()/2
 1082  
                         ),
 1083  
                         maxY);
 1084  0
                     g2.drawLine(
 1085  
                         centerX,
 1086  
                         minY,
 1087  
                         centerX,
 1088  
                         maxY);
 1089  
 
 1090  0
                 } else throw new IllegalArgumentException("Invalid edge.");
 1091  
 
 1092  
             } else {
 1093  
                 
 1094  
                 ; //throw new Exception("leaf nodes should always be closed.");
 1095  
 
 1096  
             }
 1097  28
             Rectangle r = new Rectangle(
 1098  
                     centerX-state.getSizeOfNodeSymbol()/2,
 1099  
                     centerY-state.getSizeOfNodeSymbol()/2,
 1100  
                     state.getSizeOfNodeSymbol(),
 1101  
                     state.getSizeOfNodeSymbol());
 1102  28
             g2.fill(r);
 1103  
 
 1104  28
         } else {
 1105  
 
 1106  
             // closed node.
 1107  
             Rectangle r;
 1108  
 
 1109  42
             if ((node.getLeftChild() == null) ||
 1110  
                 (node.getRightChild() == null)
 1111  
             ) {
 1112  
 
 1113  
                 // closed leaf node.
 1114  38
                 if (edge == RectangleEdge.TOP) {
 1115  
 
 1116  20
                     r = new Rectangle(
 1117  
                         centerX-state.getSizeOfNodeSymbol()/2,
 1118  
                         centerY-state.getSizeOfNodeSymbol()/2,
 1119  
                         state.getSizeOfNodeSymbol(),
 1120  
                         state.getSizeOfNodeSymbol()/2);
 1121  
 
 1122  20
                 } else if (edge == RectangleEdge.BOTTOM) {
 1123  
 
 1124  
                     // Currently (2006-08-20), this block should be unreachable.
 1125  0
                     r = new Rectangle(
 1126  
                         centerX-state.getSizeOfNodeSymbol()/2,
 1127  
                         centerY,
 1128  
                         state.getSizeOfNodeSymbol(),
 1129  
                         state.getSizeOfNodeSymbol()/2);
 1130  
 
 1131  0
                 } else if (edge == RectangleEdge.LEFT) {
 1132  
 
 1133  18
                     r = new Rectangle(
 1134  
                         centerX-state.getSizeOfNodeSymbol()/2,
 1135  
                         centerY-state.getSizeOfNodeSymbol()/2,
 1136  
                         state.getSizeOfNodeSymbol()/2,
 1137  
                         state.getSizeOfNodeSymbol());
 1138  
 
 1139  18
                 } else if (edge == RectangleEdge.RIGHT) {
 1140  
 
 1141  
                     // Currently (2006-08-20), this block should be unreachable.
 1142  0
                     r = new Rectangle(
 1143  
                         centerX,
 1144  
                         centerY-state.getSizeOfNodeSymbol()/2,
 1145  
                         state.getSizeOfNodeSymbol()/2,
 1146  
                         state.getSizeOfNodeSymbol());
 1147  
 
 1148  0
                 } else throw new IllegalArgumentException("Invalid edge.");
 1149  
 
 1150  
             } else {
 1151  
 
 1152  
                 // closed branch  node.
 1153  4
                     r = new Rectangle(
 1154  
                     centerX-(state.getSizeOfNodeSymbol())/2,
 1155  
                     centerY-(state.getSizeOfNodeSymbol())/2,
 1156  
                     state.getSizeOfNodeSymbol(),
 1157  
                     state.getSizeOfNodeSymbol());
 1158  
 
 1159  
             }
 1160  42
             if (edge == RectangleEdge.TOP) {
 1161  
 
 1162  23
                 g2.drawLine( centerX, (int)r.getMaxY(), centerX, maxY );
 1163  
 
 1164  23
             } else if (edge == RectangleEdge.BOTTOM) {
 1165  
 
 1166  
                 // Currently (2006-08-20), this block should be unreachable.
 1167  0
                 g2.drawLine( centerX, (int)r.getMinY(), centerX, minY );
 1168  
 
 1169  0
             } else if(edge == RectangleEdge.LEFT) {
 1170  
 
 1171  19
                 g2.drawLine( (int)r.getMaxX(),centerY,maxX,centerY);
 1172  
 
 1173  19
             } else if(edge == RectangleEdge.RIGHT) {
 1174  
 
 1175  
                 // Currently (2006-08-20), this block should be unreachable.
 1176  0
                 g2.drawLine( (int)r.getMinX(),centerY,minX,centerY);
 1177  
 
 1178  0
             } else throw new IllegalArgumentException("Invalid edge.");
 1179  
 
 1180  42
             g2.draw(r);
 1181  
         }
 1182  
 
 1183  70
     }
 1184  
 
 1185  
 
 1186  
     /**
 1187  
      * Draws a single block of heatmap.
 1188  
      *
 1189  
      * @param g2  the Graphics2D object to draw the subtree on.
 1190  
      * @param area  the area, where the heatmap is being drawn.
 1191  
      * @param state  a state object containing information about the geometry
 1192  
      * of the plot.
 1193  
      * @param row  the row of the heatmap block.
 1194  
      * @param column  the column of the heatmap block.
 1195  
      */
 1196  
     private void drawBlock (
 1197  
             Graphics2D g2,
 1198  
             Rectangle2D area,
 1199  
             HCPlotState state,
 1200  
             int row,
 1201  
             int column) {
 1202  
         DataRange columns;
 1203  
         DataRange rows;
 1204  
         HeatMapBlockEntity entity;
 1205  
         EntityCollection entities;
 1206  
         Rectangle r;
 1207  
         int minRow;
 1208  
         int maxRow;
 1209  
         int minColumn;
 1210  
         int maxColumn;
 1211  
         int rowCounter;
 1212  
         int columnCounter;
 1213  
         int blockCount;
 1214  
         double averageValue;
 1215  
         String tip;
 1216  
 
 1217  
         // O(lg sqrt(n))
 1218  74
         columns = this.columnClusteringInfo.getDataRangeForVisibleIndex(column);
 1219  74
         rows = this.rowClusteringInfo.getDataRangeForVisibleIndex(row);
 1220  
 
 1221  
         try {
 1222  74
             minColumn = columns.getLeftBound();
 1223  74
             maxColumn = columns.getRightBound();
 1224  74
             minRow = rows.getLeftBound();
 1225  74
             maxRow = rows.getRightBound();
 1226  0
         } catch (Exception e) {
 1227  0
             return; // trying to draw something that doesn't exist. throw?
 1228  74
         }
 1229  
         // any reason to cache these? don't think so.
 1230  
         for (
 1231  74
             averageValue = 0, blockCount = 0, rowCounter = minRow;
 1232  158
             rowCounter <= maxRow;
 1233  84
             rowCounter++
 1234  
         ) {
 1235  
             for (
 1236  84
                 columnCounter = minColumn;
 1237  204
                 columnCounter <= maxColumn;
 1238  120
                 columnCounter++, blockCount++
 1239  
             ) {
 1240  120
                 averageValue += this.dataset.getHeatMap()
 1241  
                     .get(rowCounter,columnCounter);
 1242  
             }
 1243  
         }
 1244  74
         averageValue = averageValue/blockCount;
 1245  
 
 1246  74
         g2.setColor(this.palette.getColor(averageValue));
 1247  
 
 1248  
         // we need to calculate width and height stupidly to
 1249  
         // handle rounding problems.
 1250  74
         r = new Rectangle(
 1251  
             state.getHeatMapXCoordinate(column),
 1252  
             state.getHeatMapYCoordinate(row),
 1253  
             state.getHeatMapXCoordinate(column+1) - 
 1254  
                 state.getHeatMapXCoordinate(column),
 1255  
             state.getHeatMapYCoordinate(row+1) -
 1256  
                 state.getHeatMapYCoordinate(row)
 1257  
         );
 1258  
 
 1259  74
         g2.fill(r);
 1260  
 
 1261  74
         if (blockCount>1 && getAverageHighlight()) {
 1262  
 
 1263  14
             if ( rows.getWidth() > 1) {
 1264  5
                Integer introw = new Integer(row);
 1265  5
                closedRows.add(introw);
 1266  
             }
 1267  14
             if ( columns.getWidth() > 1) {
 1268  9
                Integer intcolumn = new Integer(column);
 1269  9
                closedColumns.add(intcolumn);
 1270  
             }
 1271  
 
 1272  
         }
 1273  
 
 1274  74
         if (this.toolTipGenerator != null) {
 1275  59
             tip = this.toolTipGenerator.generateToolTip(
 1276  
                     this.dataset.getHeatMap(),
 1277  
                     rows,
 1278  
                     columns
 1279  
             );
 1280  59
         }
 1281  15
         else tip = null;
 1282  74
         entity = new HeatMapBlockEntity(
 1283  
             r,
 1284  
             tip,
 1285  
             null,
 1286  
             row,
 1287  
             column
 1288  
         );
 1289  74
         entities = state.getEntityCollection();
 1290  74
         if (entities != null) {
 1291  74
             entities.add(entity);
 1292  
         }
 1293  
 
 1294  74
     }
 1295  
 
 1296  
 
 1297  
     /**
 1298  
      * Draws the rectangles indicating selections in the heatmap.
 1299  
      *
 1300  
      * @param g2  the Graphics2D object to draw the heatmap to.
 1301  
      * @param state  a state object containing information about the geometry
 1302  
      * of the plot.
 1303  
      * @param color  color to use.
 1304  
      * @param r  the rectangle specifying the selection.
 1305  
      */
 1306  
     private void drawSelection(Graphics2D g2, HCPlotState state, Color color, Rectangle r) {
 1307  
 
 1308  7
             g2.setColor(color);
 1309  
 
 1310  7
             int column = (int)r.getX();
 1311  7
             int row = (int)r.getY();
 1312  7
             int lastrow = row + (int)r.getHeight()-1;
 1313  7
             int lastcolumn = column + (int)r.getWidth()-1;
 1314  
 
 1315  7
             g2.draw(new Rectangle(
 1316  
                 state.getHeatMapXCoordinate(column),
 1317  
                 state.getHeatMapYCoordinate(row),
 1318  
                 state.getHeatMapXCoordinate(lastcolumn+1)
 1319  
                     -state.getHeatMapXCoordinate(column)-1,
 1320  
                 state.getHeatMapYCoordinate(lastrow+1)
 1321  
                     -state.getHeatMapYCoordinate(row)-1
 1322  
             ));
 1323  7
     }
 1324  
 
 1325  
     /**
 1326  
      * Draws the heatmap.
 1327  
      *
 1328  
      * @param g2  the object to draw the heatmap on.
 1329  
      * @param area  where to exactly draw heatmap.
 1330  
      */
 1331  
     private void drawHeatMap(
 1332  
             Graphics2D g2,
 1333  
             HCPlotState state,
 1334  
             Rectangle2D area,
 1335  
             int visibleColumns,
 1336  
             int visibleRows
 1337  
             ) {
 1338  
 
 1339  
         int height;
 1340  
         int width;
 1341  
         int row;
 1342  
         int column;
 1343  
         int minRow;
 1344  
         int minColumn;
 1345  
         int maxRow;
 1346  
         int maxColumn;
 1347  11
         int count = 0;
 1348  
 
 1349  11
         Rectangle clip = g2.getClip().getBounds();
 1350  
 
 1351  
         // only draw as many blocks as there are in the clipping region.
 1352  8
         minColumn = (int)Math.floor(
 1353  
                 (clip.getMinX() - area.getMinX()) / state.getBlockWidth()
 1354  
                 );
 1355  8
         if (minColumn < 0) minColumn = 0;
 1356  
 
 1357  8
         minRow = (int)Math.floor(
 1358  
                 (clip.getMinY() - area.getMinY()) / state.getBlockHeight()
 1359  
                 );
 1360  8
         if (minRow < 0) minRow = 0;
 1361  
 
 1362  8
         maxColumn = (int)Math.ceil(
 1363  
                 (clip.getMaxX() - area.getMinX()) / state.getBlockWidth()
 1364  
                 );
 1365  8
         if (maxColumn > visibleColumns) maxColumn = visibleColumns;
 1366  
 
 1367  8
         maxRow = (int)Math.ceil(
 1368  
                 (clip.getMaxY() - area.getMinY()) / state.getBlockHeight()
 1369  
                 );
 1370  8
         if (maxRow > visibleRows) maxRow = visibleRows;
 1371  
 
 1372  30
         for (row=minRow; row < maxRow; row ++) {
 1373  
 
 1374  96
             for (column=minColumn; column < maxColumn; column ++) {
 1375  
 
 1376  74
                 drawBlock (g2,area,state,row,column);
 1377  74
                 count++;
 1378  
 
 1379  
             }
 1380  
 
 1381  
         }
 1382  
 
 1383  8
     }
 1384  
 
 1385  
     /**
 1386  
      * Draws the plot.
 1387  
      *
 1388  
      * @param g2  the graphics context.
 1389  
      * @param area  the area.
 1390  
      * @param anchor  the anchor.
 1391  
      * @param parentState   the parentstate.
 1392  
      * @param info  the info.
 1393  
      */
 1394  
     public void draw(
 1395  
             Graphics2D g2,
 1396  
             Rectangle2D area,
 1397  
             Point2D anchor,
 1398  
             PlotState parentState,
 1399  
             PlotRenderingInfo info) {
 1400  
 
 1401  
         // Most of this method is just calculating exactly where to draw
 1402  
         // what. The names of the variables should be descriptive
 1403  
         // enough for anybody to follow.
 1404  13
         int visibleColumns = this.getHeatMapColumnsCount();
 1405  13
         int visibleRows = this.getHeatMapRowCount();
 1406  13
         int totalColumns = this.dataset.getHeatMap().getColumnsCount();
 1407  13
         int totalRows = this.dataset.getHeatMap().getRowCount();
 1408  
 
 1409  13
         int topMargin = (int)(area.getHeight() * this.topMarginSize);
 1410  12
         int bottomMargin = (int)(area.getHeight() * this.bottomMarginSize);
 1411  12
         int leftMargin = (int)(area.getWidth() * this.leftMarginSize);
 1412  12
         int rightMargin = (int)(area.getWidth() * this.rightMarginSize);
 1413  12
         int minX = (int)area.getMinX()+leftMargin;
 1414  12
         int minY = (int)area.getMinY()+topMargin;
 1415  12
         int width = (int)area.getWidth()-leftMargin-rightMargin;
 1416  12
         int height = (int)area.getHeight()-bottomMargin-topMargin;
 1417  12
         int columnTreeHeight = (int)(height *this.columnTreeSize);
 1418  12
         int rowTreeWidth = (int)(width *this.rowTreeSize);
 1419  12
         if (!this.rowTreeVisibility) rowTreeWidth = 0;
 1420  12
         if (!this.columnTreeVisibility) columnTreeHeight = 0;
 1421  12
         int columnNamesInitialHeight = (int)(height * this.columnNamesSize);
 1422  12
         int rowNamesInitialWidth = (int)(width * this.rowNamesSize);
 1423  12
         int heatMapWidth = (int)((double)
 1424  
             ( area.getWidth() - rowTreeWidth - rowNamesInitialWidth)
 1425  
             * visibleColumns / totalColumns);
 1426  12
         int heatMapHeight = (int)((double)
 1427  
             ( area.getHeight() - columnTreeHeight - columnNamesInitialHeight)
 1428  
             * visibleRows / totalRows);
 1429  12
         int columnNamesHeight = height - heatMapHeight - columnTreeHeight;
 1430  12
         int rowNamesWidth = width - heatMapWidth - rowTreeWidth;
 1431  12
         if (!this.rowNamesVisibility) rowNamesWidth = 0;
 1432  12
         if (!this.columnNamesVisibility) columnNamesHeight = 0;
 1433  12
         int maxX = minX+width;
 1434  12
         int maxY = minY+height;
 1435  
         int heatMapMinX;
 1436  
         int heatMapMinY;
 1437  
         int heatMapMaxX;
 1438  
         int heatMapMaxY;
 1439  
         RectangleEdge columnNamesLocation;
 1440  
         RectangleEdge rowNamesLocation;
 1441  
         RectangleEdge columnTreeLocation;
 1442  
         RectangleEdge rowTreeLocation;
 1443  
 
 1444  
         Rectangle columnTreeArea;
 1445  
         Rectangle rowTreeArea;
 1446  
         Rectangle columnNamesArea;
 1447  
         Rectangle rowNamesArea;
 1448  
         Rectangle heatMapArea;
 1449  
 
 1450  12
         if (drawFromLeftToRight) {
 1451  
             
 1452  12
             heatMapMinX = minX+rowTreeWidth;
 1453  12
             rowTreeLocation = RectangleEdge.LEFT;
 1454  12
             rowNamesLocation = RectangleEdge.RIGHT;
 1455  
 
 1456  12
         } else {
 1457  
             
 1458  
         // Currently (2006-08-16), this block is unreachable.
 1459  0
             heatMapMinX = minX+rowNamesWidth;
 1460  0
             rowTreeLocation = RectangleEdge.RIGHT;
 1461  0
             rowNamesLocation = RectangleEdge.LEFT;
 1462  
 
 1463  
         }
 1464  
 
 1465  12
         if (drawFromTopToBottom) {
 1466  
             
 1467  12
             heatMapMinY = minY+columnTreeHeight;
 1468  12
             columnTreeLocation = RectangleEdge.TOP;
 1469  12
             columnNamesLocation = RectangleEdge.BOTTOM;
 1470  
 
 1471  12
         } else {
 1472  
             
 1473  
         // Currently (2006-08-16), this block is unreachable.
 1474  0
             heatMapMinY = minY+columnNamesHeight;
 1475  0
             columnTreeLocation = RectangleEdge.BOTTOM;
 1476  0
             columnNamesLocation = RectangleEdge.TOP;
 1477  
 
 1478  
         }
 1479  
 
 1480  12
         heatMapMaxX = heatMapMinX + heatMapWidth;
 1481  12
         heatMapMaxY = heatMapMinY + heatMapHeight;
 1482  
 
 1483  12
         if (drawFromTopToBottom) {
 1484  
             
 1485  12
             columnTreeArea = new Rectangle(
 1486  
                 heatMapMinX, minY, heatMapWidth, columnTreeHeight
 1487  
             );
 1488  12
             columnNamesArea = new Rectangle(
 1489  
                 heatMapMinX, heatMapMaxY, heatMapWidth, maxY-heatMapMaxY
 1490  
             );
 1491  
 
 1492  12
         } else {
 1493  
             
 1494  
         // Currently (2006-08-16), this block is unreachable.
 1495  0
             columnTreeArea = new Rectangle(
 1496  
                 heatMapMinX, heatMapMaxY, heatMapWidth, columnTreeHeight
 1497  
             );
 1498  0
             columnNamesArea = new Rectangle(
 1499  
                 heatMapMinX, minY, heatMapWidth, maxY-heatMapMaxY
 1500  
             );
 1501  
 
 1502  
         }
 1503  
 
 1504  12
         if (drawFromLeftToRight) {
 1505  
 
 1506  12
             rowTreeArea = new Rectangle(
 1507  
                 minX, heatMapMinY, rowTreeWidth, heatMapHeight
 1508  
             );
 1509  12
             rowNamesArea = new Rectangle(
 1510  
                 heatMapMaxX, heatMapMinY, maxX-heatMapMaxX, heatMapHeight
 1511  
             );
 1512  
             
 1513  12
         } else {
 1514  
             
 1515  
         // Currently (2006-08-16), this block is unreachable.
 1516  0
             rowTreeArea = new Rectangle(
 1517  
                 heatMapMaxX, heatMapMinY, rowTreeWidth, heatMapHeight
 1518  
             );
 1519  0
             rowNamesArea = new Rectangle(
 1520  
                 minX, heatMapMinY, maxX-heatMapMaxX, heatMapHeight
 1521  
             );
 1522  
 
 1523  
         }
 1524  
 
 1525  12
         heatMapArea = new Rectangle(
 1526  
             heatMapMinX, heatMapMinY, heatMapWidth, heatMapHeight
 1527  
         );
 1528  
 
 1529  
         int sizeOfNodeSymbol;
 1530  
         double getColumnTreeHeightUnitInPixels;
 1531  
         double getRowTreeHeightUnitInPixels;
 1532  12
         double blockWidth = columnTreeArea.getWidth()/visibleColumns;
 1533  12
         double blockHeight = rowTreeArea.getHeight()/visibleRows;
 1534  
 
 1535  12
         if (blockWidth < blockHeight)
 1536  11
             sizeOfNodeSymbol = (int)(3 + blockWidth/5);
 1537  
         else 
 1538  1
             sizeOfNodeSymbol = (int)(3 + blockHeight/5);
 1539  
 
 1540  12
         if (this.dataset.getColumnClusteringTree() != null)
 1541  11
             getColumnTreeHeightUnitInPixels = columnTreeArea.getHeight() /
 1542  
                 this.dataset.getColumnClusteringTree().getHeight();
 1543  
         else
 1544  1
             getColumnTreeHeightUnitInPixels = 0;
 1545  
 
 1546  12
         if (this.dataset.getRowClusteringTree() != null)
 1547  11
             getRowTreeHeightUnitInPixels = rowTreeArea.getWidth() /
 1548  
                 this.dataset.getRowClusteringTree().getHeight();
 1549  
         else
 1550  1
             getRowTreeHeightUnitInPixels = 0;
 1551  
 
 1552  12
         HCPlotState state = new HCPlotState(
 1553  
             info,
 1554  
             (int)(heatMapArea.getMinX()),
 1555  
             (int)(heatMapArea.getMinY()),
 1556  
             sizeOfNodeSymbol,
 1557  
             blockWidth,
 1558  
             blockHeight,
 1559  
             getColumnTreeHeightUnitInPixels,
 1560  
             getRowTreeHeightUnitInPixels);
 1561  
 
 1562  12
         if (info != null) {
 1563  
 
 1564  11
             info.setPlotArea(area);
 1565  11
             info.setDataArea(heatMapArea);
 1566  
 
 1567  
         }
 1568  
 
 1569  
         // ok, the main geometry is calculated now. We begin
 1570  
         // drawing here.
 1571  
 
 1572  12
         drawBackground(g2,area);
 1573  
 
 1574  11
         drawHeatMap(g2,state,heatMapArea,visibleColumns,visibleRows);
 1575  
 
 1576  8
         if ( (getColumnTreeVisibility ()) &&
 1577  
             (g2.getClip().intersects(columnTreeArea))) {
 1578  
 
 1579  7
             drawSubTree(
 1580  
                 g2,
 1581  
                 state,
 1582  
                 this.columnClusteringInfo.getRootNode(),
 1583  
                 columnTreeArea,
 1584  
                 columnTreeLocation
 1585  
             );
 1586  
 
 1587  
         }
 1588  8
         if ( (getRowTreeVisibility ()) &&
 1589  
             (g2.getClip().intersects(rowTreeArea))) {
 1590  
 
 1591  7
             drawSubTree(
 1592  
                 g2,
 1593  
                 state,
 1594  
                 this.rowClusteringInfo.getRootNode(),
 1595  
                 rowTreeArea,
 1596  
                 rowTreeLocation
 1597  
             );
 1598  
 
 1599  
         }
 1600  8
         if (getRowNamesVisibility ()) {
 1601  
 
 1602  8
             this.rowNames.draw(
 1603  
                 g2,
 1604  
                 RectangleEdge.coordinate(heatMapArea,rowNamesLocation),
 1605  
                 area,
 1606  
                 rowNamesArea,
 1607  
                 rowNamesLocation,
 1608  
                 info
 1609  
 
 1610  
             );
 1611  
 
 1612  
         }
 1613  8
         if (getColumnNamesVisibility ()) {
 1614  
 
 1615  8
             this.columnNames.draw(
 1616  
                 g2,
 1617  
                 RectangleEdge.coordinate(heatMapArea,columnNamesLocation),
 1618  
                 area,
 1619  
                 columnNamesArea,
 1620  
                 columnNamesLocation,
 1621  
                 info
 1622  
             );
 1623  
         }
 1624  
 
 1625  8
         if ( !closedRows.isEmpty() ) {
 1626  
 
 1627  1
            int rowcount = closedRows.size();
 1628  1
            Integer item = null;
 1629  1
            Color color = new Color(0,0,0);
 1630  
 
 1631  1
            int i=0;
 1632  2
            while (i < rowcount) {
 1633  1
                i++;
 1634  1
                item = (Integer)closedRows.first();
 1635  1
                closedRows.remove(item);
 1636  1
                Rectangle r = new Rectangle(
 1637  
                          0,
 1638  
                          item.intValue(),
 1639  
                          visibleColumns,
 1640  
                          1
 1641  
                );
 1642  1
                drawSelection(g2, state, color, r);
 1643  1
            }
 1644  
         }
 1645  
 
 1646  8
         if ( !closedColumns.isEmpty() ) {
 1647  
 
 1648  3
            int columncount = closedColumns.size();
 1649  3
            Integer item = null;
 1650  3
            Color color = new Color(0,0,0);
 1651  
 
 1652  3
            int i=0;
 1653  6
            while (i < columncount) {
 1654  3
                i++;
 1655  3
                item = (Integer)closedColumns.first();
 1656  3
                closedColumns.remove(item);
 1657  3
                Rectangle r = new Rectangle(
 1658  
                          item.intValue(),
 1659  
                          0,
 1660  
                          1,
 1661  
                          visibleRows
 1662  
                );
 1663  3
                drawSelection(g2, state, color, r);
 1664  3
            }
 1665  
         }
 1666  
 
 1667  8
         if ( this.selection != null  && getSelectionHighlight()) {
 1668  3
             Color color = new Color(255,255,255);
 1669  3
             drawSelection(g2, state, color, this.selection);
 1670  
         }
 1671  
 
 1672  8
     }
 1673  
 
 1674  
     /**
 1675  
      * Implements the ChartMouseListener interface.
 1676  
      *
 1677  
      * @param event  the mouse event.
 1678  
      */
 1679  
     public void chartMouseMoved(ChartMouseEvent event) {
 1680  
 
 1681  3
         if ((this.mouseOverHighlight) && (event.getEntity() != null)) {
 1682  
 
 1683  2
             if (event.getEntity() instanceof HeatMapBlockEntity) {
 1684  
 
 1685  1
                 HeatMapBlockEntity entity =
 1686  
                     (HeatMapBlockEntity)event.getEntity();
 1687  1
                 int oldRow = getRowTreeHighlight ();
 1688  1
                 int oldColumn = getColumnTreeHighlight ();
 1689  
 
 1690  1
                 if ((oldRow != entity.getRow())
 1691  
                     || (oldColumn != entity.getColumn())) {
 1692  
 
 1693  1
                     setRowTreeHighlight (entity.getRow());
 1694  1
                     setColumnTreeHighlight (entity.getColumn());
 1695  
 
 1696  
                     //redrawHighlightedNodes();
 1697  
 
 1698  
                 }
 1699  
 
 1700  1
             } else if (event.getEntity() instanceof HCTreeNodeEntity) {
 1701  
 
 1702  
                 ;
 1703  
 
 1704  
             }
 1705  
 
 1706  
         }
 1707  
 
 1708  3
     }
 1709  
 
 1710  
     /**
 1711  
      * Implements the ChartMouseListener interface.
 1712  
      *
 1713  
      * @param event  the mouse event.
 1714  
      */
 1715  
     public void chartMouseClicked(ChartMouseEvent event) {
 1716  
 
 1717  13
         MouseEvent e = event.getTrigger();
 1718  13
         if (event.getEntity() != null) {
 1719  
 
 1720  10
             if (event.getEntity() instanceof HeatMapBlockEntity &&
 1721  
 
 1722  
                 getSelectionHighlight()) {
 1723  
 
 1724  3
                 HeatMapBlockEntity entity =
 1725  
                     (HeatMapBlockEntity)event.getEntity();
 1726  
 
 1727  3
                 Rectangle selectionCandidate = new Rectangle (
 1728  
                     entity.getColumn(),
 1729  
                     entity.getRow(),
 1730  
                     1,
 1731  
                     1
 1732  
                 );
 1733  3
                 if (selectionCandidate.equals(this.selection)) {
 1734  
 
 1735  1
                     this.selection=null;
 1736  
 
 1737  1
                 } else {
 1738  
 
 1739  2
                     setSelection ( selectionCandidate );
 1740  
 
 1741  
                 }
 1742  
 
 1743  3
             } else if (event.getEntity() instanceof HCTreeNodeEntity) {
 1744  
 
 1745  7
                     HCTreeNodeEntity entity =
 1746  
                     (HCTreeNodeEntity)event.getEntity();
 1747  7
                 HCTreeNodeInfo node = entity.getHCTreeNodeInfo();
 1748  7
                 boolean alt = event.getTrigger().isAltDown();
 1749  7
                 boolean shift = event.getTrigger().isShiftDown();
 1750  7
                 boolean control = event.getTrigger().isControlDown();
 1751  7
                 if (shift) {
 1752  
 
 1753  2
                     node.setSubTreeOpen(!node.isNodeOpen());
 1754  
 
 1755  2
                 } else if (!control) {
 1756  
 
 1757  3
                     this.selection = null;
 1758  3
                     node.setNodeOpen(!node.isNodeOpen());
 1759  
 
 1760  3
                 } else if (control) {
 1761  
 
 1762  2
                     handleClick(node);
 1763  
 
 1764  
                 }
 1765  
 
 1766  
             }
 1767  
             
 1768  
 
 1769  
         }
 1770  
 
 1771  13
     }
 1772  
     
 1773  
 
 1774  
     /**
 1775  
      * Handels the click.
 1776  
      *
 1777  
      * @param node  the node that is clicked.
 1778  
      */
 1779  
     private void handleClick(HCTreeNodeInfo node) {
 1780  
 
 1781  2
         int treelocation = node.getClusteringInfo().getLocation();
 1782  2
         int leftbound=0;
 1783  2
         int rightbound=0;
 1784  
         Rectangle r;
 1785  
 
 1786  
         try {
 1787  2
             leftbound = node.getVisibleDataRange().getLeftBound();
 1788  2
             rightbound = node.getVisibleDataRange().getRightBound();
 1789  0
         } catch (Exception e){
 1790  0
             return;
 1791  2
         }
 1792  
 
 1793  2
         if (treelocation == this.LEFT) {
 1794  1
             r = new Rectangle(
 1795  
                 0,
 1796  
                 leftbound,
 1797  
                 this.getHeatMapColumnsCount(),
 1798  
                 rightbound-leftbound+1
 1799  
             );
 1800  1
         }
 1801  1
         else if (treelocation == this.TOP) {
 1802  1
             r = new Rectangle(
 1803  
                 leftbound,
 1804  
                 0,
 1805  
                 rightbound-leftbound+1,
 1806  
                 this.getHeatMapRowCount()
 1807  
 
 1808  
 
 1809  
             );
 1810  
 
 1811  1
         }
 1812  0
         else r=null; // Currently (2006-08-21), this block is unreachable.
 1813  
 
 1814  2
         if (this.selection != null && this.selection.equals(r))
 1815  1
             this.selection = null;
 1816  
         else
 1817  1
             this.setSelection(r);
 1818  
 
 1819  
 
 1820  2
     }
 1821  
 
 1822  
 
 1823  
     /**
 1824  
      * Implements the ChangeListener interface.
 1825  
      *
 1826  
      * @param event  the state change event.
 1827  
      */
 1828  
     public void stateChanged(ChangeEvent event) {
 1829  
  
 1830  8
         Object source = event.getSource();
 1831  7
         if (source instanceof HCTreeNodeInfo) {
 1832  
 
 1833  
             // TODO: shouldn't the received event already be a
 1834  
             // ClusteringTreeChangeEvent in the first place?
 1835  7
             HCTreeNodeInfo info = (HCTreeNodeInfo)(event.getSource());
 1836  7
             this.notifyListeners(new ClusteringTreeChangeEvent (this, info));
 1837  
 
 1838  
         }
 1839  
 
 1840  7
     }
 1841  
 
 1842  
     /**
 1843  
      * Returns the tooltip generator of a plot.
 1844  
      *
 1845  
      * @return  The tooltip generator.
 1846  
      */
 1847  
     public HCToolTipGenerator getToolTipGenerator() {
 1848  
 
 1849  2
         return this.toolTipGenerator;
 1850  
 
 1851  
     }
 1852  
 
 1853  
     /**
 1854  
      * Sets the tooltip generator of a plot.
 1855  
      *
 1856  
      * @param toolTipGenerator  the tooltip generator.
 1857  
      */
 1858  
     public void setToolTipGenerator(HCToolTipGenerator toolTipGenerator) {
 1859  
 
 1860  4
         this.toolTipGenerator = toolTipGenerator;
 1861  
 
 1862  4
     }
 1863  
 
 1864  
     /**
 1865  
      * Creates a new JPanel for adjusting visualisation settings.
 1866  
      *
 1867  
      * @return  The panel.
 1868  
      */
 1869  
     public JPanel getOptionsPanel() {
 1870  
 
 1871  1
         return this.optionsEditor.getPanel();
 1872  
 
 1873  
     }
 1874  
 
 1875  
     /**
 1876  
      * Creates a new JPanel for adjusting heatmap colors.
 1877  
      *
 1878  
      * @return  The panel.
 1879  
      */
 1880  
     public JPanel getPalettePanel() {
 1881  
 
 1882  1
         return this.paletteEditor.getPanel();
 1883  
 
 1884  
     }
 1885  
 
 1886  
     /**
 1887  
      * Returns a name for the visualisation options panel.
 1888  
      *
 1889  
      * @return  Panel name as a string.
 1890  
      */
 1891  
     public String getOptionsPanelName() {
 1892  
 
 1893  1
         ResourceBundle lr = ResourceBundle.getBundle("org.jfree.chart.editor.LocalizationBundle");
 1894  1
         return lr.getString("HC_options_panel");
 1895  
 
 1896  
     }
 1897  
     
 1898  
     /**
 1899  
      * Returns a name for the heatmap coloring panel.
 1900  
      *
 1901  
      * @return  Panel name as a string.
 1902  
      */
 1903  
     public String getPalettePanelName() {
 1904  
 
 1905  1
         ResourceBundle lr = ResourceBundle.getBundle("org.jfree.chart.editor.LocalizationBundle");
 1906  1
         return lr.getString("HC_palette_panel");
 1907  
 
 1908  
     }
 1909  
 
 1910  
 }
 1911