| /******************************************************************************* |
| * Copyright (c) 2011-2016 EclipseSource Muenchen GmbH and others. |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Mathias Schaefer - initial API and implementation |
| ******************************************************************************/ |
| /******************************************************************************* |
| * Copyright (c) 2006 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * chris.gross@us.ibm.com - initial API and implementation |
| * Chuck.Mastrandrea@sas.com - wordwrapping in bug 222280 |
| * smcduff@hotmail.com - wordwrapping in bug 222280 |
| * Claes Rosell<claes.rosell@solme.se> - rowspan in bug 272384 |
| *******************************************************************************/ |
| package org.eclipse.emf.ecp.view.internal.table.nebula.grid; |
| |
| import org.eclipse.nebula.widgets.grid.Grid; |
| import org.eclipse.nebula.widgets.grid.GridCellRenderer; |
| import org.eclipse.nebula.widgets.grid.GridColumn; |
| import org.eclipse.nebula.widgets.grid.GridItem; |
| import org.eclipse.nebula.widgets.grid.IInternalWidget; |
| import org.eclipse.nebula.widgets.grid.internal.BranchRenderer; |
| import org.eclipse.nebula.widgets.grid.internal.CheckBoxRenderer; |
| import org.eclipse.nebula.widgets.grid.internal.TextUtils; |
| import org.eclipse.nebula.widgets.grid.internal.ToggleRenderer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.graphics.TextLayout; |
| |
| // REUSED CLASS |
| /** |
| * Copy of the Nebula Grid DefaultCellRenderer (temporary fix to be able to set the selection colors) |
| * |
| */ |
| @SuppressWarnings({ "restriction", "nls" }) |
| public class CustomSelectionColorCellRenderer extends GridCellRenderer { |
| |
| int leftMargin = 4; |
| |
| int rightMargin = 4; |
| |
| int topMargin = 0; |
| |
| int bottomMargin = 0; |
| |
| int textTopMargin = 1; |
| |
| int textBottomMargin = 2; |
| |
| private final int insideMargin = 3; |
| |
| int treeIndent = 20; |
| |
| private ToggleRenderer toggleRenderer; |
| |
| private BranchRenderer branchRenderer; |
| |
| private CheckBoxRenderer checkRenderer; |
| |
| private TextLayout textLayout; |
| |
| private Color selectionBackgroundColor; |
| private Color selectionForegroundColor; |
| |
| // TODO: fix disabled/out of focus state |
| public CustomSelectionColorCellRenderer(Color selectionForegroundColor, Color selectionBackgroundColor) { |
| this.selectionForegroundColor = selectionForegroundColor; |
| this.selectionBackgroundColor = selectionBackgroundColor; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void paint(GC gc, Object value) { |
| final GridItem item = (GridItem) value; |
| |
| gc.setFont(item.getFont(getColumn())); |
| |
| boolean drawAsSelected = isSelected(); |
| |
| boolean drawBackground = true; |
| |
| if (isCellSelected()) { |
| drawAsSelected = true;// (!isCellFocus()); |
| } |
| |
| if (drawAsSelected) { |
| gc.setBackground(selectionBackgroundColor); |
| gc.setForeground(selectionForegroundColor); |
| } else { |
| if (item.getParent().isEnabled()) { |
| final Color back = item.getBackground(getColumn()); |
| |
| if (back != null) { |
| gc.setBackground(back); |
| } else { |
| drawBackground = false; |
| } |
| } else { |
| gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); |
| } |
| gc.setForeground(item.getForeground(getColumn())); |
| } |
| |
| if (drawBackground) { |
| gc.fillRectangle(getBounds().x, getBounds().y, getBounds().width, |
| getBounds().height); |
| } |
| |
| int x = leftMargin; |
| |
| if (isTree()) { |
| final boolean renderBranches = item.getParent().getTreeLinesVisible(); |
| if (renderBranches) { |
| branchRenderer.setBranches(getBranches(item)); |
| branchRenderer.setIndent(treeIndent); |
| branchRenderer.setBounds( |
| getBounds().x + x, getBounds().y, |
| getToggleIndent(item), getBounds().height + 1); // Take into account border |
| } |
| |
| x += getToggleIndent(item); |
| |
| toggleRenderer.setExpanded(item.isExpanded()); |
| |
| toggleRenderer.setHover(getHoverDetail().equals("toggle")); |
| |
| toggleRenderer.setLocation(getBounds().x + x, (getBounds().height - toggleRenderer |
| .getBounds().height) |
| / 2 + getBounds().y); |
| if (item.hasChildren()) { |
| toggleRenderer.paint(gc, null); |
| } |
| |
| if (renderBranches) { |
| branchRenderer.setToggleBounds(toggleRenderer.getBounds()); |
| branchRenderer.paint(gc, null); |
| } |
| |
| x += toggleRenderer.getBounds().width + insideMargin; |
| |
| } |
| |
| if (isCheck()) { |
| checkRenderer.setChecked(item.getChecked(getColumn())); |
| checkRenderer.setGrayed(item.getGrayed(getColumn())); |
| if (!item.getParent().isEnabled()) { |
| checkRenderer.setGrayed(true); |
| } |
| checkRenderer.setHover(getHoverDetail().equals("check")); |
| |
| if (isCenteredCheckBoxOnly(item)) { |
| // Special logic if this column only has a checkbox and is centered |
| checkRenderer.setBounds(getBounds().x + (getBounds().width - checkRenderer.getBounds().width) / 2, |
| (getBounds().height - checkRenderer.getBounds().height) |
| / 2 + getBounds().y, |
| checkRenderer |
| .getBounds().width, |
| checkRenderer.getBounds().height); |
| } else { |
| checkRenderer.setBounds(getBounds().x + x, (getBounds().height - checkRenderer |
| .getBounds().height) |
| / 2 + getBounds().y, |
| checkRenderer |
| .getBounds().width, |
| checkRenderer.getBounds().height); |
| |
| x += checkRenderer.getBounds().width + insideMargin; |
| } |
| |
| checkRenderer.paint(gc, null); |
| } |
| |
| final Image image = item.getImage(getColumn()); |
| if (image != null) { |
| int y = getBounds().y; |
| |
| y += (getBounds().height - image.getBounds().height) / 2; |
| |
| gc.drawImage(image, getBounds().x + x, y); |
| |
| x += image.getBounds().width + insideMargin; |
| } |
| |
| final int width = getBounds().width - x - rightMargin; |
| |
| if (drawAsSelected) { |
| gc.setForeground(selectionForegroundColor); |
| } else { |
| gc.setForeground(item.getForeground(getColumn())); |
| } |
| |
| if (!isWordWrap()) { |
| final String text = TextUtils.getShortString(gc, item.getText(getColumn()), width); |
| |
| if (getAlignment() == SWT.RIGHT) { |
| final int len = gc.stringExtent(text).x; |
| if (len < width) { |
| x += width - len; |
| } |
| } else if (getAlignment() == SWT.CENTER) { |
| final int len = gc.stringExtent(text).x; |
| if (len < width) { |
| x += (width - len) / 2; |
| } |
| } |
| |
| gc.drawString(text, getBounds().x + x, getBounds().y + textTopMargin + topMargin, true); |
| } else { |
| if (textLayout == null) { |
| textLayout = new TextLayout(gc.getDevice()); |
| item.getParent().addDisposeListener(new DisposeListener() { |
| @Override |
| public void widgetDisposed(DisposeEvent e) { |
| textLayout.dispose(); |
| } |
| }); |
| } |
| textLayout.setFont(gc.getFont()); |
| textLayout.setText(item.getText(getColumn())); |
| textLayout.setAlignment(getAlignment()); |
| textLayout.setWidth(width < 1 ? 1 : width); |
| if (item.getParent().isAutoHeight()) { |
| // Look through all columns (except this one) to get the max height needed for this item |
| final int columnCount = item.getParent().getColumnCount(); |
| int maxHeight = textLayout.getBounds().height + textTopMargin + textBottomMargin; |
| for (int i = 0; i < columnCount; i++) { |
| final GridColumn column = item.getParent().getColumn(i); |
| if (i != getColumn() && column.getWordWrap()) { |
| final int height = column.getCellRenderer().computeSize(gc, column.getWidth(), SWT.DEFAULT, |
| item).y; |
| maxHeight = Math.max(maxHeight, height); |
| } |
| } |
| |
| // Also look at the row header if necessary |
| if (item.getParent().isWordWrapHeader()) { |
| final int height = item.getParent().getRowHeaderRenderer().computeSize(gc, SWT.DEFAULT, SWT.DEFAULT, |
| item).y; |
| maxHeight = Math.max(maxHeight, height); |
| } |
| |
| if (maxHeight != item.getHeight()) { |
| item.setHeight(maxHeight); |
| } |
| } |
| textLayout.draw(gc, getBounds().x + x, getBounds().y + textTopMargin + topMargin); |
| } |
| |
| if (item.getParent().getLinesVisible()) { |
| if (isCellSelected()) { |
| gc.setForeground(selectionForegroundColor); |
| } else { |
| gc.setForeground(item.getParent().getLineColor()); |
| } |
| gc.drawLine(getBounds().x, getBounds().y + getBounds().height, getBounds().x |
| + getBounds().width - 1, |
| getBounds().y + getBounds().height); |
| gc.drawLine(getBounds().x + getBounds().width - 1, getBounds().y, |
| getBounds().x + getBounds().width - 1, getBounds().y + getBounds().height); |
| } |
| |
| if (isCellFocus()) { |
| final Rectangle focusRect = new Rectangle(getBounds().x, getBounds().y, getBounds().width - 1, |
| getBounds().height); |
| |
| gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND)); |
| gc.drawRectangle(focusRect); |
| |
| if (isFocus()) { |
| focusRect.x++; |
| focusRect.width -= 2; |
| focusRect.y++; |
| focusRect.height -= 2; |
| |
| gc.drawRectangle(focusRect); |
| } |
| } |
| } |
| |
| /** |
| * Calculates the sequence of branch lines which should be rendered for the provided item |
| * |
| * @param item |
| * @return an array of integers composed using the constants in {@link BranchRenderer} |
| */ |
| private int[] getBranches(GridItem item) { |
| final int[] branches = new int[item.getLevel() + 1]; |
| final GridItem[] roots = item.getParent().getRootItems(); |
| |
| // Is this a node or a leaf? |
| if (item.getParentItem() == null) { |
| // Add descender if not last item |
| if (!item.isExpanded() && roots[roots.length - 1].equals(item)) { |
| if (item.hasChildren()) { |
| branches[item.getLevel()] = BranchRenderer.LAST_ROOT; |
| } else { |
| branches[item.getLevel()] = BranchRenderer.SMALL_L; |
| } |
| } else { |
| if (item.hasChildren()) { |
| branches[item.getLevel()] = BranchRenderer.ROOT; |
| } else { |
| branches[item.getLevel()] = BranchRenderer.SMALL_T; |
| } |
| } |
| |
| } else if (item.hasChildren()) { |
| if (item.isExpanded()) { |
| branches[item.getLevel()] = BranchRenderer.NODE; |
| } else { |
| branches[item.getLevel()] = BranchRenderer.NONE; |
| } |
| } else { |
| branches[item.getLevel()] = BranchRenderer.LEAF; |
| } |
| |
| // Branch for current item |
| GridItem parent = item.getParentItem(); |
| if (parent == null) { |
| return branches; |
| } |
| |
| // Are there siblings below this item? |
| if (parent.indexOf(item) < parent.getItemCount() - 1) { |
| branches[item.getLevel() - 1] = BranchRenderer.T; |
| } else if (parent.getParentItem() == null && !parent.equals(roots[roots.length - 1])) { |
| branches[item.getLevel() - 1] = BranchRenderer.T; |
| } else { |
| branches[item.getLevel() - 1] = BranchRenderer.L; |
| } |
| |
| final Grid grid = item.getParent(); |
| item = parent; |
| parent = item.getParentItem(); |
| |
| // Branches for parent items |
| while (item.getLevel() > 0) { |
| if (parent.indexOf(item) == parent.getItemCount() - 1) { |
| if (parent.getParentItem() == null && !grid.getRootItem(grid.getRootItemCount() - 1).equals(parent)) { |
| branches[item.getLevel() - 1] = BranchRenderer.I; |
| } else { |
| branches[item.getLevel() - 1] = BranchRenderer.NONE; |
| } |
| } else { |
| branches[item.getLevel() - 1] = BranchRenderer.I; |
| } |
| item = parent; |
| parent = item.getParentItem(); |
| } |
| // item should be null at this point |
| return branches; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Point computeSize(GC gc, int wHint, int hHint, Object value) { |
| final GridItem item = (GridItem) value; |
| |
| gc.setFont(item.getFont(getColumn())); |
| |
| int x = 0; |
| |
| x += leftMargin; |
| |
| if (isTree()) { |
| x += getToggleIndent(item); |
| |
| x += toggleRenderer.getBounds().width + insideMargin; |
| |
| } |
| |
| if (isCheck()) { |
| x += checkRenderer.getBounds().width + insideMargin; |
| } |
| |
| int y = 0; |
| |
| final Image image = item.getImage(getColumn()); |
| if (image != null) { |
| y = topMargin + image.getBounds().height + bottomMargin; |
| |
| x += image.getBounds().width + insideMargin; |
| } |
| |
| // MOPR-DND |
| // MOPR: replaced this code (to get correct preferred height for cells in word-wrap columns) |
| // |
| // x += gc.stringExtent(item.getText(column)).x + rightMargin; |
| // |
| // y = Math.max(y,topMargin + gc.getFontMetrics().getHeight() + bottomMargin); |
| // |
| // with this code: |
| |
| int textHeight = 0; |
| if (!isWordWrap()) { |
| x += gc.textExtent(item.getText(getColumn())).x + rightMargin; |
| |
| textHeight = topMargin + textTopMargin + gc.getFontMetrics().getHeight() + textBottomMargin + bottomMargin; |
| } else { |
| int plainTextWidth; |
| if (wHint == SWT.DEFAULT) { |
| plainTextWidth = gc.textExtent(item.getText(getColumn())).x; |
| } else { |
| plainTextWidth = wHint - x - rightMargin; |
| } |
| |
| final TextLayout currTextLayout = new TextLayout(gc.getDevice()); |
| currTextLayout.setFont(gc.getFont()); |
| currTextLayout.setText(item.getText(getColumn())); |
| currTextLayout.setAlignment(getAlignment()); |
| currTextLayout.setWidth(plainTextWidth < 1 ? 1 : plainTextWidth); |
| |
| x += plainTextWidth + rightMargin; |
| |
| textHeight += topMargin + textTopMargin; |
| for (int cnt = 0; cnt < currTextLayout.getLineCount(); cnt++) { |
| textHeight += currTextLayout.getLineBounds(cnt).height; |
| } |
| textHeight += textBottomMargin + bottomMargin; |
| |
| currTextLayout.dispose(); |
| } |
| |
| y = Math.max(y, textHeight); |
| |
| return new Point(x, y); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean notify(int event, Point point, Object value) { |
| |
| final GridItem item = (GridItem) value; |
| |
| if (isCheck()) { |
| if (event == IInternalWidget.MouseMove) { |
| if (overCheck(item, point)) { |
| setHoverDetail("check"); |
| return true; |
| } |
| } |
| |
| if (event == IInternalWidget.LeftMouseButtonDown) { |
| if (overCheck(item, point)) { |
| if (!item.getCheckable(getColumn())) { |
| return false; |
| } |
| |
| item.setChecked(getColumn(), !item.getChecked(getColumn())); |
| item.getParent().redraw(); |
| |
| item.fireCheckEvent(getColumn()); |
| |
| return true; |
| } |
| } |
| } |
| |
| if (isTree() && item.hasChildren()) { |
| if (event == IInternalWidget.MouseMove) { |
| if (overToggle(item, point)) { |
| setHoverDetail("toggle"); |
| return true; |
| } |
| } |
| |
| if (event == IInternalWidget.LeftMouseButtonDown) { |
| if (overToggle(item, point)) { |
| item.setExpanded(!item.isExpanded()); |
| item.getParent().redraw(); |
| |
| if (item.isExpanded()) { |
| item.fireEvent(SWT.Expand); |
| } else { |
| item.fireEvent(SWT.Collapse); |
| } |
| |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| private boolean overCheck(GridItem item, Point point) { |
| if (isCenteredCheckBoxOnly(item)) { |
| point = new Point(point.x, point.y); |
| point.x -= getBounds().x; |
| point.y -= getBounds().y; |
| |
| final Rectangle checkBounds = new Rectangle(0, 0, 0, 0); |
| checkBounds.x = (getBounds().width - checkRenderer.getBounds().width) / 2; |
| checkBounds.y = (getBounds().height - checkRenderer.getBounds().height) / 2; |
| checkBounds.width = checkRenderer.getBounds().width; |
| checkBounds.height = checkRenderer.getBounds().height; |
| |
| return checkBounds.contains(point); |
| } |
| point = new Point(point.x, point.y); |
| point.x -= getBounds().x; |
| point.y -= getBounds().y; |
| |
| int x = leftMargin; |
| if (isTree()) { |
| x += getToggleIndent(item); |
| x += toggleRenderer.getSize().x + insideMargin; |
| } |
| |
| if (point.x >= x && point.x < x + checkRenderer.getSize().x) { |
| final int yStart = (getBounds().height - checkRenderer.getBounds().height) / 2; |
| if (point.y >= yStart && point.y < yStart + checkRenderer.getSize().y) { |
| return true; |
| } |
| } |
| |
| return false; |
| |
| } |
| |
| private int getToggleIndent(GridItem item) { |
| return item.getLevel() * treeIndent; |
| } |
| |
| private boolean overToggle(GridItem item, Point point) { |
| |
| point = new Point(point.x, point.y); |
| |
| point.x -= getBounds().x - 1; |
| point.y -= getBounds().y - 1; |
| |
| int x = leftMargin; |
| x += getToggleIndent(item); |
| |
| if (point.x >= x && point.x < x + toggleRenderer.getSize().x) { |
| // return true; |
| final int yStart = (getBounds().height - toggleRenderer.getBounds().height) / 2; |
| if (point.y >= yStart && point.y < yStart + toggleRenderer.getSize().y) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setTree(boolean tree) { |
| super.setTree(tree); |
| |
| if (tree) { |
| toggleRenderer = new ToggleRenderer(); |
| toggleRenderer.setDisplay(getDisplay()); |
| |
| branchRenderer = new BranchRenderer(); |
| branchRenderer.setDisplay(getDisplay()); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setCheck(boolean check) { |
| super.setCheck(check); |
| |
| if (check) { |
| checkRenderer = new CheckBoxRenderer(); |
| checkRenderer.setDisplay(getDisplay()); |
| } else { |
| checkRenderer = null; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Rectangle getTextBounds(GridItem item, boolean preferred) { |
| int x = leftMargin; |
| |
| if (isTree()) { |
| x += getToggleIndent(item); |
| |
| x += toggleRenderer.getBounds().width + insideMargin; |
| |
| } |
| |
| if (isCheck()) { |
| x += checkRenderer.getBounds().width + insideMargin; |
| } |
| |
| final Image image = item.getImage(getColumn()); |
| if (image != null) { |
| x += image.getBounds().width + insideMargin; |
| } |
| |
| final Rectangle bounds = new Rectangle(x, topMargin + textTopMargin, 0, 0); |
| |
| final GC gc = new GC(item.getParent()); |
| gc.setFont(item.getFont(getColumn())); |
| final Point size = gc.stringExtent(item.getText(getColumn())); |
| |
| bounds.height = size.y; |
| |
| if (preferred) { |
| bounds.width = size.x - 1; |
| } else { |
| bounds.width = getBounds().width - x - rightMargin; |
| } |
| |
| gc.dispose(); |
| |
| return bounds; |
| } |
| |
| private boolean isCenteredCheckBoxOnly(GridItem item) { |
| return !isTree() && item.getImage(getColumn()) == null && item.getText(getColumn()).equals("") |
| && getAlignment() == SWT.CENTER; |
| } |
| |
| /** |
| * @param selectionForegroundColor the selectionForegroundColor to set |
| */ |
| public void setSelectionForegroundColor(Color selectionForegroundColor) { |
| this.selectionForegroundColor = selectionForegroundColor; |
| } |
| |
| /** |
| * @param selectionBackgroundColor the selectionBackgroundColor to set |
| */ |
| public void setSelectionBackgroundColor(Color selectionBackgroundColor) { |
| this.selectionBackgroundColor = selectionBackgroundColor; |
| } |
| |
| } |
| // END REUSED CLASS |