blob: 77a9c2c76001392f132e1305afc063d171c8ba4d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013-2016 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Jacques Bouthillier - Initial Implementation of the table view
******************************************************************************/
package org.eclipse.egerrit.internal.dashboard.ui.model;
import java.util.Arrays;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.egerrit.internal.dashboard.ui.GerritUi;
import org.eclipse.egerrit.internal.dashboard.ui.commands.table.AdjustMyStarredHandler;
import org.eclipse.egerrit.internal.dashboard.ui.utils.UIUtils;
import org.eclipse.egerrit.internal.dashboard.ui.views.GerritTableView;
import org.eclipse.egerrit.internal.model.ChangeInfo;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider.ColorProvider;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.ToolTip;
import org.eclipse.ui.PlatformUI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class handles the creation of the table widget shown in the dashboard.
*/
public class UIReviewTable {
private static final String DASHBOARD_CONTEXT_MENU = "org.eclipse.egerrit.dashboard.contextMenu"; //$NON-NLS-1$
private static final String EGERRIT_DASHBOARD = "egerrit.dashboard"; //$NON-NLS-1$
private static final String VIEW_COLUMN_ORDER = "egerritViewColumnOrder"; //$NON-NLS-1$
private static final String VIEW_COLUMN_WIDTH = "egerritViewColumnWidth"; //$NON-NLS-1$
private static Logger logger = LoggerFactory.getLogger(UIReviewTable.class);
private static final int TABLE_STYLE = SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION;
private TableViewer fViewer;
private ComposedAdapterFactory adapterFactory;
private ColorProvider labelProvider;
private AdapterFactoryContentProvider contentProvider;
private int defaultColumn = ReviewTableDefinition.values().length;
private String[] voteColumns;
public Composite createTableViewerSection(Composite aParent, String[] voteColumns) {
this.voteColumns = voteColumns;
Composite viewerForm = new Composite(aParent, SWT.BORDER | SWT.SHADOW_ETCHED_IN);
viewerForm.setLayout(new FillLayout());
// Create the table viewer to maintain the list of reviews
fViewer = new TableViewer(viewerForm, TABLE_STYLE);
fViewer = buildAndLayoutTable(fViewer);
fViewer.getTable().addDisposeListener(e -> storeColumnsSettings());
// Add a Key event and mouse down listener
fViewer.getTable().addListener(SWT.MouseDown, mouseButtonListener);
adapterFactory = new ComposedAdapterFactory();
adapterFactory.addAdapterFactory(new ModifiedModelItemProviderAdapterFactory(voteColumns));
contentProvider = new AdapterFactoryContentProvider(adapterFactory);
fViewer.setContentProvider(contentProvider);
if (voteColumns != null) {
setLabelProvider(voteColumns);
}
return viewerForm;
}
/**
* Create the label provider after we defined the dynamic columns
*
* @param voteColumns
*/
private void setLabelProvider(String[] voteColumns) {
for (String column : voteColumns) {
createTableViewerLabelColumn(getAcronymLabel(column));
}
ReviewTableSorter.bind(fViewer);
fViewer.setComparator(new ReviewTableSorter(7)); // sort by Updated, descending
labelProvider = new AdapterFactoryLabelProvider.ColorProvider(adapterFactory, null, null);
fViewer.setLabelProvider(labelProvider);
restoreColumnsSettings();
}
private String getAcronymLabel(String label) {
String ret = ""; //$NON-NLS-1$
if (!label.isEmpty()) {
String[] arraySt = label.split("-"); //$NON-NLS-1$
for (String s : arraySt) {
ret = ret.concat(s.substring(0, 1));
}
}
return ret;
}
/**
* Create each column for the List of Reviews
*
* @param aParent
* @param aViewer
*/
private TableViewer buildAndLayoutTable(final TableViewer aViewer) {
final Table table = aViewer.getTable();
//Get the review table definition
ReviewTableDefinition[] tableInfo = ReviewTableDefinition.values();
int size = tableInfo.length;
logger.debug("Table Name Width Resize Moveable"); //$NON-NLS-1$
for (int index = 0; index < size; index++) {
logger.debug("index [ " + index + " ] " + tableInfo[index].getName() + "\t: " //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+ tableInfo[index].getWidth() + "\t: " + tableInfo[index].getResize() + "\t: " //$NON-NLS-1$ //$NON-NLS-2$
+ tableInfo[index].getMoveable());
createTableViewerColumn(tableInfo[index]);
}
TableLayout tableLayout = new TableLayout();
table.setLayout(tableLayout);
handleTooltipNotOnWindow(aViewer, table);
table.setHeaderVisible(true);
table.setLinesVisible(true);
MenuManager menuManager = new MenuManager();
Menu contextMenu = menuManager.createContextMenu(table);
table.setMenu(contextMenu);
GerritTableView.getActiveView(true).getSite().registerContextMenu(DASHBOARD_CONTEXT_MENU, menuManager, aViewer);
return aViewer;
}
/**
* Handle the tooltip on a the dashboard table when the operating system is not a window environment
*
* @param aViewer
* @param table
*/
private void handleTooltipNotOnWindow(final TableViewer aViewer, final Table table) {
String os = org.eclipse.core.runtime.Platform.getOS();
// nothing to do on windows
if (!Platform.OS_WIN32.equals(os)) {
final Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
final ToolTip tip = new ToolTip(shell, SWT.ICON_INFORMATION);
table.addListener(SWT.MouseMove, event -> tip.setVisible(false));
table.addListener(SWT.MouseHover, event -> {
ViewerCell viewerCell = UIUtils.getAdjustedViewerCell(event, aViewer);
if (viewerCell != null) {
String message = ""; //$NON-NLS-1$
int columnIndex = viewerCell.getColumnIndex();
message = adjustSubjectTooltip(viewerCell, columnIndex);
TableItem item = (TableItem) viewerCell.getViewerRow().getItem();
Rectangle rect = item.getBounds(columnIndex);
if (message.isEmpty()) {
message = item.getText(columnIndex);
}
tip.setMessage(message);
Point displayPos = item.getParent().toDisplay(rect.x, rect.y);
tip.setLocation(displayPos.x, displayPos.y);
tip.setVisible(true);
}
});
}
}
/**
* Use the commit message when hovering the subject column.
*
* @param viewerCell
* @param columnIndex
* @return String
*/
private String adjustSubjectTooltip(ViewerCell viewerCell, int columnIndex) {
String message = ""; //$NON-NLS-1$
if (columnIndex == ReviewTableDefinition.SUBJECT.ordinal()) {
Object obj = viewerCell.getElement();
if (obj instanceof ChangeInfo) {
ChangeInfo changeInfo = (ChangeInfo) obj;
message = changeInfo.getRevision() != null ? changeInfo.getRevision().getCommit().getMessage() : ""; //$NON-NLS-1$
}
}
return message;
}
/**
* Create each column in the review table list
*
* @param ReviewTableDefinition
* @return TableViewerColumn
*/
private TableViewerColumn createTableViewerColumn(ReviewTableDefinition aTableInfo) {
final TableViewerColumn viewerColumn = new TableViewerColumn(fViewer, SWT.NONE);
final TableColumn column = viewerColumn.getColumn();
column.setText(aTableInfo.getName());
column.setWidth(aTableInfo.getWidth());
column.setAlignment(aTableInfo.getAlignment());
column.setResizable(aTableInfo.getResize());
column.setMoveable(aTableInfo.getMoveable());
return viewerColumn;
}
private final Listener mouseButtonListener = aEvent -> {
logger.debug("mouseButtonListener() for " + aEvent.button); //$NON-NLS-1$
switch (aEvent.type) {
case SWT.MouseDown:
// Left Click, Selection over the STAR column only will activate the request to modify it
if (aEvent.button == 1) {
Point p = new Point(aEvent.x, aEvent.y);
ViewerCell viewerCell = fViewer.getCell(p);
if (viewerCell != null && viewerCell.getColumnIndex() == ReviewTableDefinition.STARRED.ordinal()) {
// Execute the command to adjust the column: ID with the
// starred information
AdjustMyStarredHandler handler = new AdjustMyStarredHandler();
try {
handler.execute(new ExecutionEvent());
} catch (ExecutionException excutionException) {
logger.error(excutionException.getMessage());
}
}
}
// For now, button 2 not used
if (aEvent.button == 2) {
}
// Right Click
if (aEvent.button == 3) {
// Process the Item table handling
// processItemSelection()
}
break;
default:
break;
}
};
public TableViewer getViewer() {
return fViewer;
}
public void dispose() {
if (labelProvider != null) {
labelProvider.dispose();
}
if (contentProvider != null) {
contentProvider.dispose();
}
if (adapterFactory != null) {
adapterFactory.dispose();
}
}
private void storeColumnsSettings() {
if (fViewer == null) {
return;
}
int[] columnOrder = fViewer.getTable().getColumnOrder();
//Use only the store when there is more than the default columns
//This is to allow the dynamic column position
if (columnOrder.length == ReviewTableDefinition.values().length) {
return;
}
int colInTable = fViewer.getTable().getColumnCount();
int[] columnWidth = new int[colInTable];
for (int i = 0; i < colInTable; i++) {
columnWidth[i] = fViewer.getTable().getColumn(i).getWidth();
}
getDialogSettings().put(VIEW_COLUMN_ORDER,
Arrays.stream(columnOrder).mapToObj(i -> String.valueOf(i)).toArray(String[]::new));
getDialogSettings().put(VIEW_COLUMN_WIDTH,
Arrays.stream(columnWidth).mapToObj(i -> String.valueOf(i)).toArray(String[]::new));
}
private void restoreColumnsSettings() {
if (fViewer == null) {
return;
}
String[] backedUpValue = getDialogSettings().getArray(VIEW_COLUMN_ORDER);
if (backedUpValue == null) {
return;
}
int[] columnOrderStored = Arrays.stream(backedUpValue).mapToInt(Integer::parseInt).toArray();
int columInTable = fViewer.getTable().getColumnCount();
//Keep default column position until we have data to display in the dashboard
if (columInTable == ReviewTableDefinition.values().length) {
return;
}
//Keep an array of the last saved value concerning the columns width in the dashboard
int[] oldvalue = new int[columInTable];
for (int i = 0; i < columInTable; i++) {
int width = fViewer.getTable().getColumn(i).getWidth();
oldvalue[i] = width;
}
//Lets deal with the columns order
//Initialize variables
int lastStoredColumnSize = columnOrderStored.length;
int[] nextTableColumn = new int[columInTable];
if (columInTable < columnOrderStored.length) {
//More stored columns than what we need now, let reduce the array
System.arraycopy(columnOrderStored, 0, nextTableColumn, 0, columInTable);
} else {
//Table have more columns than the storage
System.arraycopy(columnOrderStored, 0, nextTableColumn, 0, columnOrderStored.length);
//Get the extra column
//Initialize the columns order when there are more columns in the table than what has been stored previously
for (int i = lastStoredColumnSize; i < nextTableColumn.length; i++) {
nextTableColumn[i] = i; //init the new dynamic columns at the end of the table
}
}
//Validate the column , it should be between 0 to number of columns
int nextColumSize = nextTableColumn.length;
int numberColumnToMove = 0;
for (int i = 0; i < nextColumSize; i++) {
if (nextTableColumn[i] >= nextColumSize) {
numberColumnToMove++;
//Move by one slot the array
System.arraycopy(columnOrderStored, i + numberColumnToMove, nextTableColumn, i, nextColumSize - i);
//need to maintain the counter for next loop if the new number has to move as well
i--;
}
}
fViewer.getTable().setColumnOrder(nextTableColumn);
restoreColumnWidth(columnOrderStored, columInTable, oldvalue, lastStoredColumnSize - 1, nextTableColumn); //decrease by one to read the array
}
/**
* @param columnOrderStored
* @param columInTable
* @param oldvalue
* @param lastStoredColumnSize
* @param nextTableColumn
*/
private void restoreColumnWidth(int[] columnOrderStored, int columInTable, int[] oldvalue, int lastStoredColumnSize,
int[] nextTableColumn) {
String[] backedUpValue;
//Lets deal now with the columns width
backedUpValue = getDialogSettings().getArray(VIEW_COLUMN_WIDTH);
if (backedUpValue == null) {
return;
}
int[] columnWidth = Arrays.stream(backedUpValue).mapToInt(Integer::parseInt).toArray();
int totalSize = 0;
if (columInTable < columnWidth.length) {
//More store column than what we need now, let reduce the array
System.arraycopy(columnWidth, 0, nextTableColumn, 0, columInTable);
} else {
//Table have more columns than the storage
System.arraycopy(columnWidth, 0, nextTableColumn, 0, columnWidth.length);
//Add the extra columns width from the table data
for (int i = columnWidth.length; i < nextTableColumn.length; i++) {
nextTableColumn[i] = oldvalue[i];
}
}
for (int i = 0; i < nextTableColumn.length; i++) {
totalSize = totalSize + nextTableColumn[i];
fViewer.getTable().getColumn(i).setWidth(nextTableColumn[i]);
}
//Reset if total size is small number (50 or less )pixels.
if (totalSize <= 50) {
resetDefault();
}
//Read and adjust the last column defined before the dynamic labels
//Needs to be reset to allow to see the dynamic columns in the client area
if (lastStoredColumnSize < fViewer.getTable().getColumnCount()) {
int defaultWidth = oldvalue[lastStoredColumnSize];//column before the new labels
int testCol = columnOrderStored[lastStoredColumnSize];
if (testCol < ReviewTableDefinition.values().length) {
defaultWidth = ReviewTableDefinition.values()[testCol].getWidth();//column before the dynamic labels
}
fViewer.getTable().getColumn(testCol).setWidth(defaultWidth);
}
}
/**
* Reset the column width and order, then save it for the next time
*/
public void resetDefault() {
Table table = fViewer.getTable();
ReviewTableDefinition[] tableInfo = ReviewTableDefinition.values();
int defaultLength = tableInfo.length;
int colInTable = table.getColumnCount();
int[] newOrder = new int[colInTable];
//Reset the column order
for (int i = 0; i < colInTable; i++) {
newOrder[i] = i;
}
table.setColumnOrder(newOrder);//Re-set the initial column order, plus the LABELS at the end
//Use the default length defined for the table without the dynamic columns
for (int i = 0; i < defaultLength; i++) {
table.getColumn(i).setWidth(tableInfo[i].getWidth());
}
//For the dynamic columns, defined the minimum value
for (int i = defaultLength; i < colInTable; i++) {
table.getColumn(i).pack();
}
storeColumnsSettings();
}
private IDialogSettings getDialogSettings() {
IDialogSettings settings = GerritUi.getDefault().getDialogSettings();
IDialogSettings section = settings.getSection(EGERRIT_DASHBOARD);
if (section == null) {
section = settings.addNewSection(EGERRIT_DASHBOARD);
}
return section;
}
/**
* Create each column in the review table list
*
* @param ReviewTableDefinition
* @return TableViewerColumn
*/
private void createTableViewerLabelColumn(String label) {
int width = 28 + 5 * label.length();
final TableViewerColumn viewerColumn = new TableViewerColumn(fViewer, SWT.NONE);
final TableColumn column = viewerColumn.getColumn();
column.setText(label);
column.setWidth(width);
column.setAlignment(SWT.LEFT);
column.setResizable(true);
column.setMoveable(true);
}
/**
* Return the name for a dynamic column
*
* @param column
* @return
*/
public String getColumnLabel(int column) {
int val = column - defaultColumn;//Adjust the dynamic column value
if (val >= 0) {
return voteColumns[val];
}
return ""; //$NON-NLS-1$
}
}