1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * 
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * The contents of this file are subject to the terms of either the Universal Permissive License
   7  * v 1.0 as shown at http://oss.oracle.com/licenses/upl
   8  *
   9  * or the following license:
  10  *
  11  * Redistribution and use in source and binary forms, with or without modification, are permitted
  12  * provided that the following conditions are met:
  13  * 
  14  * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
  15  * and the following disclaimer.
  16  * 
  17  * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
  18  * conditions and the following disclaimer in the documentation and/or other materials provided with
  19  * the distribution.
  20  * 
  21  * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
  22  * endorse or promote products derived from this software without specific prior written permission.
  23  * 
  24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  26  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
  31  * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 package org.openjdk.jmc.ui.column;
  34 
  35 import java.util.ArrayList;
  36 import java.util.Arrays;
  37 import java.util.Comparator;
  38 import java.util.LinkedHashMap;
  39 import java.util.List;
  40 import java.util.Map;
  41 import java.util.function.Consumer;
  42 import java.util.stream.Collectors;
  43 import java.util.stream.Stream;
  44 
  45 import org.eclipse.jface.preference.JFacePreferences;
  46 import org.eclipse.jface.resource.JFaceResources;
  47 import org.eclipse.jface.viewers.ColumnLabelProvider;
  48 import org.eclipse.jface.viewers.ColumnViewer;
  49 import org.eclipse.jface.viewers.TableViewer;
  50 import org.eclipse.jface.viewers.TableViewerColumn;
  51 import org.eclipse.jface.viewers.TreeViewer;
  52 import org.eclipse.jface.viewers.TreeViewerColumn;
  53 import org.eclipse.jface.viewers.Viewer;
  54 import org.eclipse.jface.viewers.ViewerColumn;
  55 import org.eclipse.jface.viewers.ViewerComparator;
  56 import org.eclipse.swt.SWT;
  57 import org.eclipse.swt.SWTException;
  58 import org.eclipse.swt.events.ControlEvent;
  59 import org.eclipse.swt.events.ControlListener;
  60 import org.eclipse.swt.events.DisposeEvent;
  61 import org.eclipse.swt.events.DisposeListener;
  62 import org.eclipse.swt.graphics.Color;
  63 import org.eclipse.swt.graphics.Font;
  64 import org.eclipse.swt.widgets.Event;
  65 import org.eclipse.swt.widgets.Item;
  66 import org.eclipse.swt.widgets.Listener;
  67 import org.eclipse.swt.widgets.Table;
  68 import org.eclipse.swt.widgets.Tree;
  69 import org.eclipse.swt.widgets.Widget;
  70 
  71 import org.openjdk.jmc.ui.column.TableSettings.ColumnSettings;
  72 
  73 public class ColumnManager {
  74 
  75         public interface IColumnState {
  76                 IColumn getColumn();
  77 
  78                 Integer getWidth();
  79 
  80                 Boolean isHidden();
  81 
  82                 Boolean isSortAscending();
  83 
  84                 boolean isVisible();
  85         }
  86 
  87         public static class SelectionState {
  88                 private int scrollIndex;
  89                 private int[] selectionIndices;
  90 
  91                 private SelectionState(int scrollIndex, int[] selectionIndices) {
  92                         this.scrollIndex = scrollIndex;
  93                         this.selectionIndices = selectionIndices;
  94                 }
  95 
  96                 private int[] getSelectionIndices() {
  97                         return selectionIndices;
  98                 }
  99 
 100                 private int getScrollIndex() {
 101                         return scrollIndex;
 102                 }
 103 
 104         }
 105 
 106         private static final int DEFAULT_WIDTH = 150;
 107         private static final boolean DEFAULT_HIDDEN = false;
 108         private static final boolean DEFAULT_SORT_ASC = true;
 109 
 110         private final List<ColumnEntry> addedColumns;
 111         private final ColumnViewer viewer;
 112         private ColumnEntry sortColumn;
 113 
 114         private final Listener customDrawer = new Listener() {
 115 
 116                 @Override
 117                 public void handleEvent(Event event) {
 118                         Item[] columnWidgets = getColumnWidgets();
 119                         if (event.index < columnWidgets.length) {
 120                                 Widget colWidget = columnWidgets[event.index];
 121                                 for (ColumnEntry ce : addedColumns) {
 122                                         if (ce.ui != null && colWidget == getColumnWidget(ce.ui)) {
 123                                                 Listener drawer;
 124                                                 if ((drawer = ce.getColumn().getColumnDrawer()) != null) {
 125                                                         drawer.handleEvent(event);
 126                                                 }
 127                                         }
 128                                 }
 129                         }
 130                 }
 131         };
 132         private final Consumer<ColumnComparator> onSortChange;
 133 
 134         static class ColumnEntry implements ControlListener, IColumnState {
 135                 private final IColumn impl;
 136                 private Boolean hidden;
 137                 private Integer width;
 138                 private Boolean sortAscending;
 139                 private ViewerColumn ui;
 140 
 141                 public ColumnEntry(IColumn columnImpl, Boolean hidden, Integer width, Boolean sortAscending) {
 142                         impl = columnImpl;
 143                         this.hidden = hidden;
 144                         this.width = width;
 145                         this.sortAscending = sortAscending;
 146                 }
 147 
 148                 @Override
 149                 public IColumn getColumn() {
 150                         return impl;
 151                 }
 152 
 153                 @Override
 154                 public Integer getWidth() {
 155                         return width;
 156                 }
 157 
 158                 @Override
 159                 public Boolean isHidden() {
 160                         return hidden;
 161                 }
 162 
 163                 @Override
 164                 public boolean isVisible() {
 165                         return ui != null;
 166                 }
 167 
 168                 @Override
 169                 public Boolean isSortAscending() {
 170                         return sortAscending;
 171                 }
 172 
 173                 boolean isSortAscendingPreferred() {
 174                         return sortAscending == null ? DEFAULT_SORT_ASC : sortAscending;
 175                 }
 176 
 177                 Item create(ColumnViewer viewer, int columnIndex) {
 178                         Item columnWidget;
 179                         int colWidth = width == null ? DEFAULT_WIDTH : width;
 180                         if (viewer instanceof TableViewer) {
 181                                 TableViewerColumn vc = new TableViewerColumn((TableViewer) viewer, getColumn().getStyle(), columnIndex);
 182                                 ui = vc; // set before usage since viewerColumn is used to implement isVisible()
 183                                 vc.getColumn().setMoveable(true);
 184                                 vc.getColumn().addControlListener(this);
 185                                 vc.getColumn().setToolTipText(getColumn().getDescription());
 186                                 vc.getColumn().setWidth(colWidth);
 187                                 columnWidget = vc.getColumn();
 188 
 189                         } else {
 190                                 TreeViewerColumn vc = new TreeViewerColumn(((TreeViewer) viewer), getColumn().getStyle(), columnIndex);
 191                                 ui = vc; // set before usage since viewerColumn is used to implement isVisible()
 192                                 vc.getColumn().setMoveable(true);
 193                                 vc.getColumn().addControlListener(this);
 194                                 vc.getColumn().setToolTipText(getColumn().getDescription());
 195                                 vc.getColumn().setWidth(colWidth);
 196                                 columnWidget = vc.getColumn();
 197                         }
 198                         ui.setEditingSupport(getColumn().getEditingSupport());
 199                         ui.setLabelProvider(getColumn().getLabelProvider());
 200                         columnWidget.setText(getColumn().getName());
 201                         return columnWidget;
 202                 }
 203 
 204                 private void doHide() {
 205                         ViewerColumn vc = ui;
 206                         ui = null; // clear before dispose since viewerColumn is used to implement isVisible()
 207                         if (vc instanceof TableViewerColumn) {
 208                                 TableViewerColumn tc = ((TableViewerColumn) vc);
 209                                 ColumnViewer viewer = vc.getViewer();
 210                                 try {
 211                                         // Workaround. disable redraw, or TableViewer will throw exception when removing the last column
 212                                         viewer.getControl().setRedraw(false);
 213                                         tc.getColumn().dispose();
 214                                 } finally {
 215                                         try {
 216                                                 viewer.getControl().setRedraw(true);
 217                                         } catch (SWTException e) {
 218                                                 // Workaround. for some reason the table sometimes complains about the table is
 219                                                 // disposed even though it seem to be working perfectly.
 220                                         }
 221                                 }
 222                         } else if (vc != null) {
 223                                 ((TreeViewerColumn) vc).getColumn().dispose();
 224                         }
 225                 }
 226 
 227                 @Override
 228                 public void controlMoved(ControlEvent e) {
 229 
 230                 }
 231 
 232                 @Override
 233                 public void controlResized(ControlEvent e) {
 234                         width = getColumnWidth(ui);
 235                         // FIXME: Workaround to avoid drawing dug on windows when having hooked EraseItem listener and the first tree column draws a custom background or is right aligned
 236                         ui.getViewer().getControl().redraw();
 237                 }
 238 
 239         }
 240 
 241         private static Boolean getNullForDefault(boolean hidden) {
 242                 return hidden == DEFAULT_HIDDEN ? null : hidden;
 243         }
 244 
 245         public static ColumnManager build(TableViewer viewer, List<IColumn> columns, TableSettings ts) {
 246                 return build((ColumnViewer) viewer, columns, ts, viewer::setComparator);
 247         }
 248 
 249         public static ColumnManager build(
 250                 TableViewer viewer, List<IColumn> columns, TableSettings ts, Consumer<ColumnComparator> onSortChange) {
 251                 return build((ColumnViewer) viewer, columns, ts, onSortChange);
 252         }
 253 
 254         public static ColumnManager build(TreeViewer viewer, List<IColumn> columns, TableSettings ts) {
 255                 return build((ColumnViewer) viewer, columns, ts, viewer::setComparator);
 256         }
 257 
 258         public static ColumnManager build(
 259                 TreeViewer viewer, List<IColumn> columns, TableSettings ts, Consumer<ColumnComparator> onSortChange) {
 260                 return build((ColumnViewer) viewer, columns, ts, onSortChange);
 261         }
 262 
 263         private static ColumnManager build(
 264                 ColumnViewer viewer, List<IColumn> columns, TableSettings ts, Consumer<ColumnComparator> onSortChange) {
 265                 List<ColumnEntry> entries = new ArrayList<>();
 266                 String sortColumn = null;
 267                 if (ts != null) {
 268                         Map<String, IColumn> columnsMap = new LinkedHashMap<>(); // preserve order for columns with no settings
 269                         for (IColumn c : columns) {
 270                                 columnsMap.put(c.getId(), c);
 271                         }
 272                         if (ts.getOrderBy() != null && columnsMap.containsKey(ts.getOrderBy())) {
 273                                 sortColumn = ts.getOrderBy();
 274                         }
 275                         // Add settings in order
 276                         for (ColumnSettings cc : ts.getColumns()) {
 277                                 IColumn c = columnsMap.remove(cc.getId());
 278                                 if (c != null) {
 279                                         entries.add(new ColumnEntry(c, cc.isHidden(), cc.getWidth(), cc.isSortAscending()));
 280                                 }
 281                         }
 282                         // Add columns with no settings as hidden
 283                         for (IColumn c : columnsMap.values()) {
 284                                 entries.add(new ColumnEntry(c, getNullForDefault(true), null, null));
 285                         }
 286                 } else {
 287                         // No settings, add all columns as visible
 288                         for (IColumn c : columns) {
 289                                 entries.add(new ColumnEntry(c, getNullForDefault(false), null, null));
 290                         }
 291                 }
 292                 return new ColumnManager(viewer, sortColumn, entries, onSortChange);
 293         }
 294 
 295         ColumnManager(ColumnViewer viewer, String sortColumnId, List<ColumnEntry> columns,
 296                         Consumer<ColumnComparator> onSortChange) {
 297                 this.viewer = viewer;
 298                 addedColumns = columns;
 299                 this.onSortChange = onSortChange;
 300                 setLinesVisible(true);
 301                 setHeaderVisible(true);
 302                 viewer.setLabelProvider(new ColumnLabelProvider() {
 303                         @Override
 304                         public String getText(Object element) {
 305                                 return Messages.ALL_COLUMNS_HIDDEN_LABEL;
 306                         }
 307 
 308                         @Override
 309                         public Font getFont(Object element) {
 310                                 return JFaceResources.getFontRegistry().getItalic(JFaceResources.DEFAULT_FONT);
 311                         }
 312 
 313                         @Override
 314                         public Color getForeground(Object element) {
 315                                 return JFaceResources.getColorRegistry().get(JFacePreferences.QUALIFIER_COLOR);
 316                         }
 317                 });
 318                 viewer.getControl().addDisposeListener(new DisposeListener() {
 319 
 320                         @Override
 321                         public void widgetDisposed(DisposeEvent e) {
 322                                 updateColumnOrder();
 323                         }
 324                 });
 325                 for (ColumnEntry ce : addedColumns) {
 326                         if (ce.hidden == null ? !DEFAULT_HIDDEN : !ce.hidden) {
 327                                 createColumnUi(ce);
 328                         }
 329                 }
 330                 updateEraseItemListener();
 331                 if (sortColumnId != null) {
 332                         setSortColumn(sortColumnId);
 333                 }
 334         }
 335 
 336         private void updateEraseItemListener() {
 337                 for (ColumnEntry c : addedColumns) {
 338                         if (c.isVisible() && c.getColumn().getColumnDrawer() != null) {
 339                                 // Custom drawer found, ensure the the listener is added
 340                                 for (Listener l : viewer.getControl().getListeners(SWT.EraseItem)) {
 341                                         if (l == customDrawer) {
 342                                                 return;
 343                                         }
 344                                 }
 345                                 viewer.getControl().addListener(SWT.EraseItem, customDrawer);
 346                                 return;
 347                         }
 348                 }
 349 
 350                 // No custom drawer found, ensure the the listener is not added
 351                 viewer.getControl().removeListener(SWT.EraseItem, customDrawer);
 352         }
 353 
 354         public ColumnViewer getViewer() {
 355                 return viewer;
 356         }
 357 
 358         public void setColumnHidden(String columnId, boolean hidden) {
 359                 updateColumnOrder();
 360                 ColumnEntry columnEntry = getColumnEntry(columnId);
 361                 columnEntry.hidden = hidden;
 362                 if (hidden) {
 363                         columnEntry.doHide();
 364                         updateEraseItemListener();
 365                 } else {
 366                         createColumnUi(columnEntry);
 367                         updateEraseItemListener();
 368                         getViewer().refresh(); // Need to populate the added column
 369                 }
 370                 getViewer().getControl().getParent().layout();
 371         }
 372 
 373         private void createColumnUi(ColumnEntry columnEntry) {
 374                 Item columnWidget = columnEntry.create(viewer, countVisibleColumnsBefore(columnEntry));
 375                 columnWidget.addListener(SWT.Selection, e -> changeOrFlipSortColumn(columnEntry));
 376         }
 377 
 378         private int countVisibleColumnsBefore(ColumnEntry columnEntry) {
 379                 int i = 0;
 380                 for (ColumnEntry c : addedColumns) {
 381                         if (c == columnEntry) {
 382                                 return i;
 383                         } else if (c.isVisible()) {
 384                                 i++;
 385                         }
 386                 }
 387                 return i;
 388         }
 389 
 390         private void updateColumnOrder() {
 391                 if (viewer instanceof TableViewer) {
 392                         Table table = ((TableViewer) viewer).getTable();
 393                         updateColumnOrder(table.getColumns(), table.getColumnOrder());
 394                 } else {
 395                         Tree tree = ((TreeViewer) viewer).getTree();
 396                         updateColumnOrder(tree.getColumns(), tree.getColumnOrder());
 397                 }
 398         }
 399 
 400         private void updateColumnOrder(Widget[] columns, int[] order) {
 401                 int visibleIndex = 0;
 402                 for (int i = 0; i < addedColumns.size(); i++) {
 403                         ColumnEntry e = addedColumns.get(i);
 404                         if (e.isVisible()) {
 405                                 Widget v1 = getColumnWidget(e.ui);
 406                                 Widget v2 = columns[order[visibleIndex++]];
 407                                 if (v2 != v1) {
 408                                         // swap position for v1 and v2;
 409                                         for (int j = i; j < addedColumns.size(); j++) {
 410                                                 ColumnEntry e2 = addedColumns.get(j);
 411                                                 if (e2.ui != null && v2 == getColumnWidget(e2.ui)) {
 412                                                         addedColumns.set(i, e2);
 413                                                         addedColumns.set(j, e);
 414 
 415                                                 }
 416                                         }
 417                                 }
 418                         }
 419                 }
 420         }
 421 
 422         /**
 423          * Change the current sort column. If this column is already the sort column, then switch sort
 424          * order.
 425          *
 426          * @param entry
 427          *            Column to sort on. May not be null.
 428          */
 429         private void changeOrFlipSortColumn(ColumnEntry entry) {
 430                 if (sortColumn == entry) {
 431                         setSortColumn(entry, !entry.isSortAscendingPreferred());
 432                 } else {
 433                         setSortColumn(entry, entry.isSortAscendingPreferred());
 434                 }
 435         }
 436 
 437         public void clearSortColumn() {
 438                 sortColumn = null;
 439                 setSortColumn(null, SWT.UP);
 440         }
 441 
 442         public void setSortColumn(String columnId) {
 443                 ColumnEntry columnEntry = getColumnEntry(columnId);
 444                 setSortColumn(columnEntry, columnEntry.isSortAscendingPreferred());
 445         }
 446 
 447         public void setSortColumn(String columnId, boolean sortAscending) {
 448                 setSortColumn(getColumnEntry(columnId), sortAscending);
 449         }
 450 
 451         public ColumnComparator getColumnComparator() {
 452                 return sortColumn == null ? null : new ColumnComparator(sortColumn.impl, sortColumn.isSortAscendingPreferred());
 453         }
 454 
 455         private void setSortColumn(ColumnEntry entry, boolean sortAscending) {
 456                 sortColumn = entry;
 457                 entry.sortAscending = sortAscending;
 458                 setSortColumn(entry.ui, sortAscending ? SWT.UP : SWT.DOWN);
 459         }
 460 
 461         private ColumnEntry getColumnEntry(String columnId) {
 462                 return addedColumns.stream().filter(ce -> ce.impl.getId().equals(columnId)).findAny().get();
 463         }
 464 
 465         private void setSortColumn(ViewerColumn vc, int direction) {
 466                 if (viewer instanceof TableViewer) {
 467                         Table table = ((TableViewer) viewer).getTable();
 468                         table.setSortColumn(vc == null ? null : ((TableViewerColumn) vc).getColumn());
 469                         table.setSortDirection(direction);
 470                 } else {
 471                         Tree tree = ((TreeViewer) viewer).getTree();
 472                         tree.setSortColumn(vc == null ? null : ((TreeViewerColumn) vc).getColumn());
 473                         tree.setSortDirection(direction);
 474                 }
 475                 onSortChange.accept(getColumnComparator());
 476         }
 477 
 478         private static Item getColumnWidget(ViewerColumn vc) {
 479                 if (vc instanceof TableViewerColumn) {
 480                         return ((TableViewerColumn) vc).getColumn();
 481                 } else {
 482                         return ((TreeViewerColumn) vc).getColumn();
 483                 }
 484         }
 485 
 486         private Item[] getColumnWidgets() {
 487                 if (viewer instanceof TableViewer) {
 488                         return ((TableViewer) viewer).getTable().getColumns();
 489                 } else {
 490                         return ((TreeViewer) viewer).getTree().getColumns();
 491                 }
 492         }
 493 
 494         private static int getColumnWidth(ViewerColumn vc) {
 495                 if (vc instanceof TableViewerColumn) {
 496                         return ((TableViewerColumn) vc).getColumn().getWidth();
 497                 } else {
 498                         return ((TreeViewerColumn) vc).getColumn().getWidth();
 499                 }
 500         }
 501 
 502         private void setHeaderVisible(boolean visible) {
 503                 if (viewer instanceof TableViewer) {
 504                         ((TableViewer) viewer).getTable().setHeaderVisible(visible);
 505                 } else {
 506                         ((TreeViewer) viewer).getTree().setHeaderVisible(visible);
 507                 }
 508         }
 509 
 510         private void setLinesVisible(boolean visible) {
 511                 if (viewer instanceof TableViewer) {
 512                         ((TableViewer) viewer).getTable().setLinesVisible(visible);
 513                 } else {
 514                         ((TreeViewer) viewer).getTree().setLinesVisible(visible);
 515                 }
 516         }
 517 
 518         public Stream<? extends IColumnState> getColumnStates() {
 519                 if (!viewer.getControl().isDisposed()) {
 520                         updateColumnOrder();
 521                 }
 522                 return addedColumns.stream();
 523         }
 524 
 525         public TableSettings getSettings() {
 526                 List<ColumnSettings> cols = getColumnStates().map(ColumnManager::buildColumnConfig)
 527                                 .collect(Collectors.toList());
 528                 return new TableSettings(sortColumn == null ? null : sortColumn.getColumn().getId(), cols);
 529         }
 530 
 531         private static ColumnSettings buildColumnConfig(IColumnState state) {
 532                 return new ColumnSettings(state.getColumn().getId(), state.isHidden(), state.getWidth(),
 533                                 state.isSortAscending());
 534         }
 535 
 536         public static class ColumnComparator extends ViewerComparator implements Comparator<Object> {
 537 
 538                 private final IColumn column;
 539                 private final boolean sortAscending;
 540 
 541                 private ColumnComparator(IColumn column, boolean sortAscending) {
 542                         this.column = column;
 543                         this.sortAscending = sortAscending;
 544                 }
 545 
 546                 @Override
 547                 public int compare(Viewer viewer, Object e1, Object e2) {
 548                         return compare(e1, e2);
 549                 }
 550 
 551                 @Override
 552                 public int compare(Object row1, Object row2) {
 553                         int compare = 0;
 554                         Comparator<Object> comparator = column.getComparator();
 555                         if (comparator != null) {
 556                                 compare = comparator.compare(row1, row2);
 557                         } else {
 558                                 ColumnLabelProvider clp = column.getLabelProvider();
 559                                 String l1 = clp.getText(row1);
 560                                 String l2 = clp.getText(row2);
 561                                 compare = l1 == null ? (l2 == null ? 0 : -1) : (l2 == null ? 1 : l1.compareTo(l2));
 562                         }
 563                         return sortAscending ? compare : -compare;
 564                 }
 565 
 566                 public IColumn getColumn() {
 567                         return column;
 568                 }
 569 
 570                 public boolean isSortAscending() {
 571                         return sortAscending;
 572                 }
 573         }
 574 
 575         public int getVisibilityIndex(IColumn column) {
 576                 for (ColumnEntry c : addedColumns) {
 577                         if (c.impl == column) {
 578                                 if (c.ui != null) {
 579                                         return Arrays.asList(getColumnWidgets()).indexOf(getColumnWidget(c.ui));
 580                                 }
 581                                 break;
 582                         }
 583                 }
 584                 return -1;
 585         }
 586 
 587         public SelectionState getSelectionState() {
 588                 Table table = (Table) getViewer().getControl();
 589                 return new SelectionState(table.getTopIndex(), table.getSelectionIndices());
 590         }
 591 
 592         public void setSelectionState(SelectionState state) {
 593                 if (state == null) {
 594                         return;
 595                 }
 596                 Table table = (Table) getViewer().getControl();
 597                 table.setSelection(state.getSelectionIndices());
 598                 // Workaround to fire selection events for listeners of the TableViewer
 599                 getViewer().setSelection(getViewer().getSelection());
 600                 table.setTopIndex(state.getScrollIndex());
 601         }
 602 
 603 }